SUBSYSTEM_SECURITY - mark-ik/graphshell GitHub Wiki
Status: Active / Project Goal
Subsystem label: security
Long form: Security & Access Control Subsystem
Scope: Identity integrity, trust boundaries, workspace grant enforcement, cryptographic correctness, and denial-path observability โ across local operations and Verse sync
Subsystem type: Cross-Cutting Runtime Subsystem (see TERMINOLOGY.md)
Peer subsystems: diagnostics (Diagnostics), accessibility (Accessibility), storage (Persistence & Data Integrity), history (Traversal & Temporal Integrity)
Doc role: Canonical subsystem implementation guide (summarizes guarantees/roadmap and links to Verse/registry details; avoid duplicating security contract prose across feature plans)
Sources consolidated:
-
2026-02-22_registry_layer_plan.mdPhase 5.5 (workspace access control spec) -
../../verse_docs/implementation_strategy/2026-02-23_verse_tier1_sync_plan.mdยงยง2, 7, 9.5 (identity, trust, encryption, access control) -
archive_docs/checkpoint_2026-02-24/2026-02-24_step5_5_workspace_access_control.md(archived Phase 1 implementation status) Related:PLANNING_REGISTER.md(P1/P2 priority tasks reference Phase 5.4/5.5 done gates) Architecture cleanup:2026-03-08_unified_security_architecture_plan.md
Policy authority: This file is the single canonical policy authority for the Security subsystem. Supporting security docs may refine contracts, interfaces, and execution details, but must defer policy authority to this file. Policy in this file should be distilled from canonical specs and accepted research conclusions.
Adopted standards (see 2026-03-04_standards_alignment_report.md for full rationale):
- Noise Protocol Framework (via iroh QUIC) โ all Verse transport connections; XX handshake pattern.
- FIPS 197 / NIST SP 800-38D โ AES-256-GCM at-rest encryption; 256-bit key, 12-byte OsRng nonce, 16-byte tag. Nonce never reused.
- libp2p specs (via iroh) โ Tier 1 bilateral transport (QUIC, PeerID, Identify, Noise); Tier 2 GossipSub 1.1.
-
W3C DID Core 1.0 โ Verse peer identity as
did:keyDID derived from Ed25519 public key. Used in Verse wire formats; irohNodeIdis the internal form. - WASI Preview 1 (via extism) โ WASM mod capability restriction and sandboxing.
Referenced as prior art (no conformance obligation):
- ActivityPub / AT Protocol โ federated identity design patterns. Neither adopted; W3C VC + DID replace ActivityPub for Verse knowledge objects (see standards report ยง4.2).
- Default-deny policy: Access-control and grant evaluation defaults to deny when capability or trust evaluation is incomplete.
- Intent-coverage policy: Security-sensitive intents/operations require explicit grant matrix coverage before release.
- Crypto-correctness policy: At-rest and in-transit cryptographic requirements are mandatory boundaries, not optional enhancements.
- Identity-trust policy: Identity resolution and trust state must be explicit, versioned, and diagnosable.
- Denial-observability policy: Security denials, bypass attempts, and fallback paths must surface with structured diagnostics.
- Shared-surface policy: Settings pages, diagnostics panes, and workbench/Navigator surfaces may expose trust, grant, capability, and permission state, but security authority remains in identity/trust/grant/runtime boundaries rather than UI-local toggles.
Security is not a feature of Verse. It is a cross-cutting guarantee that must hold regardless of which sync backend is active, which mods are loaded, or which protocol paths are exercised.
The dominant failure mode is silent contract erosion: a new sync path silently bypasses access checks, a new intent variant isn't covered by the grant matrix, encryption is accidentally skipped for a new persistence keyspace, or a mod loads without capability restriction. None of these produce visible errors. All produce silent security regressions.
Without subsystem-level treatment, every new GraphIntent variant, every Verse protocol change, every mod capability declaration, and every persistence path extension becomes an unaudited trust boundary crossing.
Security is also a cross-surface concern: pairing, grant review, capability status, and degraded identity state must be exposed through shared UI surfaces without allowing those surfaces to invent alternate enforcement paths.
For web-content-backed nodes, this includes the browser-grade safety signals users rely on to decide whether to trust and interact with content at all: transport trust, certificate identity, mixed-content degradation, and per-origin permission state for sensitive capabilities such as camera, microphone, location, and notifications. If those states exist only in diagnostics or settings pages, the system has already failed the most important safety interaction.
| Layer | Security Instantiation |
|---|---|
| Contracts | Identity integrity, trust boundary, grant enforcement, cryptographic correctness, mod sandboxing โ ยง3 |
| Runtime State |
IdentityRegistry (keypair, trust store, peer sessions), SyncWorker (grant enforcement), ModRegistry (capability restrictions) |
| Diagnostics |
security.* + verse.sync.access_* channel families โ ยง5 |
| Validation | Contract tests (grant matrix, denial paths), harness scenarios (verse_access_control), boundary tests โ ยง6 |
-
Single identity source โ Each Graphshell instance has exactly one P2P identity (Ed25519 keypair). The
NodeId(public key) is derived deterministically from theSecretKey. No second identity source may exist without explicit user action. -
Keychain-only storage โ The Ed25519 secret key is stored exclusively in the OS keychain (
keyringcrate). It is never written to disk in plaintext, never logged, and never included in diagnostic output. -
Signature verification completeness โ Every inbound signed payload (SyncUnit, pairing confirmation, peer assertion) is verified against the claimed
NodeIdbefore any state mutation. Verification failures are rejected and logged. - Identity availability degradation โ If the keychain is unavailable (locked, missing, permission denied), the app starts in offline-only mode with explicit diagnostics. No silent fallback to unsigned operations.
- Explicit trust only โ No peer is trusted by default. Trust is established only through the pairing ceremony (code exchange, QR scan, or invite link) with explicit user confirmation.
-
Trust store persistence โ The
TrustedPeerset is persisted inuser_registries.json. Load and save are deterministic round-trips. Corruption detection causes trust store reset with diagnostics (not silent fallback). -
Peer role enforcement โ
PeerRole::Self_grants full read/write on all personal workspaces.PeerRole::Friendgrants are per-workspace, per-permission. Role escalation requires explicit user action. -
Revocation completeness โ
ForgetDeviceremoves all grants, all connection state, and all cached peer data. No residual trust survives revocation.
-
Inbound grant check โ Every inbound
SyncUnitis checked against the grant matrix before processing. Non-granted workspace sync is rejected withverse.sync.access_deniedand does not mutate graph state. -
Read-only enforcement โ Inbound mutating intents from
ReadOnlypeers are rejected. The rejection is deterministic (not timing-dependent) and logged. - Outbound grant respect โ Outbound sync to a peer only includes workspaces for which the peer has an active grant. No workspace data leaks to ungranteed peers.
-
Grant matrix completeness โ Every
GraphIntentvariant that modifies graph state has an associated access level requirement. New intent variants without grant classification are compile-time errors (or, at minimum, runtime rejections with Error-severity diagnostics). -
No implicit grant inheritance โ Adding a new workspace does not automatically share it with existing peers. Sharing requires explicit
GrantWorkspaceAccessintent.
- Transport encryption โ All Verse connections use Noise protocol via iroh QUIC. No plaintext transport path exists.
-
At-rest encryption โ The
SyncLog(per-workspace intent journal) is encrypted with AES-256-GCM. The encryption key is derived from the persistence key stored in the OS keychain. -
Nonce uniqueness โ AES-GCM nonces are never reused. Nonce generation uses
OsRngor a deterministic counter that is never reset. - Ciphertext integrity โ All decryption verifies the GCM authentication tag. Decryption failures produce explicit errors, not silent truncation or empty output.
-
WASM capability restriction โ WASM mods run in
extismsandbox with capability-restricted access. No filesystem, no network, no keychain access unless explicitly declared and granted. - Native mod audit requirement โ Native mods (compiled into binary) are not sandboxed. Any new native mod requires explicit documentation of its security surface.
- Mod channel namespace enforcement โ Mods can only emit diagnostic channels in their declared namespace. Cross-namespace emission is rejected.
-
Mod capability declaration โ
ModManifest.requiresdeclares all capabilities. Loading a mod that requires undeclared capabilities fails withregistry.mod.security_violation.
- Per-node trust visibility โ Any node backed by remote or embedded web content must expose its current trust state in shared chrome while that node is focused or selected. Trust state includes, at minimum, transport security, certificate identity entry point, and mixed-content or degraded-origin warnings.
-
Per-origin permission visibility โ Navigator/workbench chrome must expose the current permission state for sensitive origin-scoped capabilities, including camera, microphone, location, and notifications. The state model must distinguish
allowed,blocked, andpromptat minimum. - No settings-only security state โ Security-critical trust or permission status must not be discoverable only by opening settings, diagnostics, or a secondary inspector. Shared chrome may launch detailed views, but the existence of degraded or granted state must already be visible inline.
- Read-only chrome authority โ Shared chrome may summarize and launch permission/trust management, but final grant/revoke decisions remain owned by the relevant runtime/security authority rather than by ad hoc UI-local state.
Security capability declarations are folded into the relevant registry entries:
Each viewer/surface declares:
transport_encryption: full | partial | none
payload_signing: full | partial | none
grant_awareness: full | partial | none // Does the surface respect workspace grants?
sandbox_level: native | wasm | none
notes: String
-
ViewerRegistryentries: Servo (native, full transport via Noise), Wry (native, OS-level TLS), plaintext (no network). -
ProtocolRegistryentries: protocol handlers declare transport security properties. -
ModRegistryentries: sandbox level, declared requires/provides, capability verification status. - Settings and diagnostics surfaces consume these declarations to present trust, capability, and degradation state; they do not redefine the underlying security properties.
| Channel | Severity | Description |
|---|---|---|
security.identity.key_loaded |
Info | P2P keypair loaded from keychain |
security.identity.key_generated |
Info | New P2P keypair generated (first launch) |
security.identity.key_unavailable |
Error | Keychain access failed |
security.identity.sign_succeeded |
Info | Payload signed successfully |
security.identity.sign_failed |
Error | Signing failed |
security.identity.verify_succeeded |
Info | Peer signature verified |
security.identity.verify_failed |
Error | Peer signature verification failed |
security.trust.peer_added |
Info | New trusted peer added via pairing |
security.trust.peer_revoked |
Info | Peer trust revoked |
security.trust.store_load_failed |
Error | Trust store deserialization/integrity failure |
security.content.trust_state_changed |
Warn | Focused or selected content node trust state changed |
security.content.mixed_content_detected |
Warn | Mixed-content or downgraded subresource detected for a content node |
security.content.permission_state_changed |
Info | Origin permission state changed for a sensitive capability |
verse.sync.access_denied |
Error | Inbound sync for non-granted workspace rejected |
verse.sync.readonly_mutation_rejected |
Warn | ReadOnly peer attempted mutating intent |
verse.sync.grant_created |
Info | Workspace access granted to peer |
verse.sync.grant_revoked |
Info | Workspace access revoked from peer |
security.crypto.nonce_collision_prevented |
Error | Nonce reuse detected and prevented |
security.crypto.decryption_failed |
Error | AES-GCM decryption or tag verification failed |
security.mod.capability_violation |
Error | Mod attempted undeclared capability |
security.mod.sandbox_escape_prevented |
Error | WASM mod attempted unauthorized operation |
- Identity status:
active/degraded (keychain unavailable)/missing - Trust store: peer count, last sync per peer, pending pairing sessions
- Grant matrix summary: workspace โ peer โ access level
- Access denial counters (recent window)
- Mod security: any capability violations in session
- Cryptographic status: encryption active/degraded
- Focused-node trust summary: secure/insecure/degraded transport state, mixed-content warnings, certificate identity entry point
- Focused-origin permission summary: camera, microphone, location, and notifications state
Shared exposure rule:
- The same security-health aggregates may be surfaced in settings or nearby control chrome as read-only status or launch points.
- Navigator/workbench chrome should surface the focused-node trust summary and focused-origin permission summary inline, with escalation paths to detailed inspectors or settings pages when more explanation is needed.
- Denials and degraded trust state should reuse diagnostics-backed summaries rather than each surface inventing bespoke counters or warning logic.
Required watchdog invariants (start โ terminal pairs):
-
security.identity.sign_startedโsign_succeeded | sign_failed(200ms) -
security.identity.verify_startedโverify_succeeded | verify_failed(200ms) -
security.trust.store_load_startedโstore_load_succeeded | store_load_failed(1000ms) -
security.mod.load_startedโload_succeeded | capability_violation | load_failed(2000ms)
-
Contract tests (deterministic) โ Grant matrix (every
GraphIntentvariant's access level requirement), trust store round-trip, keypair load/generate/sign/verify, revocation completeness, nonce uniqueness. -
Integration tests โ
verse_access_controlharness: ReadOnly peer receives updates but local mutations are rejected; non-granted workspace sync emitsaccess_denied. - Denial-path tests โ Every access control branch has an explicit test that exercises the denial path and asserts the diagnostic channel fires.
- Mod sandbox tests โ WASM mod capability restriction tests (attempted filesystem access denied, attempted network access denied).
-
Boundary tests โ No module outside
SyncWorkercan mutate trust store; no module outsideapply_reducer_intents()can apply remote intents.
Required checks for PRs touching:
-
mods/native/verse/(sync worker, trust store, pairing) -
registries/atomic/knowledge.rs,registries/atomic/diagnostics.rs(security channels) -
registries/infrastructure/mod_loader.rs(mod capability enforcement) -
services/persistence/(encryption paths) - Any file adding new
GraphIntentvariants (must include grant classification)
Security-relevant events should be reviewable in a dedicated Diagnostic Inspector section for forensic analysis: who connected, what was granted/denied, when identity operations occurred.
- Full: Keychain available, all crypto active, trust store loaded, grant enforcement active.
- Degraded (offline-only): Keychain unavailable or corrupted. App functions as offline-only graph organizer. No Verse operations. Explicit diagnostics emitted.
- Degraded (trust-reset): Trust store corrupted. All peers untrusted. User must re-pair. Explicit notification.
- Degradation states emit to
security.*channels. - Diagnostic Inspector reflects degraded security status prominently.
- User-visible indicators for: keychain locked, trust store reset, no encryption, focused-node degraded trust, and sensitive permission grants/blocks.
- No silent fallback to unencrypted or unauthenticated operations.
| Owner | Guarantees |
|---|---|
IdentityRegistry |
Keypair lifecycle, signing, verification, persona resolution. The security root. |
SyncWorker |
Transport encryption (Noise via iroh), inbound grant enforcement, access denial emission. |
Trust Store (in user_registries.json) |
Peer persistence, grant matrix, revocation completeness. |
ModRegistry / ModLoader |
Capability restriction, namespace enforcement, sandbox isolation. |
GraphStore / Persistence |
At-rest encryption (AES-256-GCM), nonce management; see Storage Subsystem. |
-
Formalize grant matrix โ Document access level requirement for every
GraphIntentvariant. Add a compile-time or runtime check that new variants specify their grant level. -
Fill identity diagnostic channels โ Ensure all sign/verify/key operations emit to
security.identity.*channels. - Harden denial paths โ For every access control branch, add explicit denial-path test + diagnostic assertion.
- Add trust store integrity check โ Verify trust store deserialization succeeds; on failure, emit error diagnostic and reset with user notification.
- Wire security health summary โ Expose identity/trust/grant/crypto status in diagnostics pane.
- Mod capability audit โ Verify all native mods document their security surface; add capability violation tests for WASM sandbox.
-
Grant matrix CI gate โ Add CI check that new
GraphIntentvariants include grant classification.
What exists:
- Security/access-control requirements are documented across Verse Tier 1 and registry-layer plans, with implementation slices already landed for parts of identity and sync access enforcement.
- Phase 5.4/5.5 done-gate closures are tracked in the planning register and partially represented in code/tests/diagnostics.
- Security subsystem contracts, diagnostics expectations, and degradation modes are now centralized in this guide.
What's missing / open:
- Full grant-matrix coverage and CI gating for new
GraphIntentvariants. - Deterministic denial-path diagnostics validation across all sync branches.
- Trust-store integrity checks/recovery and comprehensive
security.identity.*channel coverage. - A unified authority model across identity, trust/grants, persistence crypto, mod capabilities, and host/content boundary security. See
2026-03-08_unified_security_architecture_plan.md. - Navigator/workbench chrome requirements for focused-node trust indicators and per-origin permission state are now explicit here, but still require a complete runtime data model and projection contract in the Navigator domain.
The subsystem contract is broader than the current runtime implementation model.
Today, security behavior is split across:
- Verse identity/trust/sync code
- persistence encryption
- mod loader capability surfaces
- protocol and host boundary enforcement
- emerging provider-specific capability layers such as Nostr
That split is acceptable, but the authority boundaries are not yet unified. In particular:
-
IdentityRegistryis not yet the single real identity authority surface - trust/grant acceptance is still transport-centric in practice
- diagnostics namespaces reflect implementation history more than subsystem taxonomy
- host/content boundary security is underrepresented relative to sync/grant policy
The architecture plan above is the canonical cleanup path for those gaps.
Based on the Phase 5.4/5.5 done-gate closures and current code:
- Verify
verse.sync.access_deniedis emitted deterministically on all denial paths (not just the primary one). - Add trust store round-trip integrity test (serialize โ corrupt โ deserialize โ detect โ recover).
- Audit all
GraphIntentvariants for grant classification coverage. - Add
security.identity.*channel family to diagnostics channel phase contracts. - Define the focused-node trust-state carrier and per-origin permission-state carrier consumed by Navigator/workbench chrome.
- Use
2026-03-08_unified_security_architecture_plan.mdto normalize identity authority, trust/grant boundaries, and host/content security coverage.
- Depends on Verse sync path stabilization and completion of Phase 5.4/5.5 done gates.
- Some guarantees require shared
GraphIntentclassification metadata patterns coordinated with reducers and registries. - Crypto/keychain overlap with
storagerequires aligned degradation semantics and diagnostics severity conventions.
-
2026-02-22_registry_layer_plan.md(Phase 5.5 workspace access control) -
../../verse_docs/implementation_strategy/2026-02-23_verse_tier1_sync_plan.md(identity/trust/encryption/access control) -
PLANNING_REGISTER.md(P1/P2 and subsystem sequencing) -
SUBSYSTEM_STORAGE.md(at-rest encryption and keychain overlap) -
SUBSYSTEM_DIAGNOSTICS.md(security diagnostic channels/health summaries) -
2026-03-08_unified_security_architecture_plan.md(top-level security taxonomy and authority cleanup)
Security is a guaranteed system property when:
- Every
GraphIntentvariant has an associated grant classification. - Trust boundaries are enforced at the
SyncWorkerlevel with no bypass paths. - All denial paths are tested and emit diagnostics.
- Identity operations are fully observable via security diagnostic channels.
- Trust store corruption is detected and recovered with user notification.
- At-rest encryption is verified (nonce uniqueness, tag verification).
- Mod capability restrictions are enforced and tested.
- Focused-node trust state and sensitive per-origin permission state are surfaced inline in Navigator/workbench chrome and backed by diagnostics/runtime authority rather than UI-local state.
- New sync paths, intent variants, and mod capabilities require security review (CI-gated or documented audit).