openpencil/tools
Kayshen-X 6212b486e3 feat(collab): minimize the relay credential and confirm the owner on LAN
Two halves of the same problem: the relay learned who collaborates with
whom, and a guest could not safely join a stranger's session without an
invite.

Claim-minimized relay bearer. The relay authenticated each WSS connection
with the full collaboration ticket, whose claims carry the account subject,
device id, and optional display name and avatar. Now that collaboration is
cross-account, that let a relay operator reconstruct a social graph. The
relay reads exactly one field out of that ticket — the expiry it clamps the
session deadline to — and its authorization output, (route, role, expiry),
comes from the signed locator and the route capability, not from identity.
The disclosure was gratuitous.

A separate audience-scoped token now carries only issuer, audience,
version, scope, the channel binding to the caller's X25519 key, and the
time bounds. `VerifiedRelayTokenClaims` deliberately exposes no identity
accessor at all, so the relay cannot regress into reading one. No route or
role claim was added: route authorization already comes from the locator
plus the capability secret, and putting route ids in the token would move
the graph to the issuer, which also knows the account.

Scope of the guarantee, stated in the code so nobody over-reads it: this
defends against a third-party or regional relay operator. It does not
defend against the first party, who runs both the issuer and the relay and
can rejoin on the channel-binding key and the issuance time. It also
de-identifies rather than making the view unlinkable — the device's X25519
static is persistent and in the clear in every hello, so the operator still
builds a device graph, it just cannot name the nodes or join them to the
account namespace.

The two token types are domain-separated by both JWS `typ` and `aud`,
strictly compared, with `deny_unknown_fields` on disjoint claim structs, so
each is structurally invalid against the other's parser. That property is
what makes sharing one signing key defensible, so it is tested in both
directions. The relay dual-accepts during migration, discriminating on
`typ` before claim parsing, behind an env flag. The client never retries a
rejected minimized token with the full ticket — that would be a downgrade
any curious relay could trigger at will.

Guest owner confirmation. A guest joining over unpinned LAN discovery still
required the same account, because it has no approval prompt of its own and
mDNS names nobody. It now gets the explicit decision the owner already had:
the verified owner identity is surfaced and confirmed before the peer is
authorized, so nothing from the session — snapshot, presence, session name
— exists before the user decides. With that gate in place the unpinned LAN
path admits any issued account too.

Display name and avatar are attacker-chosen, so the projection separates
them from the account subject and device id at the type level, strips
invisible and bidi-control characters, and labels them as claimed. A
display name cannot occupy an authoritative row; the test uses another
account's UUID as the display name to prove it.

Both halves are pinned in the boundary gate: losing either asymmetry is
silent, because the code still compiles and every other check still passes
while nothing authenticates the peer.
2026-08-01 14:24:46 +08:00
..
bundle-macos.sh feat(collab): add authenticated p2p collaboration 2026-07-29 00:06:58 +08:00
check-collab-deployment-boundaries.sh fix(collab): harden p2p collaboration against resource exhaustion 2026-08-01 09:48:23 +08:00
check-collab-security-boundaries-cases.sh fix(collab): harden p2p collaboration against resource exhaustion 2026-08-01 09:48:23 +08:00
check-collab-security-boundaries.sh feat(collab): minimize the relay credential and confirm the owner on LAN 2026-08-01 14:24:46 +08:00
check-collab-security-boundaries.test.sh fix(collab): harden p2p collaboration against resource exhaustion 2026-08-01 09:48:23 +08:00
check-jian-boundaries.sh fix(editor): improve import and generation reliability 2026-07-21 06:34:53 +08:00
check-macos-bundle-plist.sh feat(collab): add authenticated p2p collaboration 2026-07-29 00:06:58 +08:00
check-macos-bundle-plist.test.sh feat(collab): add authenticated p2p collaboration 2026-07-29 00:06:58 +08:00
check-op-auth-prebuilt.sh feat(collab): add authenticated p2p collaboration 2026-07-29 00:06:58 +08:00
check-op-auth-prebuilt.test.sh feat(collab): add authenticated p2p collaboration 2026-07-29 00:06:58 +08:00
check-version-sync-policy.test-cases.sh fix: enforce centralized version inputs 2026-07-15 21:17:21 +08:00
check-version-sync.sh fix(agent): retire gemini cli provider, add antigravity and grok build 2026-07-24 21:11:15 +08:00
check-version-sync.test.sh fix(agent): retire gemini cli provider, add antigravity and grok build 2026-07-24 21:11:15 +08:00
check-wasm-bundle.sh merge: land the align-branch work onto the refreshed base 2026-07-03 00:08:17 +08:00
check-web-browser-smoke.sh test(web): add rust web browser smoke 2026-06-20 19:24:59 +08:00
check-web-server-headless.sh refactor(host): rename op-web-daemon crate to op-host-services 2026-06-19 22:35:22 +08:00
check-widget-boundary.sh feat(sdk): add op-web-sdk read-only web viewer crate 2026-06-19 17:52:12 +08:00
convert-locales.py feat(i18n): complete locale coverage across menus, providers, and panels 2026-07-26 11:24:06 +08:00
fetch-skia-artifact.sh build(step-1b): add tools/fetch-skia-artifact.sh (Variant A release path) 2026-05-09 21:04:00 +08:00
macos-local-network-plist.sh feat(collab): add authenticated p2p collaboration 2026-07-29 00:06:58 +08:00
merge-settings-i18n.py fix(agent): retire gemini cli provider, add antigravity and grok build 2026-07-24 21:11:15 +08:00
package-op-auth-prebuilt.sh feat(collab): add authenticated p2p collaboration 2026-07-29 00:06:58 +08:00