3.7 KiB
Wiring Threema relay into the portal
Drop-in files in this archive:
src/lib/packages.ts # add 'threema' to catalog + customProvisioning flag
src/lib/threema-relay.ts # new — admin API client
src/app/api/tenants/[name]/threema/route.ts # new — POST provision / DELETE deprovision
src/app/api/tenants/[name]/threema/routes/route.ts # new — atomic add/remove of a single Threema ID
src/components/channel-users/channel-users.tsx # branch threema through relay-managed endpoint
src/components/packages/package-card.tsx # handle customProvisioning enable/disable
deploy/patch-i18n-threema.mjs # idempotent i18n key injection
Manual steps after dropping in
-
.env(and.env.example) — add:THREEMA_RELAY_URL=http://pieced-threema-gateway.threema-gateway.svc:8080 THREEMA_RELAY_ADMIN_TOKEN=__from_openbao__The portal pod's OpenBao client should also read
secret/data/threema-gateway/adminand surfacetokenas this env var (existing ESO pattern in the portal's Helm chart). -
Patch the message files (one-time):
node deploy/patch-i18n-threema.mjs -
Re-export
CHANNEL_PACKAGE_IDSis unchanged in source; verifytenants/[name]/page.tsxstill derives the enabled-channels list from it — it should now includethreemaautomatically once a tenant has it inspec.packages. -
Type-check:
npx tsc --noEmit
Flow summary
Enabling Threema for a tenant
- Customer toggles the threema package card.
- PackageCard sees
customProvisioning: true→ POSTs/api/tenants/<name>/threema. - Handler calls relay
POST /admin/tokens→ gets{token, hmacSecret}. - Handler writes them to OpenBao at
secret/data/tenants/tenant-<name>/threema-relay. - PackageCard then PATCHes
tenant.spec.packagesto includethreema. - Operator reconciles: ExternalSecret syncs OpenBao → Secret; OpenClaw pod restarts with
THREEMA_RELAY_*env vars; plugin registersthreemachannel.
Customer adds a Threema ID
- UI calls
POST /api/tenants/<name>/threema/routeswith{threemaId}. - Handler calls relay
POST /admin/routes(uniqueness enforced at PK). - On 201 or 409-from-same-tenant: handler patches K8s
spec.channelUsers.threema. - On 409-from-other-tenant: 409 to client with explanation.
- On K8s patch failure after relay success: handler compensates by
DELETE /admin/routes/...at the relay.
Customer removes a Threema ID
- UI calls
DELETE /api/tenants/<name>/threema/routes?threemaId=.... - Handler patches K8s
spec.channelUsers.threemato drop the ID. - Handler calls relay
DELETE /admin/routes/...(404 = idempotent OK). - If relay drop fails: K8s already updated, surface warning but treat as success — relay deletes are idempotent on retry.
Disabling Threema for a tenant
- Customer disables the threema card.
- PackageCard DELETEs
/api/tenants/<name>/threema. - Handler calls relay
DELETE /admin/tokens/<name>(cascades to all routes for this tenant). - Handler deletes OpenBao secret at
secret/data/tenants/tenant-<name>/threema-relay. - PackageCard then PATCHes
tenant.spec.packagesto dropthreema. - Operator reconciles: ExternalSecret targets a missing OpenBao path → Secret deleted → OpenClaw pod restarts without
threemachannel.
There's a small window (between step 4 and the operator's reconcile) where the pod still thinks it has a relay token but the relay has revoked it. Outbound during that window returns 401 from the relay; inbound is blackholed at the relay because routes are gone. Both are graceful failures.