geth/AGENTS.md

14 KiB

AGENTS.md

This repository is geth, a Rust monorepo for a personal, local-first, Iroh-based mesh runtime. It is not the Ethereum geth client.

geth has one executable only:

  • daemon mode: geth daemon run
  • control mode: geth status, geth node ..., geth cas ..., etc.

Do not add separate gethd or gethctl binaries.

Core Invariants

  • All remote geth node-to-node communication must be over Iroh.
  • SSH is not a geth transport and must not become a fallback transport.
  • SSH keys are admin trust anchors and ecosystem integration points.
  • SSH certificate flows and SSH proxying are geth-managed workflows carried by local control now and Iroh later.
  • The daemon owns the Iroh endpoint, local identity, local metadata store, resource registry, module routing, and synchronized data structures.
  • Discovery is untrusted and never grants capabilities or trust.
  • Authorization is resource-scoped and capability-based.
  • Signed data must use deterministic canonical encoding, not arbitrary JSON.
  • Bearer secrets may grant resource-specific access but must not create trusted node identity or trust graph mutation rights.

Workspace Shape

  • Binary crate: crates/geth
  • CLI definitions and formatting: crates/geth-cli
  • Daemon orchestration and local control: crates/geth-node
  • Local JSONL control protocol: crates/geth-control
  • Shared newtypes: crates/geth-types
  • Canonical encoding/hash helpers: crates/geth-codec
  • Config/path handling: crates/geth-config
  • Local SQLite metadata store: crates/geth-store
  • Local CAS: crates/geth-cas
  • SSH trust/cert models: crates/geth-ssh-identity

Keep crates focused. Do not create a geth-common crate.

Implementation Rules

  • Prefer existing crate boundaries and patterns.
  • Libraries should use thiserror; binaries may use anyhow.
  • Keep async only where it is useful.
  • Treat network and control inputs as untrusted.
  • Do not implement complex cryptographic protocols casually. Add types, roadmap notes, and explicit non-claims when a feature is only scaffolded.
  • Do not claim Keyhive/BeeKEM-level security properties until they are actually designed, implemented, and tested.
  • User service installation must target user service managers, not system service managers.

Testing Requirements

Every behavior change should include tests proportional to risk:

  • Add unit tests for pure data models, reducers, encoders, path generation, and command builders.
  • Add integration tests for CLI/daemon behavior when practical.
  • Use temp directories and temp GETH_HOME values.
  • Avoid tests that require privileged services, real YubiKeys, real network relays, or host service-manager mutation.
  • For platform-specific behavior, use generated-definition tests or cfg guards.

Before finishing a code change, run at least:

cargo fmt --all -- --check
cargo check --workspace --all-targets
cargo test --workspace

Also run clippy when reasonable:

cargo clippy --workspace --all-targets -- -D warnings

If a command cannot be run, state why in the final report.

Documentation And Roadmap

Keep documentation updated with behavior changes:

  • Update README.md for user-visible commands or workflows.
  • Update docs/architecture.md for architectural boundaries or invariants.
  • Add or update ADRs in docs/adr/ for durable decisions.
  • Update docs/roadmap.md whenever a feature is added, completed, split, or deferred.

Roadmap items should be actionable and checkable:

  • Use [ ], [~], or [x].
  • Include acceptance criteria.
  • Split vague items before implementation.
  • Do not mark an item done unless the acceptance criteria are met or revised honestly.

Current Feature Boundaries

  • Local daemon, local control socket, local identity, local store, local CAS, daemon-owned Iroh endpoint startup, built-in relay-mode config, SSH certificate metadata, revocation metadata, user service definitions, and a pinned geth-iroh endpoint wrapper with protocol-router scaffold, peer-card types, manual signed peer-card export/import/list commands, geth peer ping and geth peer auth-check over Iroh, signed peer-card LAN discovery payloads, authorized geth cas fetch, geth ssh cert sync, and geth ssh revocation sync over the Iroh control ALPN, a background SSH metadata, keychain/auth, KV, DB, document, and file-root live-sync loop with per-peer cursors in module_state, geth sync status/geth sync now, and an authorized sync-status summary that lets peers disclose only permitted stream watermarks before module pulls, untrusted discovery-backend trait, custom relay-map config, and Iroh local-network discovery toggle exist.
  • Canonical signed-operation envelopes exist for keychain/auth signature payloads. The keychain reducer builds an active identity view for admin keys, users, devices, nodes, agents, and endpoint bindings.
  • The auth reducer builds a current permission view for resources, grants, groups, and bearer access. The daemon persists local auth grant/revoke ops and uses them for auth explain. Enrollment approval, geth node grant/revoke-grant, and geth auth grant/revoke sign auth ops through OpenSSH when invoked through the CLI. geth auth sync <node> imports only auth ops signed by currently trusted admin keys. kv set --subject <principal> enforces local KV write grants for non-local test callers. Broader daemon-side module enforcement is still roadmap work.
  • The daemon persists local keychain ops and reduces them for keychain status. geth init --admin-key <pub> --signing-key <key> --node-name <name> records signed owner/user/device/node/agent binding operations. keychain init --signing-key <path> signs recorded keychain ops with ssh-keygen -Y sign under the geth.keychain.v1@geth.local namespace and stores signatures locally. keychain status verifies stored signatures with OpenSSH when public key material is available. geth keychain sync <node> imports only keychain ops with valid OpenSSH signatures from currently trusted admin keys. geth node list/rename/revoke/grant are the current node-management surface over that reduced keychain/auth view; rename and revoke require --signing-key. geth node enroll request|submit|import|list|approve|sync is the smooth new-device flow.
  • Local CAS supports pin/unpin metadata, surfaced through cas list, and cas cleanup evicts unpinned blobs while retaining pinned blobs. The daemon can fetch CAS blobs from an imported signed peer card over Iroh when the peer grants cas.fetch on resource:cas:local; successful fetches record local provider metadata visible through geth cas providers <hash>. Full iroh-blobs provider integration is still roadmap work. geth cas add-private/get-private stores prototype encrypted CAS envelopes gated by local resource secret epochs. The envelope is not audited AEAD and must not be described as forward-secret or post-compromise-secure.
  • The CAS crate can build deterministic tree objects for local file trees and store those manifests as CAS blobs. The daemon can register and scan local file roots, reporting create/update/delete/rename changes without writing back to the working tree. geth cas root sync <node-id> <name> can pull authorized remote tree metadata and CAS tree bytes, recording a peer-qualified remote root with path remote:<node>:<name> without applying files or overwriting same-named local roots. Background live-sync refreshes authorized remote file roots from sync-status cas-tree:<name> watermarks. geth cas root apply materializes missing files/directories from a CAS tree without deleting extras or overwriting local edits, and records conflicts for manual resolution. Repeated file-root syncs retain the previous imported remote tree as the base and record durable concurrent edit, delete/edit, and divergent rename conflicts when local and remote roots both changed. Richer three-way file application remains roadmap work.
  • DB resources can be registered locally and report local-only status plus a read-only SQLite schema summary/hash and crsql_changes metadata when present. The DB crate and daemon can extract typed read-only crsql_changes batches through geth db changes. geth db sync <node-id> <name> can pull authorized typed crsql_changes batches over the protected Iroh control ALPN with db.sync on the remote resource:db:<name>, compare schema metadata, apply compatible batches into local crsql_changes, and advance per-peer/per-DB cursors during manual or background live-sync. Loading and configuring cr-sqlite for real application databases is still outside the bootstrap.
  • KV stores support local SQLite-backed create/set/get plus authorized geth kv sync <node-id> <name> over Iroh. Background live-sync refreshes local KV stores from known peers with per-peer/per-KV cursors. Iroh Documents replication and command-level prefix-capability enforcement are still roadmap work. The auth evaluator already understands kv.write_prefix:<prefix> grants for kv.write_key:<key> requests.
  • Document resources can be registered locally and updated with validated local JSON state. geth document sync <node-id> <name> and background live-sync can pull authorized JSON last-writer-wins state over Iroh. Automerge editing/state and true CRDT sync are still roadmap work.
  • Pubsub supports local daemon-lifetime publish/subscribe snapshots through a bounded in-memory ring buffer. Remote publish over the protected Iroh control ALPN requires pubsub.publish on resource:pubsub:<topic>. Remote subscribe over the same path requires pubsub.subscribe and returns the peer's current daemon-lifetime snapshot. Iroh-gossip replication and private topics are still roadmap work.
  • Pipe listen/connect supports a daemon-lifetime registry. Remote geth pipe connect <name> --node <node-id> uses the protected Iroh control ALPN and requires pipe.connect on resource:pipe:<name>. Remote geth pipe listen <name> --node <node-id> requires pipe.listen on the same resource before creating a daemon-lifetime listener on the peer. geth pipe send <name> [message|--in <path>|--in -] --node <node-id> carries a byte message over the dedicated /geth/pipe/1 ALPN when pipe.connect is authorized, and geth pipe recv <name> drains local daemon-lifetime messages. geth pipe forward-tcp --listen 127.0.0.1:<port> --node <node-id> --target 127.0.0.1:<port> opens authorized bidirectional byte streams over /geth/pipe/1; the remote daemon requires pipe.forward on resource:pipe-tcp:<target> before connecting to the loopback target. geth pipe forward-unix --listen <local-socket> --node <node-id> --target <remote-socket> uses the same Iroh path and requires pipe.forward on resource:pipe-unix:<target> before connecting to an absolute Unix socket path.
  • geth ssh proxy <node-id> is a streaming OpenSSH ProxyCommand-style path. The CLI streams through the local daemon, the daemon uses /geth/ssh-proxy/1 over Iroh, the remote daemon requires ssh_proxy.connect on resource:ssh-proxy:local, and only then connects to 127.0.0.1:22. SSH is still not a geth transport backend. geth ssh admin-shell <node-id> <help|status|node-id> is a restricted geth admin workflow over the protected Iroh control path, requires ssh_proxy.admin_shell, and never executes host shell commands.
  • Resource secret epoch metadata can be created, rotated, and listed locally. Bearer access metadata can be created/listed/revoked as resource-scoped auth ops and must not allow trust graph mutation capabilities. Bearer challenge/proof/verify commands exist for resource-scoped possession checks. Remote module authorization paths can accept optional bearer proofs without enrolling the caller as a trusted node. Bearer creation separates the public bearer id stored in auth logs from the private bearer token returned to the caller once. Payload encryption and key envelopes are still roadmap work.
  • SSH revocations can be exported as JSONL, OpenSSH KRL specification text, or binary OpenSSH KRL files generated through ssh-keygen. JSONL and OpenSSH KRL specification imports are supported; binary KRL import is unsupported because OpenSSH KRL files are not enumerable through OpenSSH tooling. Tests cover public-key and certificate binary KRL revocations when ssh-keygen is available. geth ssh cert approve emits the OpenSSH signing command by default; approve --sign runs ssh-keygen, imports the resulting certificate, and marks the request signed when local signing succeeds. Local SSH certificate and revocation commands accept optional --subject principals and enforce ssh_cert.* capabilities on resource:ssh:certs plus ssh_revocation.* capabilities on resource:ssh:revocations for non-owner subjects. Authorized peers can pull SSH certificate-flow metadata with ssh_cert.sync on resource:ssh:certs and revocation metadata with ssh_revocation.sync on resource:ssh:revocations. The daemon live-syncs known peers using per-peer cursors; [sync] live_sync_enabled and live_sync_interval_ms in config.toml control that loop. Conflicting records with already-known ids are rejected during sync import, and new cert-flow/revocation records must carry valid agent-key signed provenance over canonical payloads. This is pull-only metadata sync, not yet a CRDT/resource-log replication model.
  • cr-sqlite apply, iroh-docs, iroh-blobs provider/fetch, Automerge sync, broader auth enforcement, and Keyhive/BeeKEM-style authorization are future roadmap items unless implemented later.