Skip to content

feat(s072): the Mattermost gateway - #122

Open
HackTuah wants to merge 1 commit into
slice/071-gateway-telegramfrom
slice/072-gateway-mattermost
Open

HackTuah wants to merge 1 commit into
slice/071-gateway-telegramfrom
slice/072-gateway-mattermost

Conversation

@HackTuah

@HackTuah HackTuah commented Oct 8, 2026

Copy link
Copy Markdown
Member

Slice 072, the Mattermost gateway. Stacked on #120 (slice 071): this PR's base is 071's branch, so the diff shows 072 alone. Merge #120 first; GitHub then retargets this PR to main. It builds on 071's gateway core rather than adding a second one.

The slice stays in_progress until the owner's live run against their own server. The live criterion already ran here, against a throwaway Mattermost Team Edition 11.11.1 server pinned by digest.

What lands

  • WebSocket inbound:
    • the server never has to reach Trinity except for the controls;
    • authenticated in the upgrade header so it can resume, with the server's connection id and sequence number;
    • TLS verified explicitly (the library's default is verify_none);
    • posts handled at most once.
  • Approvals:
    • buttons and a dialog with HMAC-signed context, the dialog state bound to the user, single-use and 10 minutes long;
    • the slash command's token compared in constant time;
    • a dialog decides nothing: its answer reaches the router as the typed command, so the channel cap applies. An exec request was refused by the cap and receipted.
  • POST /gateways/callback/:adapter/:kind: the adapter verifies each callback; an unconfigured adapter answers 404.
  • Posts fit the server's MaxPostSize in code points, checked by a property.
  • An adapter census: it reads compiled imports and refuses calls into LLM, Sessions, Permissions, Effects or Tools. A planted direct Permissions call passed both the compiler and the boundary check; only the census caught it.
  • Delivery: a cron delivery the server refuses is no longer marked delivered.
  • :regulated: Mattermost boots only when named in TRINITY_REGULATED_GATEWAYS.
  • Token handling: redaction, the secret scan and the request-log filter learn the token.

Criteria

AC1 to AC7 are proven, AC7 on the throwaway server. AC8 is the gate, below.

Evidence

  • Reviewer's full mix gate on the final tree (071 plus 072), before commit: exit 0, 1065 passed (24 properties, 1041 tests), 18 excluded, prod_check: PASS, credo found no issues.
    • The author's four runs on that tree each failed only SandboxTest's 1 s Lua timeout, at load average 20 to 40. My run was green at load average about 20.
  • Reviewer checks: the callback route verifies before acting; Trinity attaches telemetry only to its own event catalogue.
    • F-7 (websockex telemetry metadata carries the bearer header) is therefore latent, not live, and is a follow-up.

Owner decision needed (F-6)

Gateway sessions have no project root, so a shell request from a channel always comes out :ask. Decide whether a gateway session should get one.

Slice 072, stacked on slice 071 and built on its gateway core: the
supervisor, adapter status, buttons as {:message, text, buttons}, and a
press routed as the typed command. The live criterion ran against a
throwaway Mattermost Team Edition 11.11.1 server in Docker, pinned by
digest. The owner's run against their own server is queued.

- Inbound arrives over the WebSocket, so the server never has to reach
  Trinity except for the controls. The socket authenticates in the
  upgrade header, which keeps it resumable, and resumes with the server's
  connection id and sequence number. TLS is verified explicitly, because
  the library's default is verify_none. Posts are handled at most once.
- Approvals use buttons and an interactive dialog. Their context is
  HMAC-signed, the dialog state is bound to the user, single-use and
  10 minutes long, and the slash command's token is compared in constant
  time. A dialog decides nothing: its answer reaches the router as the
  typed command, so the channel cap applies. An exec request was refused
  by the cap and receipted with basis channel_cap.
- POST /gateways/callback/:adapter/:kind hands a platform's callback to
  its adapter to verify. An unconfigured adapter answers 404.
- Posts fit the server's MaxPostSize in code points, checked by a
  property.
- A census reads every adapter's compiled imports and refuses calls into
  LLM, Sessions, Permissions, Effects or Tools. A planted direct
  Permissions call passed both the compiler and the boundary check, and
  only the census caught it.
- A cron delivery the server refuses is no longer marked delivered.
- Under :regulated, Mattermost boots only when named in
  TRINITY_REGULATED_GATEWAYS.
- Redaction, the secret scan and the request-log filter learn the token,
  matched only where it is named, since its shape is every Mattermost
  id's.
- websockex becomes a direct dependency; it was already in the lock.

Red evidence R1 to R9 is in the slice NOTES.

Signed-off-by: Ayla Croft <aylacroft@proton.me>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant