Files
pieced-portal/deploy/README-threema.md
admin 85c4302f7a
All checks were successful
Build and Push / build (push) Successful in 1m30s
Threema Gateway
2026-05-16 22:00:27 +02:00

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

  1. .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/admin and surface token as this env var (existing ESO pattern in the portal's Helm chart).

  2. Patch the message files (one-time):

    node deploy/patch-i18n-threema.mjs
    
  3. Re-export CHANNEL_PACKAGE_IDS is unchanged in source; verify tenants/[name]/page.tsx still derives the enabled-channels list from it — it should now include threema automatically once a tenant has it in spec.packages.

  4. Type-check:

    npx tsc --noEmit
    

Flow summary

Enabling Threema for a tenant

  1. Customer toggles the threema package card.
  2. PackageCard sees customProvisioning: true → POSTs /api/tenants/<name>/threema.
  3. Handler calls relay POST /admin/tokens → gets {token, hmacSecret}.
  4. Handler writes them to OpenBao at secret/data/tenants/tenant-<name>/threema-relay.
  5. PackageCard then PATCHes tenant.spec.packages to include threema.
  6. Operator reconciles: ExternalSecret syncs OpenBao → Secret; OpenClaw pod restarts with THREEMA_RELAY_* env vars; plugin registers threema channel.

Customer adds a Threema ID

  1. UI calls POST /api/tenants/<name>/threema/routes with {threemaId}.
  2. Handler calls relay POST /admin/routes (uniqueness enforced at PK).
  3. On 201 or 409-from-same-tenant: handler patches K8s spec.channelUsers.threema.
  4. On 409-from-other-tenant: 409 to client with explanation.
  5. On K8s patch failure after relay success: handler compensates by DELETE /admin/routes/... at the relay.

Customer removes a Threema ID

  1. UI calls DELETE /api/tenants/<name>/threema/routes?threemaId=....
  2. Handler patches K8s spec.channelUsers.threema to drop the ID.
  3. Handler calls relay DELETE /admin/routes/... (404 = idempotent OK).
  4. If relay drop fails: K8s already updated, surface warning but treat as success — relay deletes are idempotent on retry.

Disabling Threema for a tenant

  1. Customer disables the threema card.
  2. PackageCard DELETEs /api/tenants/<name>/threema.
  3. Handler calls relay DELETE /admin/tokens/<name> (cascades to all routes for this tenant).
  4. Handler deletes OpenBao secret at secret/data/tenants/tenant-<name>/threema-relay.
  5. PackageCard then PATCHes tenant.spec.packages to drop threema.
  6. Operator reconciles: ExternalSecret targets a missing OpenBao path → Secret deleted → OpenClaw pod restarts without threema channel.

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.