2026-05-15 15:08:20 +02:00
|
|
|
# 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
|
2026-05-16 00:17:08 +02:00
|
|
|
geth daemon service install
|
2026-05-15 15:08:20 +02:00
|
|
|
geth status
|
|
|
|
|
geth node id
|
|
|
|
|
geth resource list
|
|
|
|
|
geth cas add ./file
|
|
|
|
|
```
|
|
|
|
|
|
2026-05-16 03:17:45 +02:00
|
|
|
The daemon owns local identity, the Iroh endpoint, trust state, resource
|
2026-05-15 15:08:20 +02:00
|
|
|
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`.
|
2026-07-05 18:14:52 +02:00
|
|
|
`geth status` and `geth status --json` report daemon uptime, store schema and
|
|
|
|
|
durability settings, Iroh endpoint/relay/discovery state, and native backend
|
|
|
|
|
health for automation.
|
2026-05-15 15:08:20 +02:00
|
|
|
|
2026-05-21 11:29:29 +02:00
|
|
|
`geth init --admin-key <public-key> --signing-key <private-key> --node-name
|
|
|
|
|
<name>` records an owner/admin keychain, the local user/device/node binding, and
|
|
|
|
|
signs canonical keychain payloads through `ssh-keygen -Y sign` using the
|
2026-05-19 16:04:20 +02:00
|
|
|
`geth.keychain.v1@geth.local` namespace. This is the bootstrap path for
|
2026-05-21 11:29:29 +02:00
|
|
|
admin/YubiKey-rooted trust. `geth keychain sync <node>` pulls the signed
|
|
|
|
|
keychain operation log from an imported peer and imports only operations with
|
|
|
|
|
valid OpenSSH signatures from currently trusted admin keys. `geth node list`
|
|
|
|
|
shows the active reduced node view, and `geth node rename/revoke` require
|
|
|
|
|
`--signing-key` so device-management changes can replicate as verified admin
|
2026-05-21 18:15:10 +02:00
|
|
|
statements. `geth node grant/revoke-grant` and `geth auth grant/revoke` also
|
|
|
|
|
require `--signing-key` in the CLI and store signed auth operations for
|
|
|
|
|
replication.
|
2026-05-21 01:29:55 +02:00
|
|
|
SSH certificate-flow and revocation records carry agent-key signed provenance
|
|
|
|
|
over canonical payloads, and sync import rejects new unsigned or invalidly
|
|
|
|
|
signed records.
|
2026-05-19 16:04:20 +02:00
|
|
|
|
2026-05-16 00:17:08 +02:00
|
|
|
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.
|
|
|
|
|
|
2026-05-15 15:08:20 +02:00
|
|
|
## 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.
|
2026-05-16 03:17:45 +02:00
|
|
|
The default node config uses Iroh's default relay policy for practical
|
|
|
|
|
connectivity; set `[iroh].relay_mode = "disabled"` for local-only/offline
|
2026-05-16 14:28:38 +02:00
|
|
|
development. Named custom relay maps can be selected with
|
2026-05-16 14:33:45 +02:00
|
|
|
`relay_mode = "custom"` and `relay_map = "<name>"`. Iroh local-network
|
|
|
|
|
discovery is enabled by default with `[iroh].local_discovery = true`.
|
2026-05-15 15:08:20 +02:00
|
|
|
|
|
|
|
|
SSH keys are used as admin trust anchors and ecosystem integration points.
|
|
|
|
|
OpenSSH, FIDO, and YubiKey-backed keys can sign geth trust objects through
|
2026-05-16 14:37:16 +02:00
|
|
|
canonical geth envelopes with explicit namespaces such as
|
2026-05-21 01:03:38 +02:00
|
|
|
`geth.keychain.v1@geth.local`. SSH proxying carries SSH protocol bytes over an
|
|
|
|
|
authorized Iroh stream, but SSH is still not a geth transport backend.
|
2026-05-15 15:08:20 +02:00
|
|
|
|
2026-05-16 00:17:08 +02:00
|
|
|
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,
|
2026-05-19 15:56:47 +02:00
|
|
|
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
|
|
|
|
|
<node-id>` and
|
2026-05-18 17:24:10 +02:00
|
|
|
`geth ssh revocation sync <node-id>` pull certificate-flow and revocation
|
|
|
|
|
metadata from an authorized peer over Iroh.
|
2026-05-16 00:17:08 +02:00
|
|
|
|
2026-05-15 15:08:20 +02:00
|
|
|
## MVP Features
|
|
|
|
|
|
|
|
|
|
The bootstrap implementation provides:
|
|
|
|
|
|
2026-05-23 02:40:03 +02:00
|
|
|
- `geth guide [init|owner-setup|enrollment|keys|overlay|service|completions|smoke-test]` for
|
2026-05-23 01:17:30 +02:00
|
|
|
embedded workflow help, including `--admin-key` / `--signing-key` setup
|
|
|
|
|
examples
|
2026-05-23 02:40:03 +02:00
|
|
|
- `geth completions <bash|zsh|fish|powershell|elvish>` for shell completion
|
|
|
|
|
scripts generated from the live CLI command tree
|
2026-05-15 15:08:20 +02:00
|
|
|
- `geth init`
|
2026-05-21 11:29:29 +02:00
|
|
|
- `geth init --admin-key <public-key> --signing-key <private-key> --node-name <name>`
|
2026-05-15 15:08:20 +02:00
|
|
|
- `geth daemon run`
|
2026-05-16 00:17:08 +02:00
|
|
|
- `geth daemon service install|uninstall|start|stop|status|print`
|
2026-05-15 15:08:20 +02:00
|
|
|
- `geth status`
|
|
|
|
|
- `geth node id`
|
2026-05-21 11:29:29 +02:00
|
|
|
- `geth node list`
|
2026-05-21 18:01:38 +02:00
|
|
|
- `geth node enroll request --node-name <name> --capability <resource=capability> [--out <path>]`
|
|
|
|
|
- `geth node enroll submit <owner-node> [--request-id <id>|--path <path>]`
|
|
|
|
|
- `geth node enroll import <path>`
|
|
|
|
|
- `geth node enroll list [--status pending|approved|rejected]`
|
|
|
|
|
- `geth node enroll approve <request-id> --signing-key <private-key>`
|
|
|
|
|
- `geth node enroll sync <owner-node>`
|
2026-05-21 11:29:29 +02:00
|
|
|
- `geth node rename <node-or-name> <name> --signing-key <private-key>`
|
|
|
|
|
- `geth node revoke <node-or-name> --signing-key <private-key>`
|
2026-05-21 18:15:10 +02:00
|
|
|
- `geth node endpoint-add <node-or-name> <endpoint-id> --signing-key <private-key>`
|
|
|
|
|
- `geth node endpoint-revoke <node-or-name> <endpoint-id> --signing-key <private-key>`
|
|
|
|
|
- `geth node grant <node-or-name> <resource> <capability> --signing-key <private-key> [--grant-id <id>]`
|
|
|
|
|
- `geth node revoke-grant <resource> <grant-id> --signing-key <private-key>`
|
2026-05-18 04:03:52 +02:00
|
|
|
- `geth peer export [--out <path>]`
|
|
|
|
|
- `geth peer import <path>`
|
|
|
|
|
- `geth peer list`
|
2026-05-18 12:09:50 +02:00
|
|
|
- `geth peer ping <node-id>`
|
2026-05-18 17:01:50 +02:00
|
|
|
- `geth peer auth-check <node-id> <resource> <capability>`
|
2026-05-23 01:17:30 +02:00
|
|
|
- optional overlay-network planning:
|
|
|
|
|
`geth overlay status`,
|
|
|
|
|
`geth overlay plan <name> [--cidr 172.22.0.0/24]`,
|
|
|
|
|
`geth overlay join <name> --secret <resource-secret> [--cidr 172.22.0.0/24]`,
|
2026-05-23 02:08:51 +02:00
|
|
|
`geth overlay interface-plan <name> [--platform linux|macos|windows]`,
|
|
|
|
|
`geth overlay up <name> [--bearer-secret <route-token>] [--mtu 1280]`,
|
|
|
|
|
`geth overlay down <name>`,
|
|
|
|
|
`geth overlay peers <name>`, `geth overlay send <name> <node>
|
|
|
|
|
--packet-base64 <ipv4-packet>`, `geth overlay recv <name>`, and
|
|
|
|
|
`geth overlay leave <name>`. Join persists local overlay membership, creates
|
|
|
|
|
the overlay resource when needed, assigns a deterministic virtual IP, and
|
|
|
|
|
stores only a BLAKE3 fingerprint of the supplied secret. If the overlay
|
|
|
|
|
resource already has bearer invites, join requires a bearer token with
|
|
|
|
|
`overlay.join`. Packet send validates IPv4 packets and carries them over the
|
|
|
|
|
dedicated `/geth/overlay/1` Iroh ALPN after `overlay.route` authorization.
|
|
|
|
|
`overlay up` creates a real L3 TUN/Wintun-style interface through `tun-rs`,
|
|
|
|
|
reads IPv4 packets from that interface, maps destination overlay IPs to
|
|
|
|
|
imported peer cards, and routes packets over `/geth/overlay/1`. Creating the
|
|
|
|
|
interface is explicit opt-in and may require `CAP_NET_ADMIN`, sudo, or
|
2026-05-30 12:24:40 +02:00
|
|
|
platform-specific network entitlements. Release archives include
|
|
|
|
|
`docs/overlay-platforms.md` with Linux TUN, macOS entitlement, and Windows
|
|
|
|
|
Wintun guidance.
|
2026-05-15 15:08:20 +02:00
|
|
|
- `geth resource list`
|
|
|
|
|
- `geth resource create <kind> <name>`
|
2026-05-19 16:04:20 +02:00
|
|
|
- `geth keychain init [--admin-key <path>] [--signing-key <path>]`
|
2026-05-15 15:08:20 +02:00
|
|
|
- `geth keychain status`
|
2026-05-26 18:53:20 +02:00
|
|
|
- `geth keychain admin-add --admin-key <pub> --signing-key <private> [--principal <name>]`
|
|
|
|
|
- `geth keychain admin-revoke <key-fingerprint> --signing-key <private>`
|
|
|
|
|
- `geth keychain allowed-signers`
|
|
|
|
|
- `geth keychain verify`
|
2026-05-21 11:29:29 +02:00
|
|
|
- `geth keychain sync <node-id-or-name>`
|
2026-05-21 18:01:38 +02:00
|
|
|
- `geth auth sync <node-id-or-name>`
|
2026-05-21 19:31:05 +02:00
|
|
|
- `geth sync status`
|
|
|
|
|
- `geth sync now [node-id-or-name]`
|
2026-05-16 22:18:49 +02:00
|
|
|
- `geth secret status`
|
|
|
|
|
- `geth secret create <resource>`
|
|
|
|
|
- `geth secret rotate <resource>`
|
2026-05-17 02:58:58 +02:00
|
|
|
- `geth secret bearer create <resource> --capability <capability>`
|
|
|
|
|
- `geth secret bearer list`
|
2026-05-19 19:08:08 +02:00
|
|
|
- `geth secret bearer challenge <resource> --capability <capability>`
|
2026-05-20 13:10:34 +02:00
|
|
|
- `geth secret bearer prove <token> <resource> --nonce <nonce> --capability <capability>`
|
|
|
|
|
- `geth secret bearer verify <token> <resource> --nonce <nonce> --response <response> --capability <capability>`
|
|
|
|
|
- `geth secret bearer revoke <resource> <bearer-id>`
|
2026-05-15 15:08:20 +02:00
|
|
|
- `geth auth explain <subject> <resource> <capability>`
|
2026-05-21 18:15:10 +02:00
|
|
|
- `geth auth grant <subject> <resource> <capability> --signing-key <private-key> [--grant-id <id>]`
|
|
|
|
|
- `geth auth revoke <resource> <grant-id> --signing-key <private-key>`
|
2026-05-18 17:18:25 +02:00
|
|
|
- local filesystem CAS commands: `add`, `get`, `fetch`, `hash`, `has`, `pin`,
|
2026-05-19 19:16:30 +02:00
|
|
|
`unpin`, `cleanup`, `providers`, `list`; remote fetch accepts
|
|
|
|
|
`--bearer-secret <secret>`
|
2026-05-21 01:35:00 +02:00
|
|
|
- private CAS envelope commands:
|
|
|
|
|
`geth cas add-private <resource> <path>` and
|
|
|
|
|
`geth cas get-private <resource> <hash> --out <path>`. These use local
|
|
|
|
|
resource secret epochs and are a prototype envelope, not audited AEAD.
|
2026-05-17 21:20:24 +02:00
|
|
|
- local CAS tree objects describe file trees and are stored as CAS blobs
|
2026-05-20 13:30:57 +02:00
|
|
|
- local file-root commands: `geth cas root add/list/scan/sync/apply`; root sync
|
|
|
|
|
pulls authorized remote tree metadata and CAS tree bytes into a peer-qualified
|
|
|
|
|
remote root, and apply materializes a tree without deleting files or
|
2026-05-21 01:40:50 +02:00
|
|
|
overwriting local edits. Repeated syncs keep the previous imported remote
|
|
|
|
|
tree as the base and record durable conflicts when local and remote roots
|
|
|
|
|
both changed.
|
2026-05-18 03:57:26 +02:00
|
|
|
- local file conflict metadata commands:
|
|
|
|
|
`geth cas conflict record/list/resolve`
|
2026-05-16 21:13:33 +02:00
|
|
|
- local DB resource registration: `geth db add <name> <path>` and
|
2026-05-17 20:22:50 +02:00
|
|
|
`geth db status <name>` with schema and `crsql_changes` metadata; the DB
|
2026-05-17 20:32:36 +02:00
|
|
|
crate and daemon can extract typed local `crsql_changes` batches through
|
2026-05-18 22:11:57 +02:00
|
|
|
`geth db changes <name>` and exchange authorized remote batches with
|
|
|
|
|
`geth db sync <node-id> <name>`
|
2026-05-18 04:06:41 +02:00
|
|
|
- local SQLite-backed KV commands: `geth kv create/set/get`; `kv set` accepts
|
|
|
|
|
`--subject <principal>` to exercise local capability checks for non-local
|
2026-05-22 16:24:11 +02:00
|
|
|
callers. The daemon mirrors named KV stores into Iroh Documents and
|
|
|
|
|
`geth kv sync <node-id> <name> [--bearer-secret <secret>]` pulls authorized
|
|
|
|
|
remote updates after receiving a read-only docs ticket through geth control.
|
2026-05-22 14:54:00 +02:00
|
|
|
- local Automerge document commands: `geth document create/status/set/get`;
|
|
|
|
|
CLI input and output are JSON views, while the store keeps durable Automerge
|
|
|
|
|
save bytes. `geth document sync <node-id> <name> [--bearer-secret <secret>]`
|
|
|
|
|
pulls authorized remote Automerge state.
|
2026-05-22 16:45:49 +02:00
|
|
|
- lossy pubsub wakeups: `geth pubsub pub/sub`; local messages are retained in
|
|
|
|
|
a daemon-lifetime ring buffer, while authorized remote publish/subscribe joins
|
|
|
|
|
deterministic native `iroh-gossip` topics after geth control authorization
|
2026-05-16 00:17:08 +02:00
|
|
|
- SSH certificate flow metadata:
|
2026-05-19 15:44:13 +02:00
|
|
|
- `geth ssh cert request --public-key <path> --principal <name> [--subject <principal>]`
|
|
|
|
|
- `geth ssh cert requests [--subject <principal>]`
|
2026-05-19 15:56:47 +02:00
|
|
|
- `geth ssh cert approve <request-id> --ca-key <path> [--sign] [--subject <principal>]`
|
2026-05-19 15:44:13 +02:00
|
|
|
- `geth ssh cert import <request-id> --cert <path> [--subject <principal>]`
|
|
|
|
|
- `geth ssh cert list [--subject <principal>]`
|
2026-05-19 19:16:30 +02:00
|
|
|
- `geth ssh cert sync <node-id> [--bearer-secret <secret>]`
|
2026-05-19 15:44:13 +02:00
|
|
|
- `geth ssh revocation add <kind> <target> [--subject <principal>]`
|
|
|
|
|
- `geth ssh revocation list [--subject <principal>]`
|
|
|
|
|
- `geth ssh revocation export --out <path> [--format jsonl|openssh-krl-spec|openssh-krl] [--subject <principal>]`
|
|
|
|
|
- `geth ssh revocation import <path> [--format jsonl|openssh-krl-spec] [--subject <principal>]`
|
2026-05-19 19:16:30 +02:00
|
|
|
- `geth ssh revocation sync <node-id> [--bearer-secret <secret>]`
|
2026-05-21 01:49:48 +02:00
|
|
|
- SSH proxy/admin over Iroh:
|
|
|
|
|
`geth ssh proxy <node-id> [--bearer-secret <secret>]` and
|
|
|
|
|
`geth ssh admin-shell <node-id> <help|status|node-id> [--bearer-secret <secret>]`
|
2026-05-20 13:57:14 +02:00
|
|
|
- pipe registry/message commands:
|
|
|
|
|
`geth pipe listen <name> [--node <node-id>] [--bearer-secret <secret>]`,
|
|
|
|
|
`geth pipe connect <name> [--node <node-id>] [--bearer-secret <secret>]`,
|
2026-05-20 13:59:41 +02:00
|
|
|
`geth pipe send <name> [message|--in <path>|--in -] [--node <node-id>] [--bearer-secret <secret>]`,
|
2026-05-21 01:12:01 +02:00
|
|
|
`geth pipe recv <name> [--peek]`, and
|
|
|
|
|
`geth pipe forward-tcp --listen 127.0.0.1:<port> --node <node-id> --target 127.0.0.1:<port>`
|
2026-05-21 01:15:51 +02:00
|
|
|
or `geth pipe forward-unix --listen /tmp/local.sock --node <node-id> --target /tmp/remote.sock`
|
2026-05-15 15:08:20 +02:00
|
|
|
|
2026-05-18 12:09:50 +02:00
|
|
|
`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 <node-id>` uses the local daemon's Iroh endpoint to dial an
|
|
|
|
|
imported peer card and exchange a signed candidate-only peer-card ping.
|
2026-05-18 17:01:50 +02:00
|
|
|
`geth peer auth-check <node-id> <resource> <capability>` 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.
|
2026-05-22 16:10:40 +02:00
|
|
|
`geth cas fetch <node-id> <hash>` uses the same protected Iroh control path as
|
|
|
|
|
an authorization preflight. The remote daemon verifies the caller's signed peer
|
|
|
|
|
card against the observed Iroh EndpointID and requires `cas.fetch` on
|
|
|
|
|
`resource:cas:local`. After that preflight succeeds, the requester fetches the
|
|
|
|
|
blob payload over native `iroh-blobs` (`/iroh-bytes/4`) on the same daemon-owned
|
|
|
|
|
Iroh endpoint, verifies the BLAKE3 hash, stores it in local CAS, and records the
|
|
|
|
|
serving peer as a provider visible with `geth cas providers <hash>`.
|
2026-06-16 02:53:35 +02:00
|
|
|
`geth-iroh` is pinned to `iroh 1.0.0` and compiles the native backend
|
|
|
|
|
libraries `iroh-blobs 0.103.0`, `iroh-docs 0.101.0`, and `iroh-gossip 0.101.0`
|
2026-05-22 16:24:11 +02:00
|
|
|
against the same daemon-owned endpoint generation. KV stores are mirrored into
|
|
|
|
|
native `iroh-docs` namespaces and peers receive read-only document tickets only
|
2026-05-22 16:45:49 +02:00
|
|
|
after geth authorization succeeds. Pubsub joins native `iroh-gossip` topics
|
|
|
|
|
only after the geth control path has authenticated the peer-card endpoint
|
|
|
|
|
binding and checked the topic capability.
|
2026-05-19 19:16:30 +02:00
|
|
|
Remote resource commands that accept `--bearer-secret` can also authorize with a
|
2026-05-20 13:10:34 +02:00
|
|
|
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.
|
2026-05-18 17:24:10 +02:00
|
|
|
`geth ssh cert sync <node-id>` requires `ssh_cert.sync` on `resource:ssh:certs`
|
|
|
|
|
at the peer. `geth ssh revocation sync <node-id>` requires
|
|
|
|
|
`ssh_revocation.sync` on `resource:ssh:revocations`. Both commands merge
|
2026-05-22 16:51:30 +02:00
|
|
|
authorized peer SSH distribution log entries into the local store for offline
|
|
|
|
|
listing and later approval/signing workflows. The current log is materialized
|
|
|
|
|
from signed certificate requests, signed certificate imports, and signed
|
|
|
|
|
revocation records, then reduced locally; it is not a mutable remote ACL blob.
|
|
|
|
|
While the daemon is running, it also performs a background live-sync tick for
|
|
|
|
|
known peers. The default interval is 30 seconds and can be changed in
|
|
|
|
|
`config.toml` with `[sync] live_sync_enabled` and `live_sync_interval_ms`.
|
|
|
|
|
Live-sync stores per-peer high-water cursors in local metadata so repeated ticks
|
|
|
|
|
request only newer SSH certificate-flow and revocation log entries. Sync import
|
|
|
|
|
preserves local metadata by rejecting conflicting records with ids that already
|
|
|
|
|
exist locally.
|
2026-05-18 22:17:27 +02:00
|
|
|
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.
|
2026-05-20 13:26:50 +02:00
|
|
|
File roots advertise `cas-tree:<name>` watermarks when the caller has
|
|
|
|
|
`cas.fetch`; background live-sync imports updated tree metadata and CAS tree
|
|
|
|
|
bytes into peer-qualified remote roots without writing files.
|
2026-05-21 01:40:50 +02:00
|
|
|
When a previous imported remote tree is available, sync compares that base
|
|
|
|
|
against the current local same-named root and the newly imported remote tree.
|
|
|
|
|
Concurrent edit, delete/edit, and divergent rename conflicts are recorded in
|
|
|
|
|
the local conflict table for later resolution.
|
2026-05-20 13:30:57 +02:00
|
|
|
`geth cas root apply <root> --to <path>` can then materialize that tree locally:
|
2026-05-22 14:41:09 +02:00
|
|
|
without a registered local root base it creates missing directories/files, never
|
|
|
|
|
deletes extra files, never overwrites differing local files, and records
|
|
|
|
|
conflicts for manual resolution. When the target path is a registered local file
|
|
|
|
|
root with a previous scan, apply uses a three-way base/local/remote check and
|
|
|
|
|
can safely apply non-conflicting remote creates, updates, deletes, and renames
|
|
|
|
|
only where local state still matches the recorded base.
|
2026-05-18 18:36:03 +02:00
|
|
|
Named KV stores participate in the same live-sync loop once they exist locally:
|
|
|
|
|
manual `geth kv sync <node-id> <name>` and background ticks require `kv.read`
|
2026-05-22 16:24:11 +02:00
|
|
|
on the remote `resource:kv:<name>`. Authorized sync imports from the remote
|
|
|
|
|
Iroh Documents namespace where available, keeps SQLite as the durable local
|
|
|
|
|
index, and imports only remote entries that are not older than the local value.
|
2026-05-22 16:45:49 +02:00
|
|
|
Remote pubsub publish uses the protected Iroh control path as an authorization
|
|
|
|
|
preflight. The remote peer requires `pubsub.publish` on
|
|
|
|
|
`resource:pubsub:<topic>` before recording the message and broadcasting it on a
|
|
|
|
|
deterministic native `iroh-gossip` topic. Pubsub remains lossy and is not
|
|
|
|
|
durable storage; facts that must survive restart or reconcile offline belong in
|
|
|
|
|
CAS, KV, document, or DB resources. Remote pubsub subscribe uses the same
|
|
|
|
|
protected path, requires `pubsub.subscribe` on `resource:pubsub:<topic>`, joins
|
|
|
|
|
the gossip topic, and returns the peer's current daemon-lifetime snapshot for
|
|
|
|
|
that topic.
|
2026-05-18 18:45:10 +02:00
|
|
|
Remote pipe connect uses the same protected Iroh control path and requires
|
|
|
|
|
`pipe.connect` on `resource:pipe:<name>`. The current prototype records a remote
|
2026-05-20 13:57:14 +02:00
|
|
|
connection attempt and whether a listener exists. `geth pipe send <name>
|
2026-05-20 13:59:41 +02:00
|
|
|
[message|--in <path>|--in -] --node <node-id>` uses the dedicated `/geth/pipe/1`
|
|
|
|
|
Iroh ALPN to write a byte message to an authorized peer listener, and `geth pipe
|
|
|
|
|
recv <name>` drains local daemon-lifetime messages.
|
2026-05-19 19:21:42 +02:00
|
|
|
Remote pipe listen uses the same protected path:
|
|
|
|
|
`geth pipe listen <name> --node <node-id>` requires `pipe.listen` on
|
|
|
|
|
`resource:pipe:<name>` before registering a daemon-lifetime listener on the
|
2026-05-21 01:12:01 +02:00
|
|
|
peer.
|
|
|
|
|
`geth pipe forward-tcp --listen 127.0.0.1:<local-port> --node <node-id> --target
|
|
|
|
|
127.0.0.1:<remote-port>` starts a local loopback TCP listener. Each accepted
|
|
|
|
|
connection asks the local daemon to open an authorized `/geth/pipe/1` byte
|
|
|
|
|
stream to the peer. The remote daemon validates the signed endpoint/card binding
|
|
|
|
|
and requires `pipe.forward` on `resource:pipe-tcp:<target>` before connecting to
|
|
|
|
|
the remote loopback TCP target. This is loopback-only in the prototype to avoid
|
|
|
|
|
turning geth into an accidental open proxy.
|
2026-05-21 01:15:51 +02:00
|
|
|
`geth pipe forward-unix --listen <local-socket> --node <node-id> --target
|
|
|
|
|
<remote-socket>` uses the same `/geth/pipe/1` byte stream and requires
|
|
|
|
|
`pipe.forward` on `resource:pipe-unix:<target>` before connecting to the remote
|
|
|
|
|
Unix socket. Unix socket paths must be absolute.
|
2026-05-21 01:03:38 +02:00
|
|
|
`geth ssh proxy <node-id>` is usable as an OpenSSH `ProxyCommand`: the CLI opens
|
|
|
|
|
a local daemon stream, the daemon opens the dedicated `/geth/ssh-proxy/1` Iroh
|
|
|
|
|
ALPN, the remote daemon validates the caller's endpoint/card binding and
|
|
|
|
|
requires `ssh_proxy.connect` on `resource:ssh-proxy:local`, and only then
|
|
|
|
|
connects the stream to `127.0.0.1:22`. SSH remains normal OpenSSH on top of that
|
|
|
|
|
byte stream; SSH is not a geth transport backend.
|
2026-05-21 01:49:48 +02:00
|
|
|
`geth ssh admin-shell <node-id> <command>` is a restricted geth admin workflow
|
|
|
|
|
over the protected Iroh control path. It requires `ssh_proxy.admin_shell` on the
|
|
|
|
|
same resource and supports only built-in commands (`help`, `status`, `node-id`);
|
|
|
|
|
it does not execute host shell commands.
|
2026-05-22 14:54:00 +02:00
|
|
|
Document sync exchanges durable Automerge state with a JSON view for CLI output:
|
2026-05-18 18:49:34 +02:00
|
|
|
manual `geth document sync <node-id> <name>` and background live-sync require
|
2026-05-22 14:54:00 +02:00
|
|
|
`document.read` on `resource:document:<name>` and merge authorized remote state
|
|
|
|
|
when the peer advertises a state timestamp at or after the local document.
|
2026-05-18 22:11:57 +02:00
|
|
|
DB sync is a staged cr-sqlite path: manual `geth db sync <node-id> <name>` and
|
|
|
|
|
background live-sync require `db.sync` on `resource:db:<name>`, exchange typed
|
|
|
|
|
`crsql_changes` batches over the protected Iroh control path, and check remote
|
2026-05-19 19:02:39 +02:00
|
|
|
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
|
2026-05-22 14:55:31 +02:00
|
|
|
database owner's responsibility. The prototype does not use CAS-backed DB
|
|
|
|
|
snapshots or change-batch blobs; that is deferred until large initial catch-up
|
|
|
|
|
needs it. The automated tests use deterministic `crsql_changes` fixtures because
|
|
|
|
|
this dev environment does not provide a `sqlite3` CLI or cr-sqlite extension
|
|
|
|
|
artifact for a real extension-backed integration test.
|
2026-05-18 12:09:50 +02:00
|
|
|
Importing or pinging a peer card never grants capabilities by itself.
|
2026-05-18 17:05:43 +02:00
|
|
|
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.
|
2026-05-18 04:03:52 +02:00
|
|
|
|
2026-05-15 15:08:20 +02:00
|
|
|
## 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
|
2026-05-17 20:17:26 +02:00
|
|
|
- `pipe`: dumbpipe-like byte streams over Iroh; the bootstrap has a local
|
|
|
|
|
daemon registry only
|
2026-05-15 15:08:20 +02:00
|
|
|
- `document`: Automerge documents over Iroh streams
|
2026-05-22 16:45:49 +02:00
|
|
|
- `pubsub`: lossy notifications over native `iroh-gossip` after geth
|
|
|
|
|
authorization, with only an in-memory daemon-lifetime ring buffer
|
2026-05-15 15:08:20 +02:00
|
|
|
- `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
|
2026-05-17 18:26:30 +02:00
|
|
|
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/`
|
2026-05-18 04:06:41 +02:00
|
|
|
for `kv.write_key:apps/foo/config` explain checks. `geth kv set --subject
|
|
|
|
|
<principal>` enforces those local grants for test callers; the local node/agent
|
2026-05-19 15:44:13 +02:00
|
|
|
still has owner access for local administration. SSH certificate and revocation
|
|
|
|
|
commands also accept `--subject <principal>` 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`.
|
2026-05-15 15:08:20 +02:00
|
|
|
|
2026-05-22 14:28:44 +02:00
|
|
|
`geth auth explain <subject> <resource> <capability>` is the operator-facing
|
|
|
|
|
debug path for those decisions. Human output includes the allow/deny result,
|
|
|
|
|
the reason, evaluated auth-op count, and compact diagnostics. JSON output
|
|
|
|
|
includes the same diagnostics so scripts can distinguish discovered-only peers,
|
|
|
|
|
unknown subjects, missing or matched endpoint bindings, missing grants, revoked
|
|
|
|
|
grants, and bearer-secret access without scraping prose.
|
|
|
|
|
|
2026-05-15 15:08:20 +02:00
|
|
|
## 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
|
2026-05-16 03:17:45 +02:00
|
|
|
identity/iroh.ed25519
|
2026-05-15 15:08:20 +02:00
|
|
|
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="<same dir>"
|
|
|
|
|
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
|
|
|
|
|
```
|
|
|
|
|
|
2026-05-21 11:29:29 +02:00
|
|
|
## Owner And Node Management
|
|
|
|
|
|
|
|
|
|
The intended owner setup is SSH-admin-rooted:
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
geth init \
|
|
|
|
|
--admin-key ~/.ssh/id_ed25519_sk.pub \
|
|
|
|
|
--signing-key ~/.ssh/id_ed25519_sk \
|
|
|
|
|
--node-name laptop \
|
|
|
|
|
--capability resource:ssh-proxy:local=ssh_proxy.admin_shell
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
When any owner setup option is used, both `--admin-key` and `--signing-key` are
|
|
|
|
|
required. This prevents accidentally creating an unsigned owner/device/node
|
|
|
|
|
statement that cannot be accepted by another node during keychain sync.
|
|
|
|
|
|
|
|
|
|
This records signed keychain operations for `KeychainInit`, `AdminKeyAdd`,
|
|
|
|
|
`UserAdd`, `DeviceAdd`, `NodeAdd`, and `AgentBind`. The current node identity is
|
|
|
|
|
stable above endpoint rotation: future endpoint bindings should attach to the
|
|
|
|
|
node, not replace it. Node management is done through the reduced keychain view:
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
geth node list
|
|
|
|
|
geth node rename laptop work-laptop --signing-key ~/.ssh/id_ed25519_sk
|
2026-05-21 18:15:10 +02:00
|
|
|
geth node endpoint-add work-laptop <iroh-endpoint-id> --signing-key ~/.ssh/id_ed25519_sk
|
|
|
|
|
geth node grant work-laptop resource:ssh-proxy:local ssh_proxy.connect \
|
|
|
|
|
--signing-key ~/.ssh/id_ed25519_sk
|
|
|
|
|
geth node revoke-grant resource:ssh-proxy:local <grant-id> \
|
|
|
|
|
--signing-key ~/.ssh/id_ed25519_sk
|
2026-05-21 11:29:29 +02:00
|
|
|
geth node revoke work-laptop --signing-key ~/.ssh/id_ed25519_sk
|
|
|
|
|
```
|
|
|
|
|
|
2026-05-26 18:53:20 +02:00
|
|
|
Admin SSH keys are managed through the same signed keychain log:
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
geth keychain admin-add \
|
|
|
|
|
--admin-key ~/.ssh/new_admin.pub \
|
|
|
|
|
--signing-key ~/.ssh/id_ed25519_sk \
|
|
|
|
|
--principal admin
|
|
|
|
|
geth keychain allowed-signers > /tmp/geth.allowed_signers
|
2026-05-27 00:59:52 +02:00
|
|
|
geth keychain allowed-signers --out /tmp/geth.allowed_signers
|
|
|
|
|
geth keychain sign-file \
|
|
|
|
|
--in /tmp/authorized_keys \
|
|
|
|
|
--out /tmp/authorized_keys.sig \
|
|
|
|
|
--signing-key ~/.ssh/id_ed25519_sk
|
|
|
|
|
geth keychain verify-file \
|
|
|
|
|
--in /tmp/authorized_keys \
|
|
|
|
|
--signature /tmp/authorized_keys.sig
|
|
|
|
|
geth keychain sigchain --out /tmp/geth.sigchain.jsonl
|
|
|
|
|
geth keychain publish-bundle \
|
|
|
|
|
--out ./public/.well-known/sshsigchain \
|
|
|
|
|
--signing-key ~/.ssh/id_ed25519_sk \
|
|
|
|
|
--snapshot authorized_keys=/tmp/authorized_keys
|
|
|
|
|
geth keychain verify-checkpoint \
|
|
|
|
|
--checkpoint /tmp/geth.sigchain.checkpoint.json \
|
|
|
|
|
--signature /tmp/geth.sigchain.checkpoint.json.sig \
|
|
|
|
|
--sigchain /tmp/geth.sigchain.jsonl \
|
|
|
|
|
--allowed-signers /tmp/geth.allowed_signers
|
|
|
|
|
geth keychain fetch --url https://example.com/.well-known/sshsigchain/ --import
|
|
|
|
|
geth keychain verify-sigchain --in /tmp/geth.sigchain.jsonl
|
|
|
|
|
geth keychain import-sigchain --in /tmp/geth.sigchain.jsonl
|
|
|
|
|
geth keychain explain <op-id>
|
|
|
|
|
geth keychain explain-signer <key-id>
|
2026-05-26 18:53:20 +02:00
|
|
|
geth keychain verify
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The keychain follows a sigchain model documented in
|
|
|
|
|
`docs/sigchain-keychain.md`: each keychain operation is accepted only if it is
|
|
|
|
|
signed by an admin key from the previously accepted reduced view. This is the
|
|
|
|
|
geth analogue of verifying `git-skm` allowed-signers changes from a prior
|
|
|
|
|
trusted state. The reusable mechanics live in the `geth-keychain` crate,
|
2026-05-26 18:58:25 +02:00
|
|
|
including application-specific signature profiles, allowed-signers projection,
|
|
|
|
|
replay verification through a caller-provided verifier trait, and an appendable
|
2026-05-26 18:53:20 +02:00
|
|
|
JSONL sigchain file format suitable for static hosting with HTTP caching/range
|
2026-05-27 00:59:52 +02:00
|
|
|
requests. The default discovery/publication base is
|
|
|
|
|
`https://example.com/.well-known/sshsigchain/`; publish bundles contain
|
|
|
|
|
`allowed_signers`, `geth.sigchain.jsonl`, `geth.sigchain.checkpoint.json`, and
|
|
|
|
|
`geth.sigchain.checkpoint.json.sig`. Clients can verify checkpoints, fetch
|
|
|
|
|
bundles, import verified sigchains, and remember the last accepted checkpoint to
|
|
|
|
|
reject older static bundles from the same source. `keychain fetch --url` is the
|
|
|
|
|
retrieval location, so local `file://` mirrors work for testing; the signed
|
|
|
|
|
checkpoint still records the advertised publication base URL, and
|
|
|
|
|
`verify-checkpoint --base-url` can pin that value when needed.
|
|
|
|
|
|
|
|
|
|
Signing is mediated by OpenSSH. `--signing-key` may point at a private key file,
|
|
|
|
|
a FIDO/YubiKey OpenSSH security-key stub, or a public key whose private half is
|
|
|
|
|
available in `ssh-agent`. For encrypted private keys, the recommended workflow
|
|
|
|
|
is to unlock the key with `ssh-add` and pass the public key path. For PKCS#11
|
|
|
|
|
tokens, load the key into `ssh-agent` with `ssh-add -s <provider>` and use the
|
|
|
|
|
exported public key path; direct PKCS#11 signing is not exposed by
|
|
|
|
|
`ssh-keygen -Y sign` in a portable way.
|
2026-05-26 18:53:20 +02:00
|
|
|
|
2026-05-21 18:01:38 +02:00
|
|
|
The enrollment flow for a new node is:
|
|
|
|
|
|
|
|
|
|
```sh
|
2026-05-22 01:28:45 +02:00
|
|
|
# On the new node, initialize local state and trust the owner's admin public key:
|
|
|
|
|
geth init
|
|
|
|
|
geth daemon run
|
|
|
|
|
geth keychain init --admin-key ~/.ssh/id_ed25519_sk.pub
|
|
|
|
|
|
|
|
|
|
# Then create a signed enrollment request:
|
2026-05-21 18:01:38 +02:00
|
|
|
geth node enroll request \
|
|
|
|
|
--node-name workstation \
|
|
|
|
|
--capability resource:ssh-proxy:local=ssh_proxy.connect \
|
|
|
|
|
--out /tmp/workstation-enrollment.json
|
|
|
|
|
|
|
|
|
|
# Either submit over Iroh to an imported owner peer:
|
|
|
|
|
geth node enroll submit owner-laptop --path /tmp/workstation-enrollment.json
|
|
|
|
|
|
|
|
|
|
# Or import the JSON on the owner/YubiKey machine:
|
|
|
|
|
geth node enroll import /tmp/workstation-enrollment.json
|
|
|
|
|
geth node enroll list --status pending
|
|
|
|
|
geth node enroll approve <request-id> --signing-key ~/.ssh/id_ed25519_sk
|
|
|
|
|
|
|
|
|
|
# Back on the new node, pull signed identity and authorization state:
|
2026-05-22 01:28:45 +02:00
|
|
|
geth sync now owner-laptop
|
|
|
|
|
geth sync status
|
2026-05-21 18:01:38 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Enrollment requests are signed by the requesting agent key. Approval records
|
|
|
|
|
signed keychain operations for the new device/node/agent binding and signed auth
|
2026-05-22 01:28:45 +02:00
|
|
|
operations for requested resource capabilities. The requesting node must already
|
|
|
|
|
know the owner's admin public key so it can verify the signed operation logs
|
|
|
|
|
before importing them. `geth sync now` pulls both signed logs from the owner
|
|
|
|
|
node through the same path used by background live sync.
|
2026-05-21 18:01:38 +02:00
|
|
|
|
2026-05-21 11:29:29 +02:00
|
|
|
`geth keychain sync <node>` pulls signed keychain operations from an imported
|
|
|
|
|
peer over Iroh and rejects operations that do not have a valid OpenSSH signature
|
|
|
|
|
from a currently trusted admin key over the canonical keychain payload. This is
|
|
|
|
|
the current replicated device-management substrate. It is still a pull-based
|
|
|
|
|
operation log, not yet a CRDT or Keyhive-style convergent authority.
|
|
|
|
|
|
2026-05-21 19:31:05 +02:00
|
|
|
The daemon also runs best-effort live sync for imported peers. `geth sync now
|
|
|
|
|
[node]` triggers the same sync pass immediately, and `geth sync status` reports
|
|
|
|
|
the last local attempt, success, cursor, import count, rejection count, and
|
2026-05-22 15:00:09 +02:00
|
|
|
error per peer stream. `geth sync status --json` also includes per-stream
|
|
|
|
|
`state`, `stale`, `stale_after_ms`, and `next_action` fields so smoke tests can
|
|
|
|
|
fail on stale or failed streams. Keychain and auth sync now use per-peer
|
|
|
|
|
high-water cursors, while receivers still verify every imported signed operation
|
|
|
|
|
before it can affect the reduced keychain or authorization views.
|
|
|
|
|
|
|
|
|
|
## Two-Machine Smoke Test
|
|
|
|
|
|
|
|
|
|
Use two terminals or machines with different `GETH_HOME` values.
|
|
|
|
|
|
|
|
|
|
Owner machine:
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
export GETH_HOME=/tmp/geth-owner
|
|
|
|
|
geth init --admin-key ~/.ssh/id_ed25519_sk.pub \
|
|
|
|
|
--signing-key ~/.ssh/id_ed25519_sk \
|
|
|
|
|
--node-name owner
|
|
|
|
|
geth daemon run
|
|
|
|
|
geth peer export --out /tmp/owner.peer.json
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
New node:
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
export GETH_HOME=/tmp/geth-node
|
|
|
|
|
geth init
|
|
|
|
|
geth daemon run
|
|
|
|
|
geth keychain init --admin-key ~/.ssh/id_ed25519_sk.pub
|
|
|
|
|
geth peer import /tmp/owner.peer.json
|
|
|
|
|
geth node enroll request --node-name workstation \
|
|
|
|
|
--capability resource:cas:local=cas.fetch \
|
|
|
|
|
--capability resource:kv:notes=kv.read \
|
|
|
|
|
--capability resource:document:notes=document.read \
|
|
|
|
|
--capability resource:db:notes=db.sync \
|
|
|
|
|
--capability resource:ssh-proxy:local=ssh_proxy.connect \
|
|
|
|
|
--out /tmp/workstation-enrollment.json
|
|
|
|
|
geth node enroll submit owner --path /tmp/workstation-enrollment.json
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Owner machine:
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
geth node enroll list --status pending
|
|
|
|
|
geth node enroll approve <request-id> --signing-key ~/.ssh/id_ed25519_sk
|
|
|
|
|
geth node grant workstation resource:ssh-proxy:local ssh_proxy.connect \
|
|
|
|
|
--signing-key ~/.ssh/id_ed25519_sk
|
|
|
|
|
echo "hello geth" > /tmp/hello-geth.txt
|
|
|
|
|
geth cas add /tmp/hello-geth.txt
|
|
|
|
|
geth kv create notes
|
|
|
|
|
geth kv set notes greeting "hello geth"
|
|
|
|
|
geth document create notes
|
|
|
|
|
geth document set notes '{"greeting":"hello geth"}'
|
|
|
|
|
geth ssh cert requests
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
New node:
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
geth sync now owner
|
|
|
|
|
geth sync status --json
|
|
|
|
|
geth peer ping owner
|
|
|
|
|
geth cas fetch owner <hash-from-owner-cas-add>
|
|
|
|
|
geth kv sync owner notes
|
|
|
|
|
geth kv get notes greeting
|
|
|
|
|
geth document sync owner notes
|
|
|
|
|
geth document get notes
|
|
|
|
|
geth db add notes /path/to/crsqlite-notes.sqlite
|
|
|
|
|
geth db sync owner notes
|
|
|
|
|
geth ssh cert request --public-key ~/.ssh/id_ed25519.pub --principal "$USER"
|
|
|
|
|
geth ssh cert sync owner
|
|
|
|
|
geth ssh proxy owner
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
If a command fails, the daemon error includes a `next:` line for common recovery
|
|
|
|
|
paths such as importing a peer card, running `auth explain`, granting a missing
|
|
|
|
|
capability, or creating/registering a missing resource.
|
2026-05-21 19:31:05 +02:00
|
|
|
|
2026-05-28 16:44:58 +02:00
|
|
|
## CI, Security, And Releases
|
|
|
|
|
|
2026-07-05 17:51:06 +02:00
|
|
|
The automation-facing compatibility policy is documented in
|
|
|
|
|
[`docs/compatibility.md`](docs/compatibility.md). It defines the intended
|
|
|
|
|
stability rules for CLI commands, `--json` output, the local control JSONL
|
|
|
|
|
protocol, Iroh peer wire protocols, SQLite metadata, and signed operation logs.
|
2026-07-05 17:58:49 +02:00
|
|
|
Command-family stability levels are tracked in
|
|
|
|
|
[`docs/command-stability.md`](docs/command-stability.md).
|
2026-07-05 17:51:06 +02:00
|
|
|
|
2026-05-28 16:44:58 +02:00
|
|
|
GitHub Actions workflows live under `.github/workflows/`:
|
|
|
|
|
|
|
|
|
|
- `ci.yml` runs formatting, clippy, docs, `cargo check`, and workspace tests on
|
|
|
|
|
Linux, macOS, and Windows. Cross-platform test jobs set
|
2026-05-29 15:27:45 +02:00
|
|
|
`GETH_TEST_SKIP_IROH=1` so deterministic unit and integration coverage stays
|
|
|
|
|
stable across host networking differences.
|
|
|
|
|
- `ci.yml` also runs a required Ubuntu Iroh integration smoke job for the live
|
|
|
|
|
daemon-to-daemon paths, including peer control, sync, native CAS/KV/pubsub,
|
|
|
|
|
pipe forwarding, SSH proxy handshakes, and overlay ALPN authorization.
|
2026-05-28 16:44:58 +02:00
|
|
|
- `security.yml` runs RustSec `cargo audit` on pushes, pull requests, manual
|
|
|
|
|
dispatch, and a weekly schedule.
|
|
|
|
|
- `codeql.yml` builds the Rust workspace for GitHub CodeQL analysis.
|
|
|
|
|
- `dependency-review.yml` blocks pull requests that introduce vulnerable
|
|
|
|
|
dependency changes at moderate severity or higher.
|
2026-07-05 18:25:44 +02:00
|
|
|
- `release.yml` builds release archives for Linux, macOS, and Windows, includes
|
|
|
|
|
README/docs/license files, smoke-tests the packaged binary from the archive,
|
|
|
|
|
uploads artifacts, and publishes them on `v*` tags or manual dispatch.
|
2026-05-28 16:44:58 +02:00
|
|
|
- `.github/dependabot.yml` opens weekly Cargo and GitHub Actions update PRs.
|
|
|
|
|
|
|
|
|
|
Local equivalents remain:
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
cargo fmt --all -- --check
|
|
|
|
|
cargo check --workspace --all-targets
|
|
|
|
|
cargo clippy --workspace --all-targets -- -D warnings
|
|
|
|
|
GETH_TEST_SKIP_IROH=1 cargo test --workspace
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Run the Iroh-heavy tests without `GETH_TEST_SKIP_IROH` when working on endpoint,
|
|
|
|
|
peer-card, sync, overlay, or remote module behavior.
|
|
|
|
|
|
2026-05-15 15:08:20 +02:00
|
|
|
## 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.
|