Перейти к основному содержимому

Jitsi: инвентарь изменений в thirdparty/jitsi

Полный перечень того, что PIN меняет/добавляет поверх саморазмещённого Jitsi, чтобы медиа-стек работал с контрактом messenger (JWT входа, authoritative-admission, presence-вебхуки, авто-запись → file storage). Для каждого пункта — файл-источник в thirdparty/jitsi/*, характер изменения (конфиг / новый файл / env) и куда он ложится по гиду. Мета-репозиторий Jitsi поставляет Nomad-IaC как референс конфигурации; PIN переносит ключи в собственные Helm/k8s-манифесты (jitsi-deployment.md) — здесь перечислены сами параметры, а не способ доставки.

Характер большинства правок — конфигурация, не форк. Открытые prosody-плагины и jicofo включаются и параметризуются штатными опциями; собственного кода PIN добавляет минимум: один prosody-модуль присутствия и один jibri finalize-скрипт. Всё остальное — значения опций и env.

prosody (XMPP: комнаты, presence, JWT, admission)

Файлы плагинов — thirdparty/jitsi/jitsi-meet/resources/prosody-plugins/; vhost-конфиг PIN авторит сам.

ЧтоФайл-источникХарактерЗначение / назначение
Токен-аутентификация vhostmod_auth_token.lua, token/util.lib.luaконфигauthentication = "token"; app_id = pin_messenger, app_secret из vault (env JWT_APP_ID/JWT_APP_SECRET) — prosody проверяет HS256-подпись JWT входа (jitsi-jwt.md)
Проверка привязки токена к комнатеmod_token_verification.luaконфигmodules_enabled += "token_verification"room-claim обязан совпадать с именем комнаты (токен conf-A не пускает в conf-B)
Роль модератора из claim-аmod_token_affiliation.luaконфигmodules_enabled += "token_affiliation"context.user.moderator == true / affiliation == owner → affiliation owner (роль moderator: запись/удаление/завершение, INV-CONF-6)
Authoritative admissionmod_reservations.luaконфигmodules_enabled += "reservations"; reservations_api_prefix = <messenger>/api/internal/jitsi; reservations_api_headers (HMAC/Bearer); reservations_api_timeout (20с); reservations_api_retry_count/_delay — комната создаётся только после подтверждения messenger (jitsi-rooms.md)
Presence-вебхуки (ростер)новый mod_pin_muc_presence.luaновый файлкастомный модуль: hooks muc-occupant-joined/muc-occupant-leftPOST /internal/jitsi/events (авторитет состава, INV-CPART-5). Обоснование vs prosody-egress/отсутствующий muc_webhooksjitsi-rooms.md
Домен MUC-маппингаmod_muc_domain_mapper.luaконфигmuc_mapper_domain_base = <MediaDomain> (домен развёртывания = MediaRoom.Domain)

Отключаются неиспользуемые модули открытой поставки (breakout-rooms, lobby, av-moderation, polls и пр.) — PIN включает только token-auth, token-verification, token-affiliation, reservations, muc-mapper и собственный presence-модуль.

jicofo (focus: конференции, brewery, оркестрация jibri)

Файл — thirdparty/jitsi/jicofo/jicofo-selector/src/main/resources/reference.conf (значения PIN задаёт override-конфигом/env, не правкой reference).

ЧтоКлюч (reference.conf)ХарактерЗначение для PIN
Brewery jibrijibri.brewery-jid (закомментирован, строка ~305)конфигjibribrewery@internal.auth.<domain> — пул рекордеров для авто-записи (recording-pipeline.md)
Проверка модератораconference.enable-moderator-checks (строка ~219)конфигtrue — запись/привилегии только модератору (INV-CONF-6)
Транскрипция (будущее)transcription.url-template (закомментирован, строка ~445)конфиг (оставить off)не задаём в MVP; шов для live-STT (transcription-roadmap.md)

JVB (jitsi-videobridge) — без правок кода: публичный UDP 10000, octo.enabled = false (один регион, ADR-0056), параметризация через env (jitsi-deployment.md).

jibri (рекордер → финализация)

Env-референс — thirdparty/jitsi/infra-provisioning/nomad/jibri.hcl; finalize-скрипт PIN добавляет в образ.

ЧтоИсточникХарактерЗначение для PIN
Finalize-скриптjibri.hclJIBRI_FINALIZE_RECORDING_SCRIPT_PATH (был /usr/bin/jitsi_uploader.sh)env + новый файл= /usr/bin/pin_finalize.shновый скрипт (замена jitsi_uploader.sh): читает metadata.json, стримит multipart в messenger, при 2xx удаляет локальную директорию (контракт — recording-pipeline.md)
Директория записиjibri.hclJIBRI_RECORDING_DIRenv/mnt/recordings (persistent volume)
Включение записиJIBRI_ENABLE_RECORDING / ENABLE_RECORDINGenvtrue при RECORDINGS_ENABLED
Авто-shutdown простояjibri.hclJIBRI_USAGE_TIMEOUTenv61 minutes

Обогащение метаданных записи (initiator, conference_details.session_id, проброс pin.conferenceId/ pin.tenantId) делает штатный mod_jibri_session.luaбез изменений, только используется (recording-pipeline.md).

coturn (TURN/STUN)

Референс — thirdparty/jitsi/infra-provisioning/nomad/coturn.hcl.

ЧтоКлючХарактерЗначение для PIN
Секретная TURN-аутентификацияuse-auth-secret, static-auth-secretконфигобщий секрет из vault; messenger минтит короткоживущие TURN-креды (MediaIceServerEntry.Credential) тем же секретом (jitsi-jwt.md)
Realmrealmконфигдомен развёртывания медиа
ТранспортTLS на 443конфигTURN over TLS (обход строгого NAT/файрвола)

lib-jitsi-meet (нативный клиент)

Не серверный компонент — вендорится из thirdparty/jitsi/lib-jitsi-meet (в npm не публикуется), собирается в монорепо фронта (jitsi-client.md). Правок кода библиотеки нет — только клиентский конфиг:

КнобЗначениеЗачем
disableThirdPartyRequeststrueникаких внешних вызовов (gravatar/аналитика) — приватность + self-host
p2p.enabledtrue (1:1)медиа напрямую между двумя; escalation на JVB при 3+
serviceUrl / hosts.domainиз SignalingUrl/MediaDomainendpoint prosody из MediaJoinModel

Новые артефакты PIN (не из открытой поставки)

АртефактГде живётРоль
mod_pin_muc_presence.luaprosody-плагины развёртыванияpresence-вебхуки join/leave → messenger (авторитет ростера)
pin_finalize.shобраз jibri (/usr/bin/)финализация записи: metadata.json → multipart-stream в messenger
Helm/k8s-манифестыинфра-репозиторий PINразвёртывание prosody/jicofo/JVB/jibri/coturn (jitsi-manifests.md)

pin_finalize.sh (полный текст)

Заменяет штатный jitsi_uploader.sh (env JIBRI_FINALIZE_RECORDING_SCRIPT_PATH). jibri вызывает его с директорией завершённой записи ($1). Скрипт читает metadata.json, стримит медиа multipart в messenger (память не буферизуется — curl -F читает файл с диска), при 2xx удаляет директорию, иначе выходит с ненулевым кодом (jibri/оператор ретраит). Контракт — recording-pipeline.md, эндпоинт — ../api/internal/.

#!/usr/bin/env bash
# pin_finalize.sh — финализация записи jibri → ingest в messenger.
# Вызов: pin_finalize.sh <recording_dir>
# Заменяет /usr/bin/jitsi_uploader.sh (JIBRI_FINALIZE_RECORDING_SCRIPT_PATH).
set -euo pipefail

REC_DIR="${1:?usage: pin_finalize.sh <recording_dir>}"
META="${REC_DIR}/metadata.json"

# messenger internal API + секрет вебхука (смонтированы в под jibri, см. jitsi-manifests.md)
MSG_BASE="${MESSENGER_INTERNAL_URL:?MESSENGER_INTERNAL_URL not set}"
WEBHOOK_SECRET="${JITSI_WEBHOOK_TOKEN:?JITSI_WEBHOOK_TOKEN not set}"

[ -f "${META}" ] || { echo "pin_finalize: metadata.json missing in ${REC_DIR}" >&2; exit 1; }

# sessionId = наш conferenceId/callSessionId (проброшен клиентом в app_data.pin.*);
# session_id из conference_details — запасной ключ (= meetingId).
SESSION_ID="$(jq -r '.pin.conferenceId // .conference_details.session_id // empty' "${META}")"
TENANT_ID="$(jq -r '.pin.tenantId // empty' "${META}")"
[ -n "${SESSION_ID}" ] || { echo "pin_finalize: no session id in metadata.json" >&2; exit 1; }

# берём единственный медиа-файл записи (mp4/webm/mkv)
MEDIA_FILE="$(find "${REC_DIR}" -maxdepth 1 -type f \
\( -name '*.mp4' -o -name '*.webm' -o -name '*.mkv' \) | head -n1)"
[ -n "${MEDIA_FILE}" ] || { echo "pin_finalize: no media file in ${REC_DIR}" >&2; exit 1; }

# HMAC-подпись пути (replay-window: timestamp в подписи + заголовке), fail-closed на стороне messenger
TS="$(date -u +%s)"
SIG="$(printf '%s.%s' "${TS}" "${SESSION_ID}" \
| openssl dgst -sha256 -hmac "${WEBHOOK_SECRET}" -binary | xxd -p -c 256)"

URL="${MSG_BASE}/api/messenger/internal/call-recordings/${SESSION_ID}/ingest"

# streamed multipart: файл читается с диска (memory-bounded), метаданные — отдельным полем
HTTP_CODE="$(curl -sS -o /dev/null -w '%{http_code}' -X POST "${URL}" \
-H "X-Pin-Timestamp: ${TS}" \
-H "X-Pin-Signature: ${SIG}" \
-H "Authorization: Bearer ${WEBHOOK_SECRET}" \
${TENANT_ID:+-H "X-Pin-Tenant: ${TENANT_ID}"} \
-F "metadata=@${META};type=application/json" \
-F "file=@${MEDIA_FILE};type=video/mp4")"

if [[ "${HTTP_CODE}" =~ ^2 ]]; then
echo "pin_finalize: ingest ok (${HTTP_CODE}) session=${SESSION_ID}; cleaning ${REC_DIR}"
rm -rf "${REC_DIR}"
exit 0
else
echo "pin_finalize: ingest failed (${HTTP_CODE}) session=${SESSION_ID}; keeping ${REC_DIR}" >&2
exit 1
fi

curl -F стримит файл с диска, не читая его целиком в память; байты далее проксирует messenger в file storage (recording-pipeline.md). На не-2xx директория не удаляется — запись остаётся вне Available, оператор/повторный finalize доводит выгрузку.

mod_pin_muc_presence.lua (набросок)

Собственный prosody-модуль (главный MUC-компонент): на muc-occupant-joined/muc-occupant-left шлёт неблокирующий HTTP-POST в messenger — авторитет состава конференции (INV-CPART-5, jitsi-rooms.md). Пропускает jibri/transcriber (по образцу mod_muc_cleanup_backend_services.lua), async-HTTP — по образцу mod_reservations.lua.

-- mod_pin_muc_presence.lua — presence-вебхуки MUC → messenger.
-- Грузится под главным MUC-компонентом (conference.<domain>).
local http = require 'net.http';
local json = require 'cjson.safe';
local util = module:require 'util';
local is_jibri = util.is_jibri;
local is_transcriber = util.is_transcriber;
local jid = require 'util.jid';

local api_url = module:get_option_string('pin_muc_presence_api_url'); -- .../internal/jitsi/events
local api_token = module:get_option_string('pin_muc_presence_api_token', os.getenv('JITSI_WEBHOOK_TOKEN'));

-- неблокирующий POST (fire-and-forget с логом ошибки), по образцу async_http_request в mod_reservations
local function post_event(payload)
local body = json.encode(payload);
http.request(api_url, {
method = 'POST';
body = body;
headers = {
['Content-Type'] = 'application/json';
['Authorization'] = 'Bearer '..api_token; -- + HMAC на стороне messenger (replay-window)
['X-Pin-Timestamp'] = tostring(os.time());
};
}, function (_, code)
if not (code >= 200 and code < 300) then
module:log('warn', 'pin_muc_presence POST %s failed: %s', payload.event, code);
end
end);
end

local function on_presence(evt_kind)
return function (event)
local room, occupant = event.room, event.occupant;
if is_jibri(occupant.jid) or is_transcriber(occupant.jid) then
return; -- backend-сервисы не участники ростера
end
local ctx = event.origin and event.origin.jitsi_meet_context_user;
post_event({
room = jid.node(room.jid); -- call-{id} / conf-{id}
occupantJid = tostring(occupant.jid);
nick = occupant.nick and jid.resource(occupant.nick) or nil;
userId = ctx and ctx.id or nil; -- из JWT context.user.id
role = occupant.role; -- moderator/participant
event = evt_kind; -- 'Joined' | 'Left'
occurredAt = os.date('!%Y-%m-%dT%H:%M:%SZ');
});
end
end

module:hook('muc-occupant-joined', on_presence('Joined'), -100);
module:hook('muc-occupant-left', on_presence('Left'), -100);

Приоритет -100 (последними в цепочке) — как в mod_muc_cleanup_backend_services.lua. messenger маппит room → сессия, eventCallParticipant.State; при расхождении с клиентскими join/leave побеждает серверное событие.

Сводный список изменений (репо-пути)

Полный перечень «что и где» одним взглядом — конкретные пути в thirdparty/jitsi/* и характер правки.

#КомпонентФайл / ключ (репо-путь)ХарактерЗначение для PIN
1prosodyjitsi-meet/resources/prosody-plugins/mod_auth_token.lua + token/util.lib.luaконфиг vhostauthentication="token", app_id=pin_messenger, app_secret (vault)
2prosodyjitsi-meet/resources/prosody-plugins/mod_token_verification.luamodules_enabled +=room-claim == имя комнаты
3prosodyjitsi-meet/resources/prosody-plugins/mod_token_affiliation.luamodules_enabled +=context.user.moderator/affiliation=owner → moderator (стр. 28–45)
4prosodyjitsi-meet/resources/prosody-plugins/mod_reservations.luamodules_enabled += + опцииreservations_api_prefix/_headers/_timeout; POST/DELETE /conference (стр. 371–393, 617–643)
5prosodyjitsi-meet/resources/prosody-plugins/mod_jibri_session.luamodules_enabled +=обогащение app_data записи — без правок (стр. 25–91)
6prosodyjitsi-meet/resources/prosody-plugins/mod_muc_domain_mapper.luaконфигmuc_mapper_domain_base = <domain>
7prosodyновый mod_pin_muc_presence.lua (образец: mod_muc_cleanup_backend_services.lua стр. 30–74, async — mod_reservations.lua стр. 161–196)новый файлpresence join/leave → POST /internal/jitsi/events
8prosodymain-vhost .cfg.lua (авторит PIN)новый файлнабор modules_enabled + reservation/presence-URL (jitsi-deployment.md)
9jicofojicofo/jicofo-selector/src/main/resources/reference.conf:305 jibri.brewery-jidoverridejibribrewery@internal.auth.<domain>
10jicofojicofo/.../reference.conf:219 conference.enable-moderator-checksoverridetrue
11jicofojicofo/.../reference.conf:445 transcription.url-templateоставить offне задаём в MVP
12JVBenv (octo.enabled=false, DOCKER_HOST_ADDRESS)конфигбез правок кода; публичный UDP 10000
13jibriinfra-provisioning/nomad/jibri.hcl:213 JIBRI_FINALIZE_RECORDING_SCRIPT_PATHenv + новый файл= /usr/bin/pin_finalize.sh
14jibriinfra-provisioning/nomad/jibri.hcl:214 JIBRI_RECORDING_DIRenv/mnt/recordings
15jibriinfra-provisioning/nomad/jibri.hcl:213–219 ENABLE_RECORDING/JIBRI_USAGE_TIMEOUTenvtrue / 61 minutes
16jibriновый /usr/bin/pin_finalize.sh (в образе)новый файлmultipart-stream ingest → messenger
17coturninfra-provisioning/nomad/coturn.hcl:74,108–110 use-auth-secret/static-auth-secret/realmконфигобщий TURN-секрет (vault), realm <domain>, TLS 443
18lib-jitsi-meetlib-jitsi-meet (вендор) — клиентский конфигконфигdisableThirdPartyRequests, p2p.enabled, serviceUrl (jitsi-client.md)

Собственного кода PIN — ровно два артефакта (строки 7 и 16); всё остальное — значения опций/env поверх открытой поставки.

Не трогаем (референс на будущее)

jigasi (SIP), multitrack-recorder (nomad/multitrack-recorder.hcl), transcriber (nomad/transcriber.hcl), веб-SPA jitsi-meet, Octo/мультирегион — вне MVP; относятся к transcription-roadmap.md и right-sizing (jitsi-deployment.md).

Обратные ссылки

Автоссылки: где используется этот документ.

Связанные документы