๐Ÿš€
Getting Started

This hub runs two independent real-time services on a single VPS, both secured with SSL and managed through REST APIs protected by a master bearer token.

ReverbWebSocket / Pub-Sub
wss://ws.loveworldlyricsofficial.com
LiveKitWebRTC Media
wss://webrtc.loveworldlyricsofficial.com
ReverbManagement API
https://ws.loveworldlyricsofficial.com/api/v1/
LiveKitManagement API
https://webrtc.loveworldlyricsofficial.com/api/v1/

Traffic routing

RequestPathHandled by
WebSocketwss://ws.loveworldlyricsofficial.com/Reverb (internal :6001)
Reverb APIhttps://ws.loveworldlyricsofficial.com/api/v1/*PHP-FPM โ†’ Laravel
WebRTC signalingwss://webrtc.loveworldlyricsofficial.com/LiveKit (internal :17880)
LiveKit APIhttps://webrtc.loveworldlyricsofficial.com/api/v1/*PHP-FPM โ†’ Laravel
๐Ÿ—
Your Credentials

โš ๏ธ Store the Master API Key securely โ€” it controls all management endpoints. Also saved at /root/.hub-credentials.

Master API Key

Master Keyhub_0CRIyqDmqmtXc58VSC5i3bPnBA7UrbZXYO25MfeKk4

Reverb โ€” Default App

App IDapp-58d9b9b4
App Keyc6b920a714a2a878a79946541c54500f9ba0b5da
App Secret2938216ff11b6dc00b622915ff8f255f326313f4b046452727449fee0c74f47f
Hostws.loveworldlyricsofficial.com
Port / Scheme443 / https

LiveKit โ€” Default Key

API KeyAPIQMOSY8rOEp9W0yFJ
API SecretdVwKdIJ417w2OabEMUivAPRETTjoyTMGKXLk4JOx
WS URLwss://webrtc.loveworldlyricsofficial.com
HTTP URLhttps://webrtc.loveworldlyricsofficial.com
๐Ÿ”‘
Authentication

All management API endpoints (except GET /api/v1/health) require the master key as a Bearer token.

curl -s https://ws.loveworldlyricsofficial.com/api/v1/reverb/apps   -H "Authorization: Bearer hub_0CRIyqDmqmtXc58VSC5i3bPnBA7UrbZXYO25MfeKk4"
Http::withToken($masterKey)->get('https://ws.loveworldlyricsofficial.com/api/v1/reverb/apps');
const res = await fetch('https://ws.loveworldlyricsofficial.com/api/v1/reverb/apps', {
  headers: { 'Authorization': `Bearer ${masterKey}` }
});
StatusMeaning
401No Authorization header
403Invalid or revoked key
422Validation failed โ€” check errors field
404Route not found
๐ŸŒ
Ports & Architecture
PortProtocolServiceExposedNotes
22TCPSSHYesServer access
80TCPHTTPYesRedirect + ACME challenge
443TCPHTTPSYesReverb + LiveKit + APIs via server_name
7881TCPLiveKit WebRTC fallbackYesDirect TCP
50000โ€“60000UDPLiveKit mediaYesDirect, cannot proxy UDP
6001TCPReverb internalNoLoopback only
17880TCPLiveKit internalNoLoopback only
6379TCPRedisNoLoopback only
๐Ÿ—
Master Key Management

Create multiple keys (one per developer/service), revoke individually. The last active key is protected.

GET/api/v1/keysList all keys๐Ÿ”’ Auth
200 OK
{"data":[{"id":1,"name":"Initial Master Key","key_preview":"hub_Abcd...xYzW","active":true}]}
POST/api/v1/keysCreate a new key๐Ÿ”’ Auth

โš ๏ธ Full key shown once only โ€” store immediately.

curl -s -X POST https://ws.loveworldlyricsofficial.com/api/v1/keys   -H "Authorization: Bearer hub_0CRIyqDmqmtXc58VSC5i3bPnBA7UrbZXYO25MfeKk4"   -H "Content-Type: application/json"   -d '{"name":"Dev Key"}'
201 Created
{"data":{"key":"hub_xxxx..."},"warning":"Store this key safely โ€” it will NOT be shown again."}
DELETE/api/v1/keys/{id}Revoke a key๐Ÿ”’ Auth

Cannot revoke the last active key.

โšก
Reverb App Management

Base: https://ws.loveworldlyricsofficial.com/api/v1  ยท  Changes auto-regenerate config/reverb.php and restart Reverb.

GET/api/v1/reverb/appsList apps๐Ÿ”’ Auth
200 OK
{"data":[{"app_id":"app-58d9b9b4","name":"Default App","key":"c6b920a714a2a878a79946541c54500f9ba0b5da","active":true}]}
POST/api/v1/reverb/appsCreate app๐Ÿ”’ Auth
FieldTypeRequiredDescription
namestringrequiredApp name
allowed_originsarrayoptionalCORS origins (default: ["*"])
POST/api/v1/reverb/apps/{id}/rotateRotate key & secret๐Ÿ”’ Auth

Generates new key and secret. Old credentials stop working immediately.

200 OK
{"data":{"key":"newkey...","secret":"newsecret..."}}
PUT/api/v1/reverb/apps/{id}Update app๐Ÿ”’ Auth

Update name, allowed_origins, or active (boolean).

DELETE/api/v1/reverb/apps/{id}Delete app๐Ÿ”’ Auth

Removes the app and restarts Reverb. Connected clients will be disconnected.

โšก
Connect to Reverb
BROADCAST_CONNECTION=reverb
REVERB_APP_ID=app-58d9b9b4
REVERB_APP_KEY=c6b920a714a2a878a79946541c54500f9ba0b5da
REVERB_APP_SECRET=2938216ff11b6dc00b622915ff8f255f326313f4b046452727449fee0c74f47f
REVERB_HOST=ws.loveworldlyricsofficial.com
REVERB_PORT=443
REVERB_SCHEME=https
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
// npm install laravel-echo pusher-js
import Echo from 'laravel-echo'; import Pusher from 'pusher-js';
window.Pusher = Pusher;
window.Echo = new Echo({
  broadcaster: 'reverb', key: import.meta.env.VITE_REVERB_APP_KEY,
  wsHost: import.meta.env.VITE_REVERB_HOST,
  wsPort: import.meta.env.VITE_REVERB_PORT, wssPort: import.meta.env.VITE_REVERB_PORT,
  forceTLS: true, enabledTransports: ['ws','wss'], disableStats: true,
});
// npm install pusher-js
new Pusher('c6b920a714a2a878a79946541c54500f9ba0b5da', {
  wsHost: 'ws.loveworldlyricsofficial.com', wsPort: 443, wssPort: 443,
  forceTLS: true, enabledTransports: ['ws','wss'], cluster: 'mt1', disableStats: true,
});
# composer require pusher/pusher-php-server
$p = new Pusher\Pusher('c6b920a714a2a878a79946541c54500f9ba0b5da','2938216ff11b6dc00b622915ff8f255f326313f4b046452727449fee0c74f47f','app-58d9b9b4',
  ['host'=>'ws.loveworldlyricsofficial.com','port'=>443,'scheme'=>'https','useTLS'=>true]);
$p->trigger('my-channel','my-event',['msg'=>'Hello!']);
๐ŸŽ™
LiveKit Key Management

Base: https://webrtc.loveworldlyricsofficial.com/api/v1

GET/api/v1/livekit/keysList keys๐Ÿ”’ Auth
200 OK
{"data":[{"api_key":"APIQMOSY8rOEp9W0yFJ","name":"Default Key","active":true}]}
POST/api/v1/livekit/keysCreate key๐Ÿ”’ Auth
FieldTypeDescription
namestringLabel for this key (optional)
PUT/api/v1/livekit/keys/{apiKey}Enable/disable๐Ÿ”’ Auth

Pass {"active": false} to disable without deleting.

DELETE/api/v1/livekit/keys/{apiKey}Delete key๐Ÿ”’ Auth

Tokens signed with this key will stop working immediately.

๐ŸŽ™
Token Generation

โ„น๏ธ Always generate tokens server-side. Never expose your API Secret in browser code.

POST/api/v1/livekit/tokensGenerate token๐Ÿ”’ Auth
FieldTypeRequiredDescription
roomstringrequiredRoom name
identitystringrequiredUnique participant ID
namestringoptionalDisplay name
ttlintegeroptionalExpiry seconds (default: 3600)
can_publishbooleanoptionalCan publish tracks (default: true)
can_subscribebooleanoptionalCan subscribe (default: true)
curl -s -X POST https://webrtc.loveworldlyricsofficial.com/api/v1/livekit/tokens   -H "Authorization: Bearer hub_0CRIyqDmqmtXc58VSC5i3bPnBA7UrbZXYO25MfeKk4"   -H "Content-Type: application/json"   -d '{"room":"my-room","identity":"user-42","name":"Alice"}'
200 OK
{"data":{"token":"eyJhbGci...","expires_in":3600,"room":"my-room","identity":"user-42"}}
๐ŸŽ™
Room Management
GET/api/v1/livekit/roomsList rooms๐Ÿ”’ Auth
{"rooms":[{"name":"my-room","numParticipants":2}]}
POST/api/v1/livekit/roomsCreate room๐Ÿ”’ Auth

Fields: name (required), empty_timeout (seconds, default 300), max_participants (default 100).

GET/api/v1/livekit/rooms/{name}/participantsList participants๐Ÿ”’ Auth
{"participants":[{"identity":"user-42","name":"Alice","state":"ACTIVE"}]}
DELETE/api/v1/livekit/rooms/{name}/participants/{identity}Kick participant๐Ÿ”’ Auth

Removes the participant from the room immediately.

DELETE/api/v1/livekit/rooms/{name}Delete room๐Ÿ”’ Auth

Closes the room and disconnects all participants.

๐ŸŽ™
Connect Frontend to LiveKit
1

Backend: call POST /api/v1/livekit/tokens โ†’ get JWT

2

Frontend: receive token, connect to wss://webrtc.loveworldlyricsofficial.com

3

LiveKit handles all WebRTC negotiation and media routing automatically

// npm install livekit-client
import { Room, RoomEvent, Track } from 'livekit-client';
const { data: { token } } = await fetch('/api/livekit-token', {
  method:'POST', headers:{'Content-Type':'application/json'},
  body: JSON.stringify({room:'my-room', identity:'user-1'})
}).then(r=>r.json());
const room = new Room({adaptiveStream:true, dynacast:true});
room.on(RoomEvent.TrackSubscribed, track => {
  if(track.kind===Track.Kind.Video||track.kind===Track.Kind.Audio)
    document.getElementById('video').appendChild(track.attach());
});
await room.connect('wss://webrtc.loveworldlyricsofficial.com', token);
await room.localParticipant.enableCameraAndMicrophone();
// npm install @livekit/components-react livekit-client
import { LiveKitRoom, VideoConference } from '@livekit/components-react';
import '@livekit/components-styles';
export default function Meeting({token}) {
  return <LiveKitRoom serverUrl="wss://webrtc.loveworldlyricsofficial.com" token={token} video audio>
    <VideoConference/>
  </LiveKitRoom>;
}
// npm install livekit-client
import { Room } from 'livekit-client';
import { onMounted, onUnmounted, ref } from 'vue';
export function useLiveKit(roomName, userId) {
  const room = ref(null);
  onMounted(async () => {
    const {data} = await fetch('/api/livekit-token',{
      method:'POST',headers:{'Content-Type':'application/json'},
      body:JSON.stringify({room:roomName,identity:userId})
    }).then(r=>r.json());
    room.value = new Room();
    await room.value.connect('wss://webrtc.loveworldlyricsofficial.com', data.token);
    await room.value.localParticipant.enableCameraAndMicrophone();
  });
  onUnmounted(() => room.value?.disconnect());
  return { room };
}
๐Ÿ˜
PHP / Laravel Integration

Client project .env

HUB_MASTER_KEY=hub_0CRIyqDmqmtXc58VSC5i3bPnBA7UrbZXYO25MfeKk4
HUB_REVERB_API=https://ws.loveworldlyricsofficial.com/api/v1
HUB_LIVEKIT_API=https://webrtc.loveworldlyricsofficial.com/api/v1

Broadcast an event

// app/Events/OrderShipped.php
class OrderShipped implements ShouldBroadcast {
  use Dispatchable, InteractsWithSockets, SerializesModels;
  public function __construct(public Order $order) {}
  public function broadcastOn(): array { return [new Channel('orders')]; }
}
broadcast(new OrderShipped($order));

LiveKit token endpoint

$res = Http::withToken(config('services.hub.key'))
    ->post(config('services.hub.lk_api').'/livekit/tokens', [
        'room'     => $request->room,
        'identity' => (string) auth()->id(),
        'name'     => auth()->user()->name,
    ]);
return response()->json($res->json('data'));
๐ŸŸก
JavaScript / Node.js
// hub.js
const KEY = process.env.HUB_MASTER_KEY;
const RV  = 'https://ws.loveworldlyricsofficial.com/api/v1';
const LK  = 'https://webrtc.loveworldlyricsofficial.com/api/v1';
const call = (m,b,p,d) => fetch(b+p,{method:m,
  headers:{'Authorization':`Bearer ${KEY}`,'Content-Type':'application/json'},
  body:d?JSON.stringify(d):undefined}).then(r=>r.json());

export const listApps  = ()  => call('GET',   RV,'/reverb/apps');
export const createApp = b   => call('POST',  RV,'/reverb/apps',b);
export const rotateApp = id  => call('POST',  RV,`/reverb/apps/${id}/rotate`);
export const genToken  = b   => call('POST',  LK,'/livekit/tokens',b);
export const listRooms = ()  => call('GET',   LK,'/livekit/rooms');
๐Ÿ
Python
# pip install requests
import requests
H  = {"Authorization": "Bearer hub_0CRIyqDmqmtXc58VSC5i3bPnBA7UrbZXYO25MfeKk4"}
RV = "https://ws.loveworldlyricsofficial.com/api/v1"
LK = "https://webrtc.loveworldlyricsofficial.com/api/v1"

apps  = requests.get(f"{RV}/reverb/apps", headers=H).json()
token = requests.post(f"{LK}/livekit/tokens", headers=H,
  json={"room":"my-room","identity":"user-1"}).json()["data"]["token"]
rooms = requests.get(f"{LK}/livekit/rooms", headers=H).json()
โš™
Monitor & Manage

Service commands

supervisorctl status
systemctl status nginx redis-server php8.3-fpm
curl -s https://ws.loveworldlyricsofficial.com/api/v1/health
supervisorctl restart reverb    # disconnects WS clients
supervisorctl restart livekit   # disconnects RTC participants
systemctl reload nginx           # zero-downtime config reload
tail -f /var/log/reverb.log
tail -f /var/log/livekit.log
journalctl -u nginx -f
journalctl -u php8.3-fpm -f
sqlite3 /var/www/reverb-hub/database/database.sqlite
# Inside SQLite:
.tables
SELECT * FROM hub_master_keys;
SELECT app_id, name, key FROM reverb_apps;
SELECT api_key, name, active FROM livekit_keys;
# Backup:
cp /var/www/reverb-hub/database/database.sqlite /root/hub-backup-$(date +%Y%m%d).sqlite
openssl x509 -enddate -noout -in /etc/letsencrypt/live/ws.loveworldlyricsofficial.com/cert.pem
openssl x509 -enddate -noout -in /etc/letsencrypt/live/webrtc.loveworldlyricsofficial.com/cert.pem
certbot renew --dry-run          # test auto-renewal
sudo bash /root/retry-ssl.sh     # get real certs if self-signed

File locations

PathPurpose
/var/www/reverb-hub/Laravel hub app
/var/www/reverb-hub/.envLaravel config
/var/www/reverb-hub/config/reverb.phpAuto-generated Reverb apps
/var/www/reverb-hub/database/database.sqliteAll keys and apps
/etc/livekit/config.yamlLiveKit config
/root/.hub-credentialsCredentials backup
/var/log/reverb.logReverb logs
/var/log/livekit.logLiveKit logs