# Architecture `geth` is a single-binary local-first mesh runtime. One executable provides both daemon mode and control mode. The daemon owns local identity, metadata storage, the shared Iroh endpoint, resource registry, module routing, and local control socket. Control commands connect to the Unix socket and send typed JSONL requests. Service management is also exposed through the single binary. `geth daemon service ...` installs and controls a user-level service definition for the local daemon. The initial backends are systemd user units on Linux, launchd user agents on macOS, and per-user scheduled tasks on Windows. Geth does not install itself as a privileged system service. ## Iroh-Only Remote Communication Remote geth node-to-node communication is Iroh-only. The daemon will own one shared Iroh endpoint and register module protocols on ALPNs such as `/geth/cas/1`, `/geth/kv/1`, `/geth/pipe/1`, and `/geth/ssh-proxy/1`. The first pinned Iroh integration uses `iroh = 0.90.0`, because newer Rust-1.85-compatible candidates in the 0.93-0.95 range failed to compile through a transitive `ed25519-dalek` prerelease dependency. `geth-iroh` wraps `iroh::Endpoint::builder()`, configures geth ALPNs with `Builder::alpns`, uses `Builder::relay_mode`, persists an `iroh::SecretKey` as hex-encoded 32-byte key material, and shuts down through `Endpoint::close().await`. The default config uses Iroh's default relay policy; local-only/offline development can set `[iroh].relay_mode = "disabled"`. Named custom relay maps are configured under `[iroh.relay_maps.]`, selected with `relay_mode = "custom"` plus `relay_map = ""`, validated at config load, and reported in status as `custom:` without exposing relay URLs. Module ALPNs are registered through `geth-iroh`'s protocol router scaffold. The router owns the default protocol descriptors, rejects duplicate ALPN registrations, and returns explicit unknown-ALPN errors. It does not yet accept or dispatch remote streams; peer authentication and module handlers are later Phase 1 work. The target product should use Iroh relay support for practical internet connectivity and mDNS/LAN discovery for local networks. These are connectivity and candidate-discovery mechanisms only. They do not grant trust, mutate authorization state, or make EndpointID knowledge sufficient for access. The current daemon can enable Iroh's local-network discovery service through `[iroh].local_discovery = true`, which is the default. This publishes and discovers Iroh node addressing. `geth peer export/import/list` supports manual exchange of signed peer cards as untrusted candidates. Peer cards include the Iroh EndpointID plus relay/direct address candidates when the daemon can observe them. `geth peer ping ` dials an imported peer card over Iroh and exchanges signed peer-card metadata. `geth peer auth-check ` sends a protected Iroh control request that validates the caller's signed peer card against the actual Iroh EndpointID before evaluating resource-local capabilities. When local discovery is enabled, the daemon also advertises and discovers signed peer cards through a geth-specific mDNS service. The LAN payload is TXT-encoded signed metadata only; remote geth traffic still uses Iroh. Peer cards are the discovery payload. A peer card carries node ID, agent ID, endpoint candidates, timestamp, signing public key, and an Ed25519 signature over a canonical payload. Imported and ping-discovered peer cards are stored as untrusted metadata in `peer_cards`; trust reduction is future work. `auth explain` reports when a subject is only a discovered peer candidate and denies access. The peer ping path authenticates the Iroh endpoint and peer-card signature, but it does not authorize any resource module. Protected peer control requests must also prove that the signed peer card binds the observed Iroh EndpointID, then reduce resource auth ops; an EndpointID alone is not accepted as a resource principal. The daemon starts this endpoint during `geth daemon run` and keeps it alive for the daemon lifetime. When endpoint startup succeeds, the Iroh EndpointID is recorded as a transport binding for the stable geth node identity. If local UDP binding is unavailable, the daemon keeps local control running and reports the Iroh startup error through status output. SSH keys are not transport keys. They are admin trust anchors and signing identities for keychain and authorization operations. SSH proxying, when added, will carry SSH bytes over an authorized Iroh stream and will not make SSH a geth transport backend. SSH certificate flows use the same split. Nodes can request new OpenSSH certificates or renewals through geth metadata. A machine with the CA key or YubiKey can approve the request and run an explicit `ssh-keygen -s ...` command, then import the resulting certificate for distribution. Certificate and key revocations are stored as signed-list-ready records. The bootstrap can pull certificate-flow metadata over Iroh with `geth ssh cert sync ` when the peer grants `ssh_cert.sync` on `resource:ssh:certs`, and revocation metadata with `geth ssh revocation sync ` when the peer grants `ssh_revocation.sync` on `resource:ssh:revocations`. The daemon also runs a 30-second background live-sync tick for known peers and records per-peer high-water cursors in `module_state`, so repeated ticks request only records at or beyond the last remote cursor. Boundary duplicates are harmless because records are keyed by stable IDs and inserted with replace semantics. Before issuing per-module pulls, the daemon can request an authorized sync status summary over the same protected Iroh control ALPN. The serving peer validates endpoint/card binding and returns only watermarks for streams where the caller already has the required resource capability, which reduces blind polling without letting discovery reveal private resource names. ## Resource Model Everything meaningful is modeled as a resource. Resources have a kind, name, authority reference, local role, replication policy, retention policy, and status. Authorization is resource-scoped and capability-based. Resource kinds: - `db`: cr-sqlite-backed SQLite synchronization - `kv`: Iroh Documents backed key-value data - `pipe`: authorized byte streams and forwarding - `document`: Automerge CRDT documents - `pubsub`: lossy notifications and presence - `cas`: content-addressed blobs - `ssh-proxy`: SSH/admin proxying over Iroh ## Module Overview `geth-cas` is implemented locally first using BLAKE3 hashes and filesystem blob storage. Local pin/unpin metadata is tracked in SQLite and surfaced in `cas list`. `cas cleanup` removes unpinned local blobs while retaining pinned blobs. The CAS crate can build deterministic tree objects that describe directories, files, executable bits, and file blob hashes; those tree objects are stored as CAS blobs. The daemon can register local file roots and scan them into CAS tree objects while reporting create/update/delete/rename changes. These scans are local metadata only and never overwrite the working tree. The daemon also has durable local file-conflict records with explicit resolution choices; future cross-node file sync will create those records automatically instead of silently applying ambiguous remote changes. As a bootstrap network path, `geth cas fetch ` dials an imported signed peer card over the daemon-owned Iroh control ALPN. The serving daemon validates the caller's peer-card signature and observed Iroh EndpointID, then reduces local auth ops and requires `cas.fetch` on `resource:cas:local` before returning blob bytes. The requester verifies that the returned bytes hash to the requested BLAKE3 CAS hash before storing them. Successful fetches update durable local provider metadata keyed by CAS hash and peer node, which can be inspected through `geth cas providers `. Iroh-blobs, encrypted blobs, richer cache policies, cross-node file roots, and automatic conflict detection are future work. `geth-db` currently registers local SQLite paths as DB resources and reports local-only sync status plus a read-only SQLite schema summary/hash. It also inspects `crsql_changes` metadata when that table or view exists, reporting change count, columns, and max `db_version`. The crate and local daemon can extract read-only typed change batches from `crsql_changes` with schema metadata through `db changes`. As a staged network path, `geth db sync ` uses the protected Iroh control ALPN to request remote typed change batches when the caller has `db.sync` on the remote `resource:db:`. The requester checks remote schema metadata against its local DB before advancing its per-peer/per-DB cursor. Loading cr-sqlite and applying remote changes through `crsql_changes` are future work; the current path exchanges and cursors changes but does not mutate the local application database. `geth-kv` currently provides a SQLite-backed local fallback for named KV stores through `kv create/set/get`. `kv set --subject ` evaluates local auth ops for `kv.write_key:` so prefix grants can be tested before networked callers exist. The local node/agent retains owner access for administration. Iroh Documents namespaces remain the target backend, but the bootstrap can sync named KV stores over the protected Iroh control ALPN. `geth kv sync ` requires `kv.read` on the remote `resource:kv:`, transfers entries at or beyond a per-peer/per-KV high-water cursor, and imports only values that are at least as new as the local entry timestamp. The daemon background live-sync loop runs the same KV sync for local KV stores and known peers. Private value encryption should use resource secret epochs before payloads are exposed to remote peers. `geth-document` currently registers local document resources and stores validated JSON state in the local SQLite metadata store through `document create/status/set/get`. This is a bootstrap editing surface, not yet Automerge CRDT state. `geth document sync ` can pull remote JSON state over the protected Iroh control ALPN when the peer grants `document.read` on `resource:document:`. The daemon background live-sync loop runs the same sync for local documents and known peers using per-peer/per-document cursors. The import rule is last-writer-wins by document timestamp. Automerge state encoding and sync are future work. `geth-pubsub` currently supports local publish/subscribe snapshots through the daemon control protocol. Messages live in a bounded in-memory ring buffer and are lost when the daemon stops. This is deliberate: pubsub is a lossy wakeup and presence channel, not authoritative storage. `geth pubsub pub --node ` can publish to an imported peer over the protected Iroh control ALPN. The remote daemon validates endpoint/card binding and requires `pubsub.publish` on `resource:pubsub:` before recording the message in its local ring buffer. `geth pubsub sub --node ` can read an authorized peer's current snapshot for that topic over the same protected path when the caller has `pubsub.subscribe` on `resource:pubsub:`. Iroh-gossip replication and private topics are future work. `geth-pipe` currently supports `pipe listen/connect` against a daemon-lifetime registry. `geth pipe connect --node ` sends an authorized remote connect request over the protected Iroh control ALPN. The remote daemon validates endpoint/card binding and requires `pipe.connect` on `resource:pipe:` before recording the connection attempt and reporting whether a listener exists. This is still a control-plane scaffold for names and connection attempts only; it does not carry bytes or forward sockets yet. `geth-ssh-proxy` currently defines types, command shape, and roadmap stubs. `geth-ssh-identity` defines SSH trust namespaces plus certificate request, approval, certificate import, and revocation-list data models. The bootstrap persists these flows locally and exports revocations as JSONL or OpenSSH KRL specification text. It can also invoke `ssh-keygen -k` to produce a binary OpenSSH KRL; serial and key-ID KRL entries require a CA public key via `--ca-public`, matching OpenSSH behavior. It can import geth JSONL revocation exports and OpenSSH KRL specification source files. Binary OpenSSH KRL files are not enumerable through OpenSSH tooling, so geth treats binary import as unsupported and asks for JSONL or the spec source. Revocation lists are not yet full CRDT-replicated resources, but the daemon can already pull cert-flow and revocation metadata from authorized peers over the protected Iroh control ALPN. Manual sync commands and the background live-sync loop share the same capability checks and cursor state. Local SSH certificate and revocation metadata commands also accept an optional subject principal for authorization testing: non-owner subjects must hold `ssh_cert.*` capabilities on `resource:ssh:certs` or `ssh_revocation.*` capabilities on `resource:ssh:revocations` before requests, approval/import/read operations, or revocation publish/read/import operations are accepted. The live-sync loop first asks for authorized stream watermarks and skips module pulls whose remote high-water value has not advanced. ## Keychain, Auth, And Secrets The identity plane is `geth-keychain`: admin keys, users, devices, nodes, agents, and endpoint bindings. Endpoint rotation must not destroy higher-level node identity. Keychain operations reduce into an active view containing current admin keys, users, devices, node records, agent bindings, and endpoint-to-node bindings. Revoked identity subtrees are excluded from that active view. The daemon persists local keychain init/admin-key operations and `keychain status` reports the reduced local view. OpenSSH signature capture and verification for those operations is still future work. The authorization plane is `geth-auth`: resource-local signed operation logs, grants, revocations, groups, and `auth explain`. Auth operations reduce into a current permission view for resources, grants, groups, and bearer access. The library can explain direct and group grants. The daemon persists local auth grant/revoke operations and `geth auth explain` evaluates that local operation log. Signature validation, replication, and module enforcement are still future work. Capability evaluation supports exact matches plus explicit scoped forms. For KV, `kv.write_prefix:` grants writes requested as `kv.write_key:` only when the key is under that prefix; `kv.write` remains the broad write capability. Command-level KV enforcement is still future work. Both keychain and auth operations use `geth-codec` canonical envelopes for signature payloads. The envelope includes a version, an explicit signature namespace, and the operation payload encoded with postcard. JSON remains useful for CLI/control output, but it is not the signed representation. The payload access plane is `geth-secrets`: resource master secrets, epochs, key envelopes, bearer secrets, and rotation. Revocation for private data is modeled initially as secret epoch rotation. The daemon persists resource secret epoch metadata through `secret create/rotate/status`. Bearer access is recorded as resource-scoped auth operations and rejects trust-mutation capabilities such as `auth.delegate`, `auth.revoke`, and `node.enroll`. The daemon does not yet store payload key material, encrypt resource data, or distribute key envelopes. ## Multi-User Direction The project is structured for future multi-user local-first authorization: - authorization is replicated data, not one mutable ACL blob - resources can carry or delegate to their own auth state - users, devices, nodes, agents, and endpoints are separate principals - capabilities are the underlying permission unit - bearer access is resource-scoped and does not mutate the trust graph - offline revocation is eventual - encryption key distribution is part of authorization ## Keyhive/BeeKEM Roadmap Resource secret epochs are the v0/v1 approximation for private payload access. Later designs can add Keyhive-like convergent capabilities and BeeKEM/CGKA-style group key evolution. The bootstrap does not implement BeeKEM and does not claim strong forward secrecy or post-compromise security. ## Security Invariants - All remote node-to-node communication is over Iroh. - SSH is not a geth transport. - SSH keys are admin trust anchors and signing identities. - Agent/node keys handle routine local identity. - Discovery is untrusted. - Knowing an EndpointID does not grant access. - Bearer secrets are resource-scoped capabilities. - Bearer access does not imply trust graph mutation rights. - Authorization is capability-based and resource-scoped. - SSH certificate issuance must be explicitly approved by an authorized principal before signing. - SSH certificate and key revocations are durable metadata that should be distributed over Iroh, not fetched through unauthenticated discovery. - Network and control decoders treat input as untrusted. - Service installation targets user service managers, not system service managers.