3432ac6b
docs: v0.0.4 release notes
a73x 2026-08-08 14:02
Commit message
ROADMAP.md
| Old | New | ||
|---|---|---|---|
| @@ -3,19 +3,19 @@ | |||
| 3 | What shipped in each version is in the | 3 | What shipped in each version is in the |
| 4 | [release notes](https://eitri.sh/docs/releases/); this is what's ahead. | 4 | [release notes](https://eitri.sh/docs/releases/); this is what's ahead. |
| 5 | 5 | ||
| 6 | ## v0.0.4 — candidates | 6 | ## v0.0.5 — candidates |
| 7 | 7 | ||
| 8 | - Connectors: browser clients (claude.ai among them) sign in to the MCP | ||
| 9 | endpoint with OAuth rather than a pasted token—dynamic client registration | ||
| 10 | on eitri-oidc, so a connector enrols itself | ||
| 8 | - Bridged guest networking: a Linux host puts its guests on the operator's | 11 | - Bridged guest networking: a Linux host puts its guests on the operator's |
| 9 | own network — taps on a named bridge, the site's DHCP addresses the guests, | 12 | own network—taps on a named bridge, the site's DHCP addresses the guests, |
| 10 | the agent discovers the result | 13 | the agent discovers the result |
| 11 | - Public gateway: route inbound traffic to guest services through eitri, | 14 | - Public gateway: route inbound traffic to guest services through eitri, |
| 12 | building on published ports | 15 | building on published ports |
| 13 | - A PAT is enough: an opt-in eitri-managed tenant CA signs short-lived SSH | 16 | - Hot-reload trusted CAs into running guests: a CA added to the tenant reaches |
| 14 | certificates, so a bare token can reach its VMs—BYO CAs stay first-class, | 17 | guests that already exist—delivery over vsock or a host file mount that sshd |
| 15 | and a tenant that wants eitri holding no signing power keeps that | 18 | reads per connection, so no recreation and no SSH bootstrap problem. Needs |
| 16 | - Remote MCP: a hosted endpoint (api.eitri.sh/mcp) any client can connect | 19 | changing a tenant's trust set to demand more than a bearer PAT first, or a |
| 17 | to—streamable HTTP, per-tenant auth, TLS at the edge; its identity story | 20 | stolen token becomes access to every guest |
| 18 | is the managed tenant CA above | ||
| 19 | - Live cloud-hypervisor handover for running guests | 21 | - Live cloud-hypervisor handover for running guests |
| 20 | - Provider integration: provision on existing cloud infrastructure (aws, gcp, | ||
| 21 | proxmox, …) | ||
docs/quickstart.md
| Old | New | ||
|---|---|---|---|
| @@ -69,6 +69,14 @@ mkdir -p ~/.local/bin && cp eitri-agent ~/.local/bin/ | |||
| 69 | State lands in `~/.eitri/agent`, logs in `~/Library/Logs/eitri-agent.log`, and | 69 | State lands in `~/.eitri/agent`, logs in `~/Library/Logs/eitri-agent.log`, and |
| 70 | the agent starts with your session. | 70 | the agent starts with your session. |
| 71 | 71 | ||
| 72 | The agent dials its guests over vmnet, and macOS gates local-network dialing | ||
| 73 | per app: the agent needs the **Local Network** permission. Run from a | ||
| 74 | terminal, the agent is prompted for it; installed as a LaunchAgent, it may | ||
| 75 | never be. Grant it under System Settings → Privacy & Security → Local | ||
| 76 | Network. Without | ||
| 77 | it, guests boot and take a vmnet lease but stay unreachable through the | ||
| 78 | gate—`eitri ssh` fails with "cannot reach VM". | ||
| 79 | |||
| 72 | Keep the binary somewhere this account owns. The agent replaces its own binary | 80 | Keep the binary somewhere this account owns. The agent replaces its own binary |
| 73 | when it upgrades, so a root-owned prefix like `/usr/local/bin` would look tidy | 81 | when it upgrades, so a root-owned prefix like `/usr/local/bin` would look tidy |
| 74 | and quietly disable every future upgrade—the installer refuses rather than let | 82 | and quietly disable every future upgrade—the installer refuses rather than let |
docs/releases.md
| Old | New | ||
|---|---|---|---|
| @@ -10,34 +10,54 @@ running VM. | |||
| 10 | A PAT is enough. Point an LLM client anywhere on the internet at | 10 | A PAT is enough. Point an LLM client anywhere on the internet at |
| 11 | `https://api.eitri.sh/mcp` with a personal access token and it gets the whole | 11 | `https://api.eitri.sh/mcp` with a personal access token and it gets the whole |
| 12 | eitri toolset: create a VM, run commands in it, publish a port, hand back the | 12 | eitri toolset: create a VM, run commands in it, publish a port, hand back the |
| 13 | address. No local install, no config file, no CA of your own. | 13 | address. No local install, no config file, no CA of your own. Around it, the |
| 14 | 14 | console is drawn in the site's own language. | |
| 15 | **The MCP endpoint.** eitri-server serves MCP at `/mcp` over streamable HTTP, | 15 | |
| 16 | stateless, authenticated with the same bearer token the API takes. One binary, | 16 | **MCP.** eitri-server serves MCP at `/mcp` over streamable HTTP, authenticated |
| 17 | one deploy: the tools call the API in-process, so tenant filtering and | 17 | with the same bearer token the API takes; a caller sees exactly the fleet its |
| 18 | authorization are the API's own, and a caller sees exactly the fleet its token | 18 | token can see. `api.eitri.sh` carries that one route and nothing else. The |
| 19 | can see. The stdio binary is unchanged and still there for local use—both | 19 | stdio binary is there for local use—both transports serve the same tools. |
| 20 | transports serve the same tools, from the same code. | 20 | |
| 21 | 21 | **Delegated access, not a held key.** eitri never holds a signing key for your | |
| 22 | **Delegated access, not a held key.** A token holder has no key to sign with, | 22 | guests. When a token-only caller needs SSH access, you delegate it: sign the |
| 23 | and eitri has none to lend: it holds no signing key for anyone. So the direction | 23 | public key eitri shows you with your own CA, on your own TTL (`ssh-keygen -s`). |
| 24 | reverses. eitri generates an ephemeral keypair, in memory only, and hands you | 24 | Revoke it by letting it expire or by deleting it — eitri's access ends when you |
| 25 | the public half; you sign it with your own CA on your own TTL (`ssh-keygen -s`) | 25 | say so. Because the certificate chains to a CA you already registered, VMs |
| 26 | and hand the certificate back. eitri authenticates as that key plus that | 26 | created before you delegated accept it too. |
| 27 | certificate until it expires, and holds nothing else. A restart drops the | 27 | |
| 28 | delegation and you delegate again. Because the certificate chains to a CA you | 28 | **Your guests' host keys stay on your hosts.** A guest's SSH host key is born on |
| 29 | already registered, VMs created before you delegated accept it too. | 29 | the machine that runs it and never leaves it; the control plane signs only |
| 30 | 30 | public keys. The database now holds no private keys at all — keys stored by an | |
| 31 | **Your guests' host keys stay on your hosts.** A guest's SSH host key is | 31 | earlier release are dropped the first time the new server opens it. |
| 32 | generated by the machine that runs it and never leaves it. The host reports the | 32 | |
| 33 | public half, the control plane signs a certificate for the name on the VM's | 33 | `vm_create` reports what it is waiting for as it waits — usually seconds, |
| 34 | row, and the guest boots with a key nothing above its host has ever held. | 34 | minutes when a host downloads an image for the first time — so a long call reads |
| 35 | Opening the database clears every host key escrowed by an earlier release. | 35 | as a VM coming up rather than silence. |
| 36 | 36 | ||
| 37 | **Progress on the long call.** `vm_create` waits for the guest to boot and | 37 | **Upgrades read a host bundle.** A self-upgrading agent can now be handed the |
| 38 | cloud-init to settle, which can take minutes. It now reports what it is waiting | 38 | same tarball you would install by hand, verified against the release manifest |
| 39 | for as it waits, so a model watching a ten-minute call sees a VM coming up | 39 | as ever. `/dl` still carries bare binaries beside the bundles for agents from |
| 40 | rather than silence. | 40 | older releases. |
| 41 | |||
| 42 | **The console speaks the site's language.** We revamped the console: | ||
| 43 | monospace, hairline rules, light and dark mode—because we care. | ||
| 44 | |||
| 45 | **Known limits.** | ||
| 46 | |||
| 47 | - `/mcp` authenticates with a bearer token and nothing else. Browser | ||
| 48 | connectors that sign in with OAuth—claude.ai among them—cannot reach it. | ||
| 49 | - A delegation lives in memory. A control-plane restart or redeploy drops it | ||
| 50 | and you delegate again — one `ssh-keygen` against a public key that has not | ||
| 51 | changed. This is a property, not a bug: eitri's access to your guests ends | ||
| 52 | when its process does. | ||
| 53 | - An agent from an earlier release does not know how to generate a guest host | ||
| 54 | key, so a VM it creates against a v0.0.4 control plane boots with an | ||
| 55 | uncertified one and `eitri ssh` fails host verification with a clear error. | ||
| 56 | Upgrade the host's agent — from the console, as usual — and re-create the VM. | ||
| 57 | - Self-hosters: v0.0.4 adds a required `key_encryption_key` to `server.json` | ||
| 58 | (`openssl rand -hex 32`), and the server refuses to start without it. It | ||
| 59 | encrypts the fleet's own SSH CA and gate host key, so keep it outside your | ||
| 60 | database backups. | ||
| 41 | 61 | ||
| 42 | ## v0.0.3 | 62 | ## v0.0.3 |
| 43 | 63 | ||
docs/upgrade.md
| Old | New | ||
|---|---|---|---|
| @@ -22,13 +22,57 @@ If a download fails, nothing is swapped; click again to retry. To roll back by | |||
| 22 | hand, stop the agent, move `eitri-agent.prev` back over the binary, and start | 22 | hand, stop the agent, move `eitri-agent.prev` back over the binary, and start |
| 23 | it again. | 23 | it again. |
| 24 | 24 | ||
| 25 | **Requirements.** The button lights up only when the running agent reports a | 25 | **Requirements.** The button lights up only when the running agent's version |
| 26 | release version (`vX.Y.Z`) or a pre-release of one (`vX.Y.Z-pre.N`, which orders | 26 | orders before the published release. Release tags (`vX.Y.Z`), pre-releases |
| 27 | below the release it leads to)—agents built from an untagged or dirty tree | 27 | (`vX.Y.Z-pre.N`, which order below the release they lead to), and git-describe |
| 28 | report a git hash instead and are never offered upgrades. The server needs | 28 | builds (`vX.Y.Z-N-g<hex>`, N commits past the tag) all order, so a |
| 29 | `release_manifest_url` reachable; set it to `""` in the server config to | 29 | hand-deployed describe build behind the latest release is offered the upgrade, |
| 30 | and one ahead of it is never offered a downgrade. Anything else—`dev`, a | ||
| 31 | `-dirty` tree—orders before nothing and is never offered anything. The server | ||
| 32 | needs `release_manifest_url` reachable; set it to `""` in the server config to | ||
| 30 | disable upgrade checks entirely. | 33 | disable upgrade checks entirely. |
| 31 | 34 | ||
| 35 | ## Agents, by hand | ||
| 36 | |||
| 37 | An agent whose version never orders—a `dev` or `-dirty` build—never gets the | ||
| 38 | button. The manual path is the same swap, done by you. | ||
| 39 | |||
| 40 | On Linux, set `V` to the release you want (shown at | ||
| 41 | [/dl](https://eitri.sh/dl/)); on an arm64 host, `_arm64` replaces `_amd64` | ||
| 42 | throughout: | ||
| 43 | |||
| 44 | ```sh | ||
| 45 | V=v0.0.3 | ||
| 46 | curl -fsSLO "https://eitri.sh/dl/$V/eitri-agent_linux_amd64" | ||
| 47 | curl -fsSL "https://eitri.sh/dl/$V/SHA256SUMS" | grep ' eitri-agent_linux_amd64$' | sha256sum -c - | ||
| 48 | sudo cp /usr/local/bin/eitri-agent /usr/local/bin/eitri-agent.prev | ||
| 49 | sudo install -m 0755 eitri-agent_linux_amd64 /usr/local/bin/eitri-agent | ||
| 50 | sudo systemctl restart eitri-agent | ||
| 51 | ``` | ||
| 52 | |||
| 53 | Running VMs survive the restart—the unit's `KillMode=process` (see Operations | ||
| 54 | below) leaves the guests alone. The `.prev` copy mirrors what the button path | ||
| 55 | keeps, so rollback is the same either way. | ||
| 56 | |||
| 57 | On a Mac the binary lives in the running account's own space (e.g. | ||
| 58 | `~/.local/bin/eitri-agent`). Overwrite it with `eitri-agent` from the darwin | ||
| 59 | bundle, then restart the LaunchAgent: | ||
| 60 | |||
| 61 | ```sh | ||
| 62 | launchctl kickstart -k gui/$(id -u)/sh.eitri.agent | ||
| 63 | ``` | ||
| 64 | |||
| 65 | No `sudo` anywhere, same as the install. If guests turn unreachable through | ||
| 66 | the gate after the swap ("cannot reach VM"), check the agent's Local Network | ||
| 67 | permission—the quickstart's "Join a Mac" section covers it. | ||
| 68 | |||
| 69 | If the swapped-in agent reports it is not enrolled, it is running as a | ||
| 70 | different account than the one that joined: enrollment state is per-account | ||
| 71 | (`~/.eitri/agent` on macOS, `/var/lib/eitri-agent` on Linux), so under a new | ||
| 72 | account the binary is a new host. Join it fresh from the console, then | ||
| 73 | decommission the old host row it leaves behind. If that old agent is gone for | ||
| 74 | good, the delete needs force—the dead-hardware escape hatch on host delete. | ||
| 75 | |||
| 32 | ## The server | 76 | ## The server |
| 33 | 77 | ||
| 34 | The console banner links here when the published release differs from the | 78 | The console banner links here when the published release differs from the |
| @@ -43,6 +87,13 @@ reconciling and VMs keep running while it's away: | |||
| 43 | A newer server with older agents is safe: new fields in the sync protocol are | 87 | A newer server with older agents is safe: new fields in the sync protocol are |
| 44 | ignored by agents that predate them. | 88 | ignored by agents that predate them. |
| 45 | 89 | ||
| 90 | **One exception, from v0.0.4 on.** A guest's SSH host key is generated by the | ||
| 91 | host that runs it, and an agent from before v0.0.4 does not know how to do | ||
| 92 | that. A VM created on such a host boots with a host key nothing has certified, | ||
| 93 | and `eitri ssh` refuses it at host verification with a clear error. Existing | ||
| 94 | VMs are unaffected — they keep the host key and certificate they were built | ||
| 95 | with. Upgrade the host's agent, then re-create any VM created in the meantime. | ||
| 96 | |||
| 46 | ## cloud-hypervisor | 97 | ## cloud-hypervisor |
| 47 | 98 | ||
| 48 | Swap `/usr/local/bin/cloud-hypervisor` on the host. New and restarted VMs use | 99 | Swap `/usr/local/bin/cloud-hypervisor` on the host. New and restarted VMs use |