# geth `geth` is a personal, local-first mesh runtime for scripts, devices, databases, documents, blobs, pipes, and future multi-user collaboration. This project is not the Ethereum `geth` client. The project and executable are still named `geth`. ## One Binary There is one executable: `geth`. It has daemon mode and control mode: ```sh geth init geth daemon run geth daemon service install geth status geth node id geth resource list geth cas add ./file ``` The daemon owns local identity, the Iroh endpoint, trust state, resource registry, module router, local metadata store, and synchronized data structures. Most non-daemon commands talk to the daemon through a local Unix socket at `$GETH_HOME/run/geth.sock`. `geth keychain init --admin-key --signing-key ` records the keychain initialization/admin-key operations and signs their canonical payloads through `ssh-keygen -Y sign` using the `geth.keychain.v1@geth.local` namespace. This is the bootstrap path for admin/YubiKey-rooted trust. `geth keychain status` reports the number of stored keychain signatures plus how many currently verify with OpenSSH; rejecting unsigned or invalid replicated keychain ops is still future work. The daemon can also install itself as a user service: ```sh geth daemon service install geth daemon service status geth daemon service uninstall ``` The bootstrap service managers are systemd user units on Linux, launchd user agents on macOS, and per-user scheduled tasks on Windows. These are user-level services, not system services. ## Transport And SSH All remote node-to-node geth communication is designed to happen over Iroh only. SSH is not a geth transport backend, and there is no SSH fallback transport. The default node config uses Iroh's default relay policy for practical connectivity; set `[iroh].relay_mode = "disabled"` for local-only/offline development. Named custom relay maps can be selected with `relay_mode = "custom"` and `relay_map = ""`. Iroh local-network discovery is enabled by default with `[iroh].local_discovery = true`. SSH keys are used as admin trust anchors and ecosystem integration points. OpenSSH, FIDO, and YubiKey-backed keys can sign geth trust objects through canonical geth envelopes with explicit namespaces such as `geth.keychain.v1@geth.local`. Future SSH proxying may carry SSH protocol bytes over authorized Iroh streams, but the geth transport remains Iroh. SSH certificate request and renewal flows are managed as geth metadata. A node can create a certificate request, another machine can approve it and receive an explicit `ssh-keygen -s ...` command suitable for a CA key or YubiKey-backed CA, or pass `--sign` to run `ssh-keygen` immediately and import the resulting `-cert.pub` for distribution. Certificate and key revocation entries are tracked locally and can be exported as JSONL or as an OpenSSH KRL specification file or a binary OpenSSH KRL generated through `ssh-keygen -k`. `geth ssh cert sync ` and `geth ssh revocation sync ` pull certificate-flow and revocation metadata from an authorized peer over Iroh. ## MVP Features The bootstrap implementation provides: - `geth init` - `geth daemon run` - `geth daemon service install|uninstall|start|stop|status|print` - `geth status` - `geth node id` - `geth peer export [--out ]` - `geth peer import ` - `geth peer list` - `geth peer ping ` - `geth peer auth-check ` - `geth resource list` - `geth resource create ` - `geth keychain init [--admin-key ] [--signing-key ]` - `geth keychain status` - `geth secret status` - `geth secret create ` - `geth secret rotate ` - `geth secret bearer create --capability ` - `geth secret bearer list` - `geth secret bearer challenge --capability ` - `geth secret bearer prove --nonce --capability ` - `geth secret bearer verify --nonce --response --capability ` - `geth secret bearer revoke ` - `geth auth explain ` - `geth auth grant [--grant-id ]` - `geth auth revoke ` - local filesystem CAS commands: `add`, `get`, `fetch`, `hash`, `has`, `pin`, `unpin`, `cleanup`, `providers`, `list`; remote fetch accepts `--bearer-secret ` - local CAS tree objects describe file trees and are stored as CAS blobs - local file-root commands: `geth cas root add/list/scan` - local file conflict metadata commands: `geth cas conflict record/list/resolve` - local DB resource registration: `geth db add ` and `geth db status ` with schema and `crsql_changes` metadata; the DB crate and daemon can extract typed local `crsql_changes` batches through `geth db changes ` and exchange authorized remote batches with `geth db sync ` - local SQLite-backed KV commands: `geth kv create/set/get`; `kv set` accepts `--subject ` to exercise local capability checks for non-local callers; `geth kv sync [--bearer-secret ]` pulls authorized remote updates - local JSON document commands: `geth document create/status/set/get`; `geth document sync [--bearer-secret ]` pulls authorized remote JSON state - local daemon-lifetime pubsub snapshots: `geth pubsub pub/sub`; `geth pubsub pub --node ` publishes to an authorized peer; `geth pubsub sub --node ` reads an authorized peer snapshot - SSH certificate flow metadata: - `geth ssh cert request --public-key --principal [--subject ]` - `geth ssh cert requests [--subject ]` - `geth ssh cert approve --ca-key [--sign] [--subject ]` - `geth ssh cert import --cert [--subject ]` - `geth ssh cert list [--subject ]` - `geth ssh cert sync [--bearer-secret ]` - `geth ssh revocation add [--subject ]` - `geth ssh revocation list [--subject ]` - `geth ssh revocation export --out [--format jsonl|openssh-krl-spec|openssh-krl] [--subject ]` - `geth ssh revocation import [--format jsonl|openssh-krl-spec] [--subject ]` - `geth ssh revocation sync [--bearer-secret ]` - SSH proxy authorization probe: `geth ssh proxy [--bearer-secret ]` - pipe registry/connect commands: `geth pipe listen [--node ] [--bearer-secret ]` and `geth pipe connect [--node ] [--bearer-secret ]` `geth peer export/import/list` is for untrusted peer-card exchange. Peer cards include the Iroh EndpointID plus currently known relay/direct addresses. `geth peer ping ` uses the local daemon's Iroh endpoint to dial an imported peer card and exchange a signed candidate-only peer-card ping. `geth peer auth-check ` sends a protected Iroh control request: the remote daemon verifies that the caller's signed peer card binds the actual Iroh EndpointID before reducing resource-local auth ops. `geth cas fetch ` uses the same protected Iroh control path to request a blob from a peer. The remote daemon only returns bytes when the caller has `cas.fetch` on `resource:cas:local`, and the caller verifies that the bytes hash to the requested BLAKE3 CAS hash before storing them locally. Successful fetches record the serving peer as a local provider, visible with `geth cas providers `. This is the bootstrap transfer path; future work will move provider/fetch behavior to `iroh-blobs`. Remote resource commands that accept `--bearer-secret` can also authorize with a resource-scoped bearer proof generated from the private bearer token returned at creation time. The persisted auth log stores a public bearer id and token verifier, not the private token. This does not enroll the caller as a trusted node; it only unlocks the requested capability on that one resource. `geth ssh cert sync ` requires `ssh_cert.sync` on `resource:ssh:certs` at the peer. `geth ssh revocation sync ` requires `ssh_revocation.sync` on `resource:ssh:revocations`. Both commands merge authorized peer metadata into the local store for offline listing and later approval/signing workflows. While the daemon is running, it also performs a background live-sync tick for known peers every 30 seconds. Live-sync stores per-peer high-water cursors in local metadata so repeated ticks request only newer SSH certificate-flow and revocation records. Before probing individual modules, the daemon asks the peer for an authorized sync-status summary over Iroh. The peer only returns stream watermarks for resources where the caller already has the matching capability, letting the local daemon skip unchanged or unauthorized streams. Named KV stores participate in the same live-sync loop once they exist locally: manual `geth kv sync ` and background ticks require `kv.read` on the remote `resource:kv:` and import only remote entries that are not older than the local value. Remote pubsub publish uses the protected Iroh control path too. The remote peer requires `pubsub.publish` on `resource:pubsub:` before recording the message in its local daemon-lifetime ring buffer. Pubsub remains lossy and is not durable storage. Remote pubsub subscribe uses the same protected path and requires `pubsub.subscribe` on `resource:pubsub:` before returning the peer's current daemon-lifetime snapshot for that topic. Remote pipe connect uses the same protected Iroh control path and requires `pipe.connect` on `resource:pipe:`. The current prototype records a remote connection attempt and whether a listener exists; byte streaming and forwarding are still future work. Remote pipe listen uses the same protected path: `geth pipe listen --node ` requires `pipe.listen` on `resource:pipe:` before registering a daemon-lifetime listener on the peer. `geth ssh proxy ` also uses the protected Iroh control path. The remote peer validates the caller's endpoint/card binding and requires `ssh_proxy.connect` on `resource:ssh-proxy:local` before returning proxy connection metadata. The current prototype does not carry SSH bytes or connect to remote sshd yet; it only proves the authorization gate. Document sync is a bootstrap JSON last-writer-wins path before Automerge: manual `geth document sync ` and background live-sync require `document.read` on `resource:document:` and import only state that is not older than the local document timestamp. DB sync is a staged cr-sqlite path: manual `geth db sync ` and background live-sync require `db.sync` on `resource:db:`, exchange typed `crsql_changes` batches over the protected Iroh control path, and check remote schema metadata against the local DB before applying and advancing the per-peer cursor. Compatible batches are inserted into the local `crsql_changes` table or view; for real cr-sqlite databases, loading/configuring cr-sqlite remains the database owner's responsibility. Importing or pinging a peer card never grants capabilities by itself. When `[iroh].local_discovery = true`, the daemon also advertises and discovers signed peer cards on LAN using a geth-specific mDNS TXT payload. That payload is candidate metadata only; all geth node-to-node requests still run over Iroh. ## Resource Modules Everything meaningful is modeled as a resource. Planned resource kinds are: - `db`: SQLite/cr-sqlite synchronization - `kv`: Iroh Documents backed key-value stores - `pipe`: dumbpipe-like byte streams over Iroh; the bootstrap has a local daemon registry only - `document`: Automerge documents over Iroh streams - `pubsub`: lossy notifications, not authoritative storage; the bootstrap keeps only an in-memory daemon-lifetime ring buffer - `cas`: content-addressed blob storage and distribution - `ssh-proxy`: authorized SSH proxy/admin access over Iroh Authorization is resource-scoped and capability-based. Bearer secrets may grant specific resource capabilities but do not create trusted node identity. The auth evaluator supports scoped KV write grants such as `kv.write_prefix:apps/foo/` for `kv.write_key:apps/foo/config` explain checks. `geth kv set --subject ` enforces those local grants for test callers; the local node/agent still has owner access for local administration. SSH certificate and revocation commands also accept `--subject ` on local metadata operations to exercise the same capability checks: certificate requests/read/approval/import use `ssh_cert.request`, `ssh_cert.read`, `ssh_cert.approve`, and `ssh_cert.import` on `resource:ssh:certs`, while revocation publish/read/import use `ssh_revocation.publish`, `ssh_revocation.read`, and `ssh_revocation.import` on `resource:ssh:revocations`. ## Local State If `GETH_HOME` is set, geth uses it. Otherwise it uses an OS-specific data directory. The bootstrap layout is: ```text $GETH_HOME/ geth.sqlite config.toml identity/agent.ed25519 identity/iroh.ed25519 cas/blobs/ run/geth.sock ``` ## Quick Start In one shell: ```sh export GETH_HOME="$(mktemp -d)" cargo run -p geth -- init cargo run -p geth -- daemon run ``` In another shell: ```sh export GETH_HOME="" cargo run -p geth -- status cargo run -p geth -- node id echo "hello geth" > /tmp/hello-geth.txt cargo run -p geth -- cas add /tmp/hello-geth.txt cargo run -p geth -- cas list ``` ## Authorization Direction The MVP defines the split between: - keychain: SSH-rooted users, devices, nodes, agents, and endpoint bindings - auth: resource-local signed authorization operations and capability grants - secrets: resource master secrets, epochs, envelopes, and bearer access The current code does not implement Keyhive, BeeKEM, strong forward secrecy, or post-compromise security. It leaves room for future local-first, replicated auth logs and BeeKEM/CGKA-style group key evolution.