Skip to content

feat(core): the presentation oracle, and where the console can actually reach - #206

Merged
stormer78 merged 1 commit into
mainfrom
feat/rooms-chain-repair
Sep 8, 2026
Merged

feat(core): the presentation oracle, and where the console can actually reach#206
stormer78 merged 1 commit into
mainfrom
feat/rooms-chain-repair

Conversation

@stormer78

Copy link
Copy Markdown
Contributor

The gap that was invisible

rooms/keys/present was missing from the rooms client, and its absence did not look like a gap. Every host-served room task takes an authority presentation; nothing in this library could produce one. So roomsRecordsList, roomsRecordsGet, roomsRecordsPut and roomsEpochMint were exported, typechecked, documented — and impossible to call.

A coverage count does not catch that. The missing family sits in a different half of the surface from the ones it made unreachable, so the number moved by one while four functions were dead.

Adds roomsKeysPresent, roomsKeysChain, and roomsEpochChain.

The presentation is checked where it is produced

The published schemas type the two ends of the same value differently: rooms/keys/present's response declares presentation as a bare open object, while every host task $refs AuthorityPresentation, whose membership and authority are REQUIRED. An incomplete answer is therefore representable, and the only ways past it are a blind cast or a check.

A blind cast moves the failure somewhere useless. The host refuses with "no authority chain presented; a room operation is authorized by the chain" — an accusation aimed at the member, three hops away from the agent that actually answered incompletely. Checking here names the party that did.

(Worth a look upstream: the two ends of one value could share the $ref.)

Two functions build typed literals instead of spread-and-cast

The as unknown as Payload pattern this module uses elsewhere hides a misspelled member. It nearly did: the freshness value on rooms/keys/present is nonce, and the schema's description calls it a challenge, which is what I first wrote. packages/core/tests/rooms.present-and-chain.mjs pins both the name and that an absent one is absent rather than null.

The finding worth not rediscovering

The browser console cannot call the host-served half of this family at all.

  • manager/carrier.ts passes exactly {type, payload} — deliberately, so the offscreen document mints and signs the envelope rather than counter-signing one composed in a page.
  • handleManagerTask then supplies vtaDid: active.conn.vtaDid as the recipient.
  • So a service naming a host is dropped, and the call lands at an agent that does not serve it.

Nothing type-checks as wrong, and the test harness answers by task URI regardless of recipient — so a fake agent happily replies to a call the console could never make.

Console can reach Console cannot reach
keys/{list,open,seal,present,chain} rooms/create
owner/{invite,issue-membership,issue-authority} records/{list,get,put}
vta/webvh/dids/create epoch/{mint,chain}

This means the create-a-room flow merged in #204 always fails at its second step — the DID is minted (the agent's own work, which succeeds) and the registration cannot be sent. The Minted recovery panel built for the rare case fires every time. It fails loudly rather than corrupting anything, but it reads as a broken agent.

The module header now records all of this, including which surfaces the host-served functions are for: a server-side consumer or a CLI with its own channel.

The fix, already specified

trustoverip/dtgwg-trust-tasks-tf#402 adds rooms/keys/backfill and rooms/owner/register — the member's own agent makes the host call, being the party that can. It already holds the credentials, mints the presentation, and holds the group state. Once VTI implements them, the console finishes the create flow and gains the history repair its rooms pane already names.

The backfill pane is deliberately not in this PR: it would be a screen that always fails at its middle hop, and it will be one call rather than three once the task lands.

Verification

npm run lint, npm run build, npm test all green — 1034 tests, 0 failures, of which 7 are new and cover the presentation shape check, the nonce naming, and that stored: 0 is passed through rather than swallowed (it is what a retry looks like). Coverage census updated 194 → 196 with the reasoning recorded in task-surface.mjs.

…ly reach

`rooms/keys/present` was missing, and its absence was not visible as a gap.
Every host-served room task takes an authority presentation and nothing in
this library could produce one — so `records/{list,get,put}` and
`epoch/mint` were exported, typechecked, and impossible to call. A coverage
count does not catch that: the missing family sits in a different half of
the surface from the ones it made unreachable.

Adds `roomsKeysPresent`, `roomsKeysChain` and `roomsEpochChain`.

The presentation is checked before it is returned. The published schemas
type the two ends of that value differently — `present`'s response is a
bare open object, every host task `$ref`s `AuthorityPresentation` whose
`membership` and `authority` are REQUIRED — so an incomplete answer is
representable, and the only ways past are a blind cast or a check. A blind
cast moves the failure: the host refuses with "no authority chain
presented", an accusation aimed at the member, three hops from the agent
that actually answered incompletely. Checking here names the party that did.

Two of the three new functions are built as typed literals rather than the
spread-and-cast this module uses elsewhere, because that cast hides a
misspelled member — the freshness value is `nonce`, and the schema's
description calls it a challenge, which is what I first wrote.

Also records a finding worth not rediscovering: **the browser console
cannot call the host-served half at all.** Its bridge passes only
`{type, payload}` so the offscreen document mints and signs the envelope,
and the background then addresses it to the wallet's own VTA — so a
`service` naming a host is dropped and the call lands at an agent that does
not serve it. Nothing type-checks as wrong. `rooms/keys/backfill` and
`rooms/owner/register` (trustoverip/dtgwg-trust-tasks-tf#402) are the fix:
the member's agent makes the call, being the party that can.

Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
@stormer78
stormer78 merged commit e798809 into main Sep 8, 2026
3 checks passed
@stormer78
stormer78 deleted the feat/rooms-chain-repair branch September 8, 2026 17:48
@affinidi-appsecurity-bot

Copy link
Copy Markdown

🛡️ AI Agentic Security Code Review

🔎 A manual security review is recommended before merging. Please contact the Security team for specifics and remediation guidance.

ℹ️ Detailed findings are not published on public repositories; the Security team holds the complete report.

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.

2 participants