Skip to content

Commit 238f05e

Browse files
MattDHillclaude
andcommitted
Fix call drops, always-on TURN, JVB public IP, health checks
- Add runAsInit to all s6-based daemons (prosody, web, jicofo, jvb) - Fix BOSH proxy timeout: write custom-meet.conf via nginx-patch oneshot on every boot to set proxy_read_timeout 3600s - Remove TURN toggle: always register TURN interface, coturn runs when a public domain is available and idles otherwise - Add ENABLE_AUTH + ENABLE_GUESTS to jicofo env for room creation auth - Resolve JVB public IPs from the jvb-media interface and pass via JVB_ADVERTISE_IPS so JVB advertises correct addresses behind VPS/proxy - Add health check: JVB fails if UI is public but no public IPv4 for video bridge media - Add health check: TURN fails if UI is public but no public domain for TURN relay; disabled with informational message otherwise - Extract shared constants (jvbMediaPort, jvbHttpPort, jicofoHealthPort, jvbMediaInterfaceId) to utils.ts - Remove setTurn action, taskSetTurn init, turnEnabled from store schema Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 5727393 commit 238f05e

File tree

11 files changed

+242
-196
lines changed

11 files changed

+242
-196
lines changed

startos/actions/index.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { sdk } from '../sdk'
22
import { resetPassword } from './resetPassword'
3-
import { setTurn } from './setTurn'
43

5-
export const actions = sdk.Actions.of().addAction(resetPassword).addAction(setTurn)
4+
export const actions = sdk.Actions.of().addAction(resetPassword)

startos/actions/setTurn.ts

Lines changed: 0 additions & 38 deletions
This file was deleted.

startos/fileModels/store.json.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const shape = z.object({
55
JICOFO_AUTH_PASSWORD: z.string(),
66
JVB_AUTH_PASSWORD: z.string(),
77
TURN_SECRET: z.string(),
8-
turnEnabled: z.boolean().catch(false),
8+
99
ADMIN_PASSWORD: z.string().catch(''),
1010
})
1111

startos/i18n/dictionaries/default.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ const dict = {
1919
'The TURN server is ready': 14,
2020
'The TURN server is not ready': 15,
2121

22+
'Required to support mobile carriers, hotel WiFi, and UDP-blocking firewalls. To enable, add a public domain to the "TURN Relay" interface.': 37,
23+
'Required for clearnet. Enable a public IPv4 address in the "Video Bridge Media" interface.': 38,
24+
'Only needed if using a public domain.': 39,
25+
2226
// interfaces.ts
2327
'Web UI': 16,
2428
'The web interface of Jitsi Meet': 17,
@@ -27,16 +31,7 @@ const dict = {
2731
'TURN Relay': 20,
2832
'TURN relay server for NAT traversal': 21,
2933

30-
// actions
31-
'Configure TURN': 22,
32-
'Enable or disable the TURN relay server for participants behind restrictive NATs or firewalls. Requires a public address configured in StartOS.': 23,
33-
'Enable TURN': 24,
34-
35-
// main.ts (TURN)
36-
'Waiting for a public domain on the TURN interface. Please configure a domain in StartOS.': 25,
37-
3834
// init
39-
'Enable a TURN relay server to help participants behind restrictive firewalls or NATs join video calls.': 26,
4035
'Create an admin password so only you can start meetings.': 27,
4136

4237
// actions (auth)

startos/i18n/dictionaries/translations.ts

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,6 @@ export default {
2424
19: 'Transporte de medios WebRTC para video y audio',
2525
20: 'Relevo TURN',
2626
21: 'Servidor de relevo TURN para travesia NAT',
27-
22: 'Configurar TURN',
28-
23: 'Habilitar o deshabilitar el servidor de relevo TURN para participantes detras de NATs o firewalls restrictivos. Requiere una direccion publica configurada en StartOS.',
29-
24: 'Habilitar TURN',
30-
25: 'Esperando un dominio publico en la interfaz TURN. Configure un dominio en StartOS.',
31-
26: 'Habilitar un servidor de relevo TURN para ayudar a los participantes detras de firewalls o NATs restrictivos a unirse a videollamadas.',
3227
27: 'Crea una contrasena de administrador para que solo tu puedas iniciar reuniones.',
3328
28: 'Restablecer contrasena de administrador',
3429
29: 'Crear contrasena de administrador',
@@ -38,6 +33,9 @@ export default {
3833
33: 'Tu contrasena de administrador ha sido establecida. Usa estas credenciales para crear reuniones.',
3934
34: 'Usuario',
4035
35: 'Contrasena',
36+
37: 'Necesario para operadores moviles, WiFi de hoteles y firewalls que bloquean UDP. Para habilitar, agregue un dominio publico a la interfaz "TURN Relay".',
37+
38: 'Necesario para clearnet. Habilite una direccion IPv4 publica en la interfaz "Video Bridge Media".',
38+
39: 'Solo necesario si se usa un dominio publico.',
4139
},
4240
de_DE: {
4341
0: 'Starte Jitsi Meet!',
@@ -62,11 +60,6 @@ export default {
6260
19: 'WebRTC-Medientransport fur Video und Audio',
6361
20: 'TURN-Relay',
6462
21: 'TURN-Relay-Server fur NAT-Traversierung',
65-
22: 'TURN konfigurieren',
66-
23: 'TURN-Relay-Server fur Teilnehmer hinter restriktiven NATs oder Firewalls aktivieren oder deaktivieren. Erfordert eine offentliche Adresse in StartOS.',
67-
24: 'TURN aktivieren',
68-
25: 'Warten auf eine offentliche Domain auf der TURN-Schnittstelle. Bitte konfigurieren Sie eine Domain in StartOS.',
69-
26: 'Einen TURN-Relay-Server aktivieren um Teilnehmern hinter restriktiven Firewalls oder NATs die Teilnahme an Videoanrufen zu ermoglichen.',
7063
27: 'Erstellen Sie ein Administratorpasswort damit nur Sie Meetings starten konnen.',
7164
28: 'Administratorpasswort zurucksetzen',
7265
29: 'Administratorpasswort erstellen',
@@ -76,6 +69,9 @@ export default {
7669
33: 'Ihr Administratorpasswort wurde festgelegt. Verwenden Sie diese Anmeldedaten um Meetings zu erstellen.',
7770
34: 'Benutzername',
7871
35: 'Passwort',
72+
37: 'Erforderlich fur Mobilfunknetze, Hotel-WLAN und UDP-blockierende Firewalls. Zum Aktivieren fugen Sie eine offentliche Domain zur "TURN Relay"-Schnittstelle hinzu.',
73+
38: 'Erforderlich fur Clearnet. Aktivieren Sie eine offentliche IPv4-Adresse in der "Video Bridge Media"-Schnittstelle.',
74+
39: 'Nur erforderlich bei Verwendung einer offentlichen Domain.',
7975
},
8076
pl_PL: {
8177
0: 'Uruchamianie Jitsi Meet!',
@@ -100,11 +96,6 @@ export default {
10096
19: 'Transport mediow WebRTC dla wideo i audio',
10197
20: 'Przekaznik TURN',
10298
21: 'Serwer przekaznikowy TURN do przechodzenia NAT',
103-
22: 'Skonfiguruj TURN',
104-
23: 'Wlacz lub wylacz serwer przekaznikowy TURN dla uczestnikow za restrykcyjnymi NAT-ami lub firewallami. Wymaga publicznego adresu skonfigurowanego w StartOS.',
105-
24: 'Wlacz TURN',
106-
25: 'Oczekiwanie na publiczna domene na interfejsie TURN. Skonfiguruj domene w StartOS.',
107-
26: 'Wlacz serwer przekaznikowy TURN aby pomoc uczestnikom za restrykcyjnymi firewallami lub NAT-ami dolaczyc do rozmow wideo.',
10899
27: 'Utworz haslo administratora aby tylko Ty mogl rozpoczynac spotkania.',
109100
28: 'Zresetuj haslo administratora',
110101
29: 'Utworz haslo administratora',
@@ -114,6 +105,9 @@ export default {
114105
33: 'Haslo administratora zostalo ustawione. Uzyj tych danych logowania aby tworzyc spotkania.',
115106
34: 'Nazwa uzytkownika',
116107
35: 'Haslo',
108+
37: 'Wymagane dla operatorow komorkowych, WiFi hotelowego i zapor blokujacych UDP. Aby wlaczyc, dodaj publiczna domene do interfejsu "TURN Relay".',
109+
38: 'Wymagane dla clearnet. Wlacz publiczny adres IPv4 w interfejsie "Video Bridge Media".',
110+
39: 'Potrzebne tylko przy uzyciu domeny publicznej.',
117111
},
118112
fr_FR: {
119113
0: 'Demarrage de Jitsi Meet !',
@@ -138,11 +132,6 @@ export default {
138132
19: 'Transport de medias WebRTC pour la video et audio',
139133
20: 'Relais TURN',
140134
21: 'Serveur relais TURN pour la traversee NAT',
141-
22: 'Configurer TURN',
142-
23: "Activer ou desactiver le serveur relais TURN pour les participants derriere des NATs ou pare-feux restrictifs. Necessite une adresse publique configuree dans StartOS.",
143-
24: 'Activer TURN',
144-
25: "En attente d'un domaine public sur l'interface TURN. Veuillez configurer un domaine dans StartOS.",
145-
26: "Activer un serveur relais TURN pour aider les participants derriere des pare-feux ou NATs restrictifs a rejoindre les appels video.",
146135
27: "Creez un mot de passe administrateur pour que vous seul puissiez demarrer des reunions.",
147136
28: "Reinitialiser le mot de passe administrateur",
148137
29: "Creer le mot de passe administrateur",
@@ -152,5 +141,8 @@ export default {
152141
33: "Votre mot de passe administrateur a ete defini. Utilisez ces identifiants pour creer des reunions.",
153142
34: "Nom d'utilisateur",
154143
35: 'Mot de passe',
144+
37: "Requis pour les operateurs mobiles, le WiFi d'hotel et les pare-feux bloquant UDP. Pour activer, ajoutez un domaine public a l'interface \"TURN Relay\".",
145+
38: 'Requis pour le clearnet. Activez une adresse IPv4 publique dans l\'interface "Video Bridge Media".',
146+
39: "Necessaire uniquement si vous utilisez un domaine public.",
155147
},
156148
} satisfies Record<string, LangDict>

startos/init/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@ import { actions } from '../actions'
66
import { restoreInit } from '../backups'
77
import { seedFiles } from './seedFiles'
88
import { taskSetPassword } from './taskSetPassword'
9-
import { taskSetTurn } from './taskSetTurn'
109
export const init = sdk.setupInit(
1110
seedFiles,
1211
taskSetPassword,
13-
taskSetTurn,
1412
restoreInit,
1513
versionGraph,
1614
setInterfaces,

startos/init/seedFiles.ts

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,44 @@
11
import { storeJson } from '../fileModels/store.json'
22
import { sdk } from '../sdk'
3-
import { getPassword } from '../utils'
3+
import { getPassword, prosodyEnv, prosodyMounts, prosodyPort } from '../utils'
44

55
export const seedFiles = sdk.setupOnInit(async (effects, kind) => {
66
if (kind !== 'install') return
77

8+
const JICOFO_AUTH_PASSWORD = getPassword()
9+
const JVB_AUTH_PASSWORD = getPassword()
10+
const TURN_SECRET = getPassword()
11+
812
await storeJson.merge(effects, {
9-
JICOFO_AUTH_PASSWORD: getPassword(),
10-
JVB_AUTH_PASSWORD: getPassword(),
11-
TURN_SECRET: getPassword(),
13+
JICOFO_AUTH_PASSWORD,
14+
JVB_AUTH_PASSWORD,
15+
TURN_SECRET,
1216
})
17+
18+
// Start prosody briefly so its entrypoint generates config files.
19+
// The resetPassword action needs /config/prosody.cfg.lua to exist.
20+
await sdk.Daemons.of(effects)
21+
.addDaemon('prosody', {
22+
subcontainer: await sdk.SubContainer.of(
23+
effects,
24+
{ imageId: 'prosody' },
25+
prosodyMounts,
26+
'prosody-seed',
27+
),
28+
exec: {
29+
command: sdk.useEntrypoint(),
30+
runAsInit: true,
31+
env: prosodyEnv({ JICOFO_AUTH_PASSWORD, JVB_AUTH_PASSWORD, TURN_SECRET }),
32+
},
33+
ready: {
34+
display: null,
35+
fn: () =>
36+
sdk.healthCheck.checkPortListening(effects, prosodyPort, {
37+
successMessage: '',
38+
errorMessage: '',
39+
}),
40+
},
41+
requires: [],
42+
})
43+
.runUntilSuccess(30_000)
1344
})

startos/init/taskSetTurn.ts

Lines changed: 0 additions & 13 deletions
This file was deleted.

startos/interfaces.ts

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
import { storeJson } from './fileModels/store.json'
21
import { i18n } from './i18n'
32
import { sdk } from './sdk'
4-
import { turnInterfaceId, turnPort, uiInterfaceId, uiPort } from './utils'
3+
import {
4+
jvbMediaInterfaceId,
5+
jvbMediaPort,
6+
turnInterfaceId,
7+
turnPort,
8+
uiInterfaceId,
9+
uiPort,
10+
} from './utils'
511

612
export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => {
7-
const turnEnabled = await storeJson.read((s) => s.turnEnabled).const(effects)
8-
913
// Web UI
1014
const uiMulti = sdk.MultiHost.of(effects, 'ui-multi')
1115
const uiOrigin = await uiMulti.bindPort(uiPort, {
@@ -27,16 +31,16 @@ export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => {
2731

2832
// JVB media
2933
const jvbMulti = sdk.MultiHost.of(effects, 'jvb-media-multi')
30-
const jvbOrigin = await jvbMulti.bindPort(10000, {
34+
const jvbOrigin = await jvbMulti.bindPort(jvbMediaPort, {
3135
protocol: null,
32-
preferredExternalPort: 10000,
36+
preferredExternalPort: jvbMediaPort,
3337
secure: { ssl: false },
3438
addSsl: null,
3539
})
3640
const jvbReceipt = await jvbOrigin.export([
3741
sdk.createInterface(effects, {
3842
name: i18n('Video Bridge Media'),
39-
id: 'jvb-media',
43+
id: jvbMediaInterfaceId,
4044
description: i18n('WebRTC media transport for video and audio'),
4145
type: 'api',
4246
masked: false,
@@ -50,33 +54,31 @@ export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => {
5054
const receipts = [uiReceipt, jvbReceipt]
5155

5256
// TURN relay
53-
if (turnEnabled) {
54-
const turnMulti = sdk.MultiHost.of(effects, 'turn-multi')
55-
const turnOrigin = await turnMulti.bindPort(turnPort, {
56-
protocol: null,
57+
const turnMulti = sdk.MultiHost.of(effects, 'turn-multi')
58+
const turnOrigin = await turnMulti.bindPort(turnPort, {
59+
protocol: null,
60+
preferredExternalPort: turnPort,
61+
addSsl: {
5762
preferredExternalPort: turnPort,
58-
addSsl: {
59-
preferredExternalPort: turnPort,
60-
alpn: null,
61-
addXForwardedHeaders: false,
62-
},
63-
secure: null,
64-
})
65-
const turnReceipt = await turnOrigin.export([
66-
sdk.createInterface(effects, {
67-
name: i18n('TURN Relay'),
68-
id: turnInterfaceId,
69-
description: i18n('TURN relay server for NAT traversal'),
70-
type: 'api',
71-
masked: false,
72-
schemeOverride: null,
73-
username: null,
74-
path: '',
75-
query: {},
76-
}),
77-
])
78-
receipts.push(turnReceipt)
79-
}
63+
alpn: null,
64+
addXForwardedHeaders: false,
65+
},
66+
secure: null,
67+
})
68+
const turnReceipt = await turnOrigin.export([
69+
sdk.createInterface(effects, {
70+
name: i18n('TURN Relay'),
71+
id: turnInterfaceId,
72+
description: i18n('TURN relay server for NAT traversal'),
73+
type: 'api',
74+
masked: false,
75+
schemeOverride: null,
76+
username: null,
77+
path: '',
78+
query: {},
79+
}),
80+
])
81+
receipts.push(turnReceipt)
8082

8183
return receipts
8284
})

0 commit comments

Comments
 (0)