Parent: #28
Independently design-verified (2026-09-14) against the real spec and TypeScript source, matching sections A-C's own dedicated passes -- no longer just conversational reasoning. Confirmed the core finding, corrected two real overclaims, and found genuine gaps the original framing glossed over.
Message/content lifetime -- verified, ready to implement
token-claims' expires/not-before gate authorisation only, checked in verifyTokenChain (ts/packages/core/src/domain/tokens.ts:190-196) against the verifier's own local Clock. Neither room.send/room.read nor room-notice-claims carry any TTL field. core/data's retention is explicitly deployment policy (spec/data-domain.cddl:11-21), not protocol.
Design (both extension points already exist, no schema renegotiation needed -- token-claims' tail at tokens.cddl:95, room-notice-claims' tail at room.cddl:236):
? "valid-until": uint, ; Unix ms; bounds the authorised CONTENT/ACTION's lifetime, distinct from `expires` (authorisation-to-present-the-token). Absent = unbounded.
Verifier obligation: reject when now > valid-until, independent of expires/revocation; for notices, checked at read time. Real integration point for tokens: verifyTokenChain in tokens.ts, right after its existing expires check. room-notice-claims has no verifier function at all today (no room.ts/verifyRoomNotice exists in ts/packages/core/src/domain) -- the notice-side check is net-new domain logic, not an addition. Also inherited (not introduced) by this: mesh-session.ts::applyManageRequest does no verification and no re-check loop -- "expired mid-flight" is already undefined behaviour for expires today, and valid-until won't get continuous enforcement for free either.
Fungibility -- two of three mechanisms have no current use case; correctly deferred
Exhaustive search of spec/*.cddl found no single-use/redemption/use-count language anywhere except delegations-remaining (a depth bound, not a use-count bound). The obvious candidate (room.invite as a coupon) doesn't fit on inspection: its token is bearer-bound at mint time and room.join mints fresh per request -- membership isn't "spent," it's the authorisation itself. Single-use and limited-fungibility should not be built without a real use case -- speculative machinery, against this project's own no-premature-abstraction principle. Conditional fungibility isn't a deliverable -- it's just the existing extension-tail-plus-verifier-obligation pattern, already shipped.
Redemption/double-spend detection -- does NOT hold as originally stated; real gap, own design pass needed (see sub-issue)
The original "reuse the existing revocation-announce/gossip mechanism" claim doesn't survive checking the real code:
revocation-claims (management.cddl:45-50) is a closed CDDL map, no extension tail -- can't carry a "redeemed" fact without its own schema change.
- Revocation is issuer-only by design; a redemption fact is naturally attested by whoever received it, often not the issuer -- doesn't fit the trust model as-is.
- Propagation is not flood-gossip today:
sendRevocationAnnounce (mesh-session.ts:586-599) sends over one direct connection only; nothing re-broadcasts; relay-hub.ts:163 drops these frames; web-console/src/webrtc-negotiation.ts:36 already documents "no revocation-gossip ingestion yet" as a known limitation.
Real current behaviour: a second presentation of an already-redeemed grant to a peer that hasn't seen the redemption announcement verifies as valid, full stop -- not "best-effort detected eventually," since there's no flood propagation to make it best-effort. Genuinely decentralisation-limited (no true double-spend-proofness without consensus) remains correctly stated and unchanged.
Parent: #28
Independently design-verified (2026-09-14) against the real spec and TypeScript source, matching sections A-C's own dedicated passes -- no longer just conversational reasoning. Confirmed the core finding, corrected two real overclaims, and found genuine gaps the original framing glossed over.
Message/content lifetime -- verified, ready to implement
token-claims'expires/not-beforegate authorisation only, checked inverifyTokenChain(ts/packages/core/src/domain/tokens.ts:190-196) against the verifier's own localClock. Neitherroom.send/room.readnorroom-notice-claimscarry any TTL field.core/data's retention is explicitly deployment policy (spec/data-domain.cddl:11-21), not protocol.Design (both extension points already exist, no schema renegotiation needed --
token-claims' tail attokens.cddl:95,room-notice-claims' tail atroom.cddl:236):Verifier obligation: reject when
now > valid-until, independent ofexpires/revocation; for notices, checked at read time. Real integration point for tokens:verifyTokenChainintokens.ts, right after its existingexpirescheck.room-notice-claimshas no verifier function at all today (noroom.ts/verifyRoomNoticeexists ints/packages/core/src/domain) -- the notice-side check is net-new domain logic, not an addition. Also inherited (not introduced) by this:mesh-session.ts::applyManageRequestdoes no verification and no re-check loop -- "expired mid-flight" is already undefined behaviour forexpirestoday, andvalid-untilwon't get continuous enforcement for free either.Fungibility -- two of three mechanisms have no current use case; correctly deferred
Exhaustive search of
spec/*.cddlfound no single-use/redemption/use-count language anywhere exceptdelegations-remaining(a depth bound, not a use-count bound). The obvious candidate (room.inviteas a coupon) doesn't fit on inspection: its token is bearer-bound at mint time androom.joinmints fresh per request -- membership isn't "spent," it's the authorisation itself. Single-use and limited-fungibility should not be built without a real use case -- speculative machinery, against this project's own no-premature-abstraction principle. Conditional fungibility isn't a deliverable -- it's just the existing extension-tail-plus-verifier-obligation pattern, already shipped.Redemption/double-spend detection -- does NOT hold as originally stated; real gap, own design pass needed (see sub-issue)
The original "reuse the existing revocation-announce/gossip mechanism" claim doesn't survive checking the real code:
revocation-claims(management.cddl:45-50) is a closed CDDL map, no extension tail -- can't carry a "redeemed" fact without its own schema change.sendRevocationAnnounce(mesh-session.ts:586-599) sends over one direct connection only; nothing re-broadcasts;relay-hub.ts:163drops these frames;web-console/src/webrtc-negotiation.ts:36already documents "no revocation-gossip ingestion yet" as a known limitation.Real current behaviour: a second presentation of an already-redeemed grant to a peer that hasn't seen the redemption announcement verifies as valid, full stop -- not "best-effort detected eventually," since there's no flood propagation to make it best-effort. Genuinely decentralisation-limited (no true double-spend-proofness without consensus) remains correctly stated and unchanged.