openpencil/docs/security
Kayshen-X b98154df15 fix(collab): close review follow-ups and align the reauth budget test
Follow-up to 0649cf40, which split renewal concurrency away from initial
authentication so an unauthenticated flood cannot starve live tunnels.
`online_challenge_timeout_keeps_its_auth_concurrency_permit` encoded the
old coupling as its contract — it saturated `max_auth_in_flight` to make a
second online challenge contend — so after the split nothing contended and
the test hung the whole suite. The property it covers (a timed-out online
challenge neither leaks its permit nor spawns another blocking task) is
unchanged; it now saturates the renewal budget that actually governs that
path. Its initial-path sibling was already passing and is untouched.

Roster: add `release_disconnected_peer`. A departed guest is retained for
same-epoch resume, but that retention is unbounded and counts against
`max_participants`, so a session that churns through guests eventually
admits nobody and each retained peer holds its result window until the
epoch ends. Releasing is refused for the owner and for a still-connected
peer, and it does not weaken id-space safety: a namespace can only be
reissued when the document carries none of its ids, which is checked
against the document rather than the roster.

Tickets: replace the derived `PartialEq` on `OpaqueTicket` with a
constant-time comparison. Nothing compares a ticket against a stored
secret today — verification lives in the host auth layer — but the derive
inherited `String`'s early-exit compare, so any future `==` on
attacker-supplied input would have been a timing oracle.

Bootstrap cache: a failed write now degrades instead of failing the
bootstrap. The document was already fully verified; what a failed write
costs is the anti-rollback generation floor for the next start, and the
threat model already accepts a missing floor because deleting the cache
has the same effect. An unwritable configuration directory must not mean
"cannot collaborate". The degradation travels with the document rather
than passing silently. Reads stay fail-closed: an absent cache reads as
`Ok(None)`, so only a cache that exists and is corrupt or unreadable
takes that path, and that is an anomaly rather than a first run.

Threat model: document what the relay operator actually sees. The bearer
ticket it verifies carries the account subject, device id, and optional
profile claims, so it learns who collaborates with whom — the asset table
previously implied those reached session participants only. Also adds the
invite-as-bearer-capability lifecycle and the local MCP endpoint, which
during a session reads and writes the shared document outside the
admission story. The relay-client doc comment no longer claims the relay
"sees only bytes".
2026-08-01 11:42:10 +08:00
..
p2p-collaboration-threat-model.md fix(collab): close review follow-ups and align the reauth budget test 2026-08-01 11:42:10 +08:00