b6a96396
docs: index the doc set, record decisions, and set the v0.0.1 roadmap
a73x 2026-07-26 10:52
Commit message
README.md
| Old | New | ||
|---|---|---|---|
| @@ -79,6 +79,15 @@ Guests boot from cloud images (the default is Ubuntu resolute) via UEFI firmware | |||
| 79 | (`CLOUDHV.fd`) shipped to each host, so the guest owns its kernel and any | 79 | (`CLOUDHV.fd`) shipped to each host, so the guest owns its kernel and any |
| 80 | disk-only image boots unmodified. | 80 | disk-only image boots unmodified. |
| 81 | 81 | ||
| 82 | ## Documentation | ||
| 83 | |||
| 84 | [docs/](docs/README.md) is indexed by what you're trying to do — understand it | ||
| 85 | ([architecture](docs/architecture.md), [ethos](docs/ethos.md)), run a fleet | ||
| 86 | ([cert rotation](docs/cert-rotation.md), [revocation](docs/credential-revocation.md)), | ||
| 87 | use a fleet ([ssh access](docs/ssh-access.md), [mcp](docs/mcp.md)), or see why | ||
| 88 | it's built this way ([decisions](docs/decisions.md)). What ships next is in | ||
| 89 | [ROADMAP.md](ROADMAP.md). | ||
| 90 | |||
| 82 | ## Repository layout | 91 | ## Repository layout |
| 83 | 92 | ||
| 84 | ``` | 93 | ``` |
ROADMAP.md
| Old | New | ||
|---|---|---|---|
| @@ -0,0 +1,49 @@ | |||
| 1 | # Roadmap | ||
| 2 | |||
| 3 | ## v0.0.1 — installable | ||
| 4 | |||
| 5 | The goal: someone with a Linux box goes from [eitri.sh](https://eitri.sh) to a | ||
| 6 | booted VM they can `eitri-ssh` into. Prebuilt binaries fetched from stable | ||
| 7 | URLs — no toolchain, no curl-pipe installer. This is the first tag; history is | ||
| 8 | frozen from here. | ||
| 9 | |||
| 10 | **Binaries & releases** | ||
| 11 | |||
| 12 | - Version-stamped binaries (`--version`), cross-compiled release tarballs + | ||
| 13 | SHA256SUMS (linux amd64/arm64; darwin for `eitri-ssh`) | ||
| 14 | - Artifacts at stable URLs: `eitri.sh/dl/v0.0.1/...` with a `latest` alias, so | ||
| 15 | tooling can bootstrap from them, not just humans | ||
| 16 | |||
| 17 | **eitri.sh** | ||
| 18 | |||
| 19 | - Landing page, rendered docs, downloads, and a `CLOUDHV.fd` firmware mirror — | ||
| 20 | one static site image, replacing the current site | ||
| 21 | |||
| 22 | **Upgrades** | ||
| 23 | |||
| 24 | - Agent: replace binary + restart in place; running VMs survive and are | ||
| 25 | re-adopted | ||
| 26 | - cloud-hypervisor: swap the binary in place; new/restarted VMs use the new | ||
| 27 | version, running VMs keep their old process | ||
| 28 | - Server: stop → replace → start; downtime acceptable, VMs unaffected; | ||
| 29 | migrations run on boot; version skew with agents stays safe | ||
| 30 | |||
| 31 | **Docs** | ||
| 32 | |||
| 33 | - Quickstart: zero-to-first-VM on one host, then add a second host | ||
| 34 | - Upgrade guide, release notes | ||
| 35 | |||
| 36 | **Gate** | ||
| 37 | |||
| 38 | - Clean-slate rehearsal: the quickstart followed verbatim on a fresh host, | ||
| 39 | end-to-end, before the tag exists | ||
| 40 | |||
| 41 | Out of scope: curl installer, OS packages, live cloud-hypervisor handover for | ||
| 42 | running guests, new features. | ||
| 43 | |||
| 44 | ## v0.0.2 — candidates | ||
| 45 | |||
| 46 | - macOS host support: a Mac joins the fleet as a host | ||
| 47 | (Virtualization.framework backend) | ||
| 48 | - Live cloud-hypervisor handover for running guests | ||
| 49 | - Public gateway: route inbound traffic to guest services through eitri | ||
docs/README.md
| Old | New | ||
|---|---|---|---|
| @@ -0,0 +1,32 @@ | |||
| 1 | # eitri docs | ||
| 2 | |||
| 3 | By what you're trying to do: | ||
| 4 | |||
| 5 | **Understand it** | ||
| 6 | |||
| 7 | - [architecture.md](architecture.md) — the architectural invariants and the | ||
| 8 | executable governance that enforces them | ||
| 9 | - [ethos.md](ethos.md) — the design principles; what to fall back on when a | ||
| 10 | debate stalls | ||
| 11 | - [shape.html](shape.html) — the generated, explorable package graph | ||
| 12 | (`make shape` regenerates) | ||
| 13 | |||
| 14 | **Run a fleet** | ||
| 15 | |||
| 16 | - [cert-rotation.md](cert-rotation.md) — rotating the server's QUIC identity, | ||
| 17 | and why expiry is never an emergency | ||
| 18 | - [credential-revocation.md](credential-revocation.md) — leaked host | ||
| 19 | credentials, leaked SSH certs, and the disaster levers | ||
| 20 | - Quickstart and upgrade guide are planned for v0.0.1 — see | ||
| 21 | [ROADMAP.md](../ROADMAP.md) | ||
| 22 | |||
| 23 | **Use a fleet** | ||
| 24 | |||
| 25 | - [ssh-access.md](ssh-access.md) — reaching a guest through the jump gate with | ||
| 26 | your own tenant CA | ||
| 27 | - [mcp.md](mcp.md) — `eitri-mcp`, the MCP server that lets Claude drive VMs | ||
| 28 | |||
| 29 | **Why it's this way** | ||
| 30 | |||
| 31 | - [decisions.md](decisions.md) — the settled decisions, the alternatives they | ||
| 32 | beat, and the reasons | ||
docs/cert-rotation.md
| Old | New | ||
|---|---|---|---|
| @@ -49,3 +49,10 @@ during a window where that is acceptable, host by host. | |||
| 49 | listener support. | 49 | listener support. |
| 50 | - Pushing new pins over the existing authenticated channel (would remove the | 50 | - Pushing new pins over the existing authenticated channel (would remove the |
| 51 | re-enroll requirement). | 51 | re-enroll requirement). |
| 52 | |||
| 53 | ## Related | ||
| 54 | |||
| 55 | - [credential-revocation.md](credential-revocation.md) — revoking a leaked host | ||
| 56 | credential (generation bump), the complementary emergency lever | ||
| 57 | - [ssh-access.md](ssh-access.md) — the SSH CAs, which are separate from the | ||
| 58 | QUIC identity rotated here | ||
docs/decisions.md
| Old | New | ||
|---|---|---|---|
| @@ -0,0 +1,63 @@ | |||
| 1 | # Decisions | ||
| 2 | |||
| 3 | Settled ground: the irreversible or contested choices, what they beat, and why. | ||
| 4 | Three lines each — when a rationale needs more, it lives in the relevant doc. | ||
| 5 | For *how* we decide, see [ethos.md](ethos.md). | ||
| 6 | |||
| 7 | ### Desired-state loop, not RPC | ||
| 8 | |||
| 9 | The server streams desired state; agents converge toward it and report actual | ||
| 10 | state back. Instead of imperative RPC per operation. Level-triggered convergence | ||
| 11 | survives disconnects, crashes, and missed messages — a host re-derives | ||
| 12 | everything from persisted records plus what it observes. Details in | ||
| 13 | [architecture.md](architecture.md). | ||
| 14 | |||
| 15 | ### Guests boot their own kernel (UEFI firmware) | ||
| 16 | |||
| 17 | VMs boot via UEFI firmware (`CLOUDHV.fd`) into the image's own bootloader. | ||
| 18 | Instead of a host-supplied kernel/initrd (cloud-hypervisor's direct-boot path). | ||
| 19 | The guest owns the guest: any disk-only cloud image boots unmodified, and guest | ||
| 20 | kernel upgrades are the guest's business, not the fleet's. | ||
| 21 | |||
| 22 | ### Bring-your-own tenant user CA | ||
| 23 | |||
| 24 | Each tenant signs its own SSH user certs; eitri stores only CA public keys. | ||
| 25 | Instead of the server minting user certs from an eitri-held CA. A compromised | ||
| 26 | eitri server cannot mint access to any tenant's guests — the user-auth trust | ||
| 27 | root lives with the tenant. Details in [ssh-access.md](ssh-access.md). | ||
| 28 | |||
| 29 | ### SSH-CA jump gate, not a mesh | ||
| 30 | |||
| 31 | Guest access is plain OpenSSH through a certificate-verified bastion. Instead | ||
| 32 | of an overlay mesh with per-node identity. Both ends speak stock ssh, | ||
| 33 | verification is by certificate in both directions, and there is no fabric | ||
| 34 | dependency to operate or trust. | ||
| 35 | |||
| 36 | ### Agent-local resource admission | ||
| 37 | |||
| 38 | Each host admits vCPU / memory / disk / address through one serialized gate on | ||
| 39 | the agent. Instead of a server-side scheduling ledger. The host is the source | ||
| 40 | of truth for its own capacity — admission that consults reality cannot drift | ||
| 41 | from it, and a partitioned host stays correct on its own. | ||
| 42 | |||
| 43 | ### Embedded DHCP owns guest addressing | ||
| 44 | |||
| 45 | The agent runs a DHCP server handing each VM a sticky, deterministic address, | ||
| 46 | reserved at create through the same admission gate. Instead of static IP | ||
| 47 | injection via cloud-init or an external DHCP daemon. Addressing is a host | ||
| 48 | resource like memory; owning it host-locally keeps images untouched and needs | ||
| 49 | no external service. | ||
| 50 | |||
| 51 | ### No superuser principal | ||
| 52 | |||
| 53 | Every API principal is `{Tenant, Fleet}`; fleet-level endpoints are gated | ||
| 54 | fleet-only, and no principal crosses tenant boundaries. Instead of a global | ||
| 55 | admin identity. Cross-tenant capability is the thing multi-tenancy exists to | ||
| 56 | prevent, so it is unrepresentable rather than merely forbidden. | ||
| 57 | |||
| 58 | ### Agents pin the server cert by fingerprint | ||
| 59 | |||
| 60 | Agents trust the server's self-signed QUIC cert by fingerprint from the join | ||
| 61 | blob — not by CA path, not by expiry. Instead of a CA hierarchy or expiring | ||
| 62 | trust. An expired cert can never brick a fleet; rotation is deliberate hygiene, | ||
| 63 | not an emergency. Details in [cert-rotation.md](cert-rotation.md). | ||
docs/ssh-access.md
| Old | New | ||
|---|---|---|---|
| @@ -15,7 +15,8 @@ host-key-changed warnings when VM names or IPs are recycled. | |||
| 15 | 15 | ||
| 16 | Two CAs, two directions: **your tenant's user CA** (private key on your machine) | 16 | Two CAs, two directions: **your tenant's user CA** (private key on your machine) |
| 17 | signs what you present; **eitri's host CA** (private key on the server) signs | 17 | signs what you present; **eitri's host CA** (private key on the server) signs |
| 18 | what the gate and VMs present. | 18 | what the gate and VMs present. See [decisions.md](decisions.md) for why eitri |
| 19 | holds no user signing key. | ||
| 19 | 20 | ||
| 20 | ## Bring your own CA (once per tenant) | 21 | ## Bring your own CA (once per tenant) |
| 21 | 22 | ||