FIDO-bound browser endpoint capabilities¶
This page is the normative PETRA 1.0 contract for a browser session that publishes or queries directly as its authenticated human principal. It extends the Zenoh and DDIL data-plane contract and the connected-authority contract. It does not delegate Web core-machine authority to the browser.
Permanent identity boundary¶
Directory issues a browser capability set directly to the human principal and the exact ephemeral Ed25519 signing key created for one successful FIDO-authenticated browser session. The browser signs its own chat, voice, drawing, command, heartbeat, and query-use proofs. Web is neither the issuer nor the subject.
| Identity | May do | Must not do |
|---|---|---|
| FIDO-bound browser endpoint | Use exact human subject grants issued to its current session key | Use Web-machine grants, persist its set, publish position, or claim another endpoint identity |
| Web core machine | Proxy canonical bytes; distribute Directory commits; publish approved Web-authored plans, requirements, targets, control measures, and snapshots | Rewrite a browser request/set, attach its capability to a human envelope, or claim the human signed a Web publication |
| Bearer-only or legacy browser session | Use authenticated HTTPS application surfaces | Refresh capabilities, publish/query PETRA data-plane traffic, or fall back to unsigned liveliness |
This is direct authority issuance, not delegation. A capability remains bound to the human principal, exact session signing key, exact action, exact expression, purpose, classification ceiling, and signed absolute interval. Possession of a Web session cookie, IdentityToken bytes, group key, or Web-machine capability does not substitute for that binding.
Authentication and session binding¶
Directory owns the browser endpoint classification in private current state; it is not a
caller-declared field and platform_type is not added to IdentityToken.
- Directory verifies a current FIDO/WebAuthn assertion for the human principal. The authorization record retains the exact current FIDO credential anchor used by that ceremony.
- Code exchange creates a fresh ephemeral Ed25519 session signing key and one canonical
IdentityTokenwhose signed expiry is no later than the configured absolute session deadline. It does not issue the tactical 5 × 7-day offline batch. The token'sdevice_idis the existing domain-separated digest of the exact FIDO credential id, not the principal id, cookie, browser label, user agent, or caller input. - In the same connected flow, Directory records an active browser capability session bound to the principal, FIDO credential, token digest, session public key, issuance time, and absolute expiry. The record is Directory-private and confers no scope by itself.
- Web places the exact token and matching private key in the encrypted, HttpOnly Adonis
session. Each authenticated,
no-storeInertia document receives those existing authentication results; Web does not replace either value. - Every capability refresh rechecks the Directory signature, proof, nonce, current principal state, current FIDO credential, exact active browser-session record, exact token digest, exact session key, revocation, roles, clearance, and relationships.
A principal-id-anchored WebAuth token, an expired or removed FIDO credential, a token/key pair from another session, or a token that lacks the active browser-session record fails before generation allocation. FIDO proves the human/session binding; it does not make the browser a trusted tactical location source.
Canonical refresh and proxy boundary¶
Common's existing CapabilityRefreshRequestV1, CapabilitySetEntryV1, and
CapabilityRefreshResponseV1 remain the only wire. No browser-specific capability
message, requested-scope field, delegation token, or Web wrapper is introduced.
The browser obtains a Directory challenge, signs the exact Common refresh request with its session key, and verifies the exact signed complete response against current Directory trust and revocation state. Web may proxy the canonical challenge, request, and response bytes when direct browser access is unavailable. A proxy:
- applies byte and content-type bounds but does not decode and re-encode a successful body;
- does not mint, sign, rewrite, filter, merge, broaden, or interpret the capability set;
- returns Directory connectivity failure without creating an authority or capability outbox; and
- never substitutes the Web-machine token, key, capability, or generation.
The browser atomically replaces the entire verified set. A verified newer signed empty set removes all authority. Transport, decode, signature, canonicalization, bound, generation, entry, revocation, or binding failure preserves the prior in-memory bytes but blocks connected traffic after Directory reconnection until a current complete set succeeds.
Lifetime and browser-session custody¶
Non-production development exception¶
Directory and Web consume the same BROWSER_CAPABILITY_TTL_MINUTES setting.
Omission selects 60 minutes. It must be a positive whole number of minutes with
an exactly representable millisecond duration and a supported absolute timestamp.
A value above 60 is accepted only when both services explicitly enable DEV_LOGIN_ENABLED=true. NODE_ENV
alone does not enable this exception. The explicit development-login opt-in marks
the deployment as non-production for this contract, even when compiled services
run with NODE_ENV=production. Ordinary deployments leave development login
disabled and reject a longer override at startup.
The Infrastructure local environment supplies one shared value, defaulting to 480 minutes. An explicitly configured value also sets Web's HTTP session age to that duration; its pre-existing eight-hour inactivity age remains when the setting is omitted. The token's absolute signed expiry is never extended by HTTP activity. Directory additionally rejects a configured browser lifetime beyond its ordinary human-token lifetime (seven days by default).
Directory issues exactly one token whose signed expires_at_ms - issued_at_ms
equals the selected duration. Web requires that exact duration during callback
validation, so either direction of a Directory/Web configuration mismatch fails
with a clear configuration error, commits no browser credential locally, and
attempts immediate connected revocation of the failed handoff. Deploy both
services together and sign in again: changing configuration never rebases an
existing session. Directory's forward database migration preserves existing
expiry and exact-token records; rollback to the one-hour database constraint
fails while longer rows remain.
This exception changes only duration in an explicitly non-production deployment. The existing development-code authenticator retains its development credential anchor; FIDO authentication still retains its exact FIDO credential anchor. Single-token issuance, ephemeral signing keys, immediate connected revocation, credential deletion/rotation and logout remain mandatory. No refresh token, silent renewal, bearer fallback or persistent browser key is introduced.
Absolute deadline and key custody¶
The browser session and every capability in its set expire no later than the earliest of:
- the
IdentityTokendeadline; - the Directory-private browser-session deadline;
- the configured lifetime after browser-session issuance (at most one hour in production);
- the capability's own signed deadline;
- a relationship, classification, key, or policy deadline; and
- any earlier current revocation decision.
The tactical seven-day offline horizon does not apply. Restart never rebases the signed absolute
deadline. Browser capability request/response bytes, verified entries, and use proofs are
memory-only. The session private key is retained only in the encrypted, HttpOnly Adonis
session and the current document's JavaScript memory. Authenticated Inertia props rehydrate
the exact key after a hard refresh and give every same-login tab the same endpoint
authority. Inertia history is encrypted and authenticated responses are no-store. The
key is never written to Local Storage, Session Storage, IndexedDB, Cache API, a
service-worker cache, download, sealed file, log, or diagnostic.
This deliberately makes possession of the ordinary Web session sufficient to recover its matching endpoint signing authority. That is the browser UX boundary: one login spans refreshes and tabs, while Directory still binds and limits the token/key pair to the exact FIDO-authenticated session and its original signed deadline.
Local logout, local Web session expiry, and explicit local rejection synchronously close browser Zenoh traffic in every open tab and wipe the private key from session and memory, together with request/response bytes, the verified set, proof state, and derived opaque routing state. A non-authority-bearing cross-tab notification makes one tab's logout browser-wide. Directory also makes the corresponding session ineligible for subsequent refresh. When a current FIDO credential is removed or a browser session key is rotated, Directory atomically revokes every active browser-session signing key anchored to that credential/session through its token's signed expiry and includes those exact keys in the next signed revocation snapshot.
Remote invalidation follows PETRA's DDIL boundary. A connected browser or peer rejects the session once it holds the newer signed revocation state. An isolated browser that has not learned the removal may use only its already-verified set until the pre-existing signed absolute deadline; it cannot refresh, renew, broaden, or extend that authority. Stale or cross-login browser storage cannot restore authority: the client accepts only the exact current token binding and original unexpired deadline, and every connected refresh still requires Directory's current session, credential, relationship, and revocation state.
PETRA reuses the existing empty POST /api/auth/logout boundary: a FIDO-bound browser
logout carries its exact Directory-signed browser IdentityToken as the Authorization
bearer. This public, targetless endpoint verifies the canonical Directory signature,
absolute token expiry, exact token digest, principal, and session-signing-key row. It may
return idempotent success only for that exact row when the row/key was already revoked; it
does not use ordinary revocation-aware bearer middleware in a way that makes an exact retry
fail. Missing, malformed, noncanonical, expired, foreign, non-browser, or row-mismatched
tokens fail, and no request field can select another session.
On connected success Directory revokes that row and exact key through its signed validity deadline. If Directory is unavailable, Web creates no logout/authority outbox: local browser wipe and session-cookie destruction still complete immediately, and the bounded isolated residual ends at the original signed deadline or independently newer peer revocation. At this logout boundary, a stolen token can select only the exact session encoded by that token; it cannot target another session or refresh/publish without the matching private key. This statement does not remove the token's ordinary bearer consequences on other authenticated HTTPS endpoints. No logout signing domain, opaque revoke token, or browser-specific Common protobuf is added.
Exact browser grant matrix¶
Directory derives every entry from current effectiveCapabilities plus current
Directory-owned relationships. Role without relationship and relationship without the
required action both grant nothing. Unknown roles, missing relationships, suspended or
revoked subjects, removed credentials, and stale token claims grant nothing.
| Browser action | Required current action authority | Required Directory relationship | Result |
|---|---|---|---|
| Retained chat query | view_operation |
current channel member, or the already documented administrator visibility path | exact retained chat query only |
| Chat publication | send_chat |
current member of that exact channel | exact human-signed retained chat publication |
| Voice publication | transmit_voice |
current member of that exact channel | exact human-signed LIVE voice publication |
| Drawing query | view_operation |
exact assigned cell | exact cell drawing query |
| Drawing publication/tombstone | draw_annotations or manage_annotations |
exact assigned cell | exact retained human-signed drawing authority; sender ownership and moderation override remain mandatory |
| Command publication | command_device_camera or command_device_movement |
exact current target in ORBAT scope or explicit command-target assignment | exact LIVE target; the Node still checks the decoded command subtype |
| Channel, invite, ORBAT, plan, report, and requirement query | the existing read action | exact current channel/visible-unit/operation relationship | exact subject query entries only |
| Browser presence | active FIDO-bound browser session | exact human/session signing key | exact self-bound LIVE heartbeat source |
The heartbeat key's canonical geohash slot is routing metadata for one concrete sample; it does not create a position assertion, drawing cell assignment, or location relationship. Subscribers and Web history must not render or ingest it as tactical position.
An ordinary browser endpoint receives no:
- position publication capability;
- Web core-machine relationship grant;
SnapshotAuthorityGrantV1or purpose-3 publication capability;- committed channel, invite, membership, lifecycle, ownership, or ORBAT publication capability;
- plan or report-requirement publication capability (those remain explicitly Web-machine authored after authenticated human approval and audit);
- deployment-wide wildcard, standalone sensor-family grant, or caller-requested scope.
Channel and ORBAT changes use the actor-signed connected authority mutation request. Directory commits and seals the result once; a related Web core machine may distribute the exact committed bytes under its own machine capability. The browser never re-encodes or publishes that authority state.
Presence and unsigned liveliness retirement¶
The signed, capability-bound browser heartbeat replaces unsigned browser presence. Browser channel-member liveliness tokens, clear channel/principal liveliness keys, and the global liveliness subscriber are removed with no compatibility publisher, reader, or fallback. A UI that needs online channel membership computes it from current verified Directory membership intersected with authenticated, unexpired heartbeat presence; an unsigned transport token never creates membership or presence.
Ordinary browser geolocation permission, a user-agent string, a session field, or a caller-declared device type cannot enable position publication. A future browser-based tactical laptop must be connected-provisioned into a Directory-owned location-capable endpoint relationship before receiving a self position grant. Android, Node, Gateway, and other already provisioned tactical endpoints retain their existing position and heartbeat rules.
Required hostile and runout tests¶
Directory, Web, and affected subscribers prove:
- the exact current FIDO credential, token digest, principal, and session key bind the set; credential substitution, principal-anchor bearer tokens, wrong key, old key, forged proof, challenge replay, and proof replay fail closed;
- browser tokens use the configured lifetime (at most one hour in production), and capabilities never exceed that signed deadline; neither rebases after restart, reconnect, or refresh;
- development overrides require explicit opt-in; mismatched service durations fail at callback, and longer sessions still revoke immediately on logout and credential rotation;
- role without membership/assignment/target and relationship without the required action grant nothing; full-set omission removes revoked roles and relationships atomically;
- retained human drawings remain owned by the human signer; another human needs
manage_annotationsfor cross-owner mutation; - each command grant names one exact current target and camera authority cannot authorize a movement subtype at the receiving Node;
- heartbeat publication is present and verifies for the exact browser subject/key, while position publication is absent and a position attempt fails;
- Web core, committed publication, snapshot, purpose-3, plan-publication, and requirement-publication entries are absent from a browser set;
- Web proxying preserves canonical challenge/request/response bytes and cannot replace the subject, key, generation, entries, or Directory signature;
- signed complete-set replacement precedes connected browser traffic; a failed refresh preserves but does not broaden the prior in-memory set;
- local logout/session expiry/rejection wipe browser key/set/proof state immediately; connected logout exactly binds and idempotently revokes its token/session row, while an unreachable Directory creates no outbox and leaves only the signed bounded residual;
- credential removal and signing-key rotation atomically revoke every anchored active browser-session key, a peer with the newer signed revocation rejects the cached capability, and no affected session can refresh;
- logout token/key/digest substitution cannot name or victimize another row; exact retry succeeds after revocation, while malformed, noncanonical, expired, foreign, and non-browser tokens fail safely; and
- unsigned channel-member liveliness publication and subscription no longer exist or influence UI presence.
This contract is tracked by docs #147, Directory #152, and Web #614. The development lifetime exception is tracked by docs #211.