Upgrading from v0.5.4
- Rerun the installer to install both
coop
and the newcoop-proxycompanion in the same directory. The v0.5.4 updater
replaces onlycoop, so its first upgrade does not install the companion
required for proxy mode. Preserve yourINSTALL_DIRif you used a custom
installation directory. - The published v0.5.4 Linux ARM64 binary reports
coop 0.5.4-dev (8e24729+dirty)and refuses self-update as a development
build. Use the installer to upgrade it. - Restart all running Linux VMs after upgrading to apply guest-to-guest
network isolation. Every VM on the shared bridge needs the restart; a
pre-upgrade VM leaves peers reachable. macOS is unaffected. - Save guest-only work before
restore --reprovision: it replaces the guest
disk, including any guest keyring and cached account login. Rebuilding an
image alone does not update existing VM disks.
New features
-
Codex ChatGPT account auth — New
[codex] auth = "chatgpt"mode for
account/workspace access without OpenAI API billing. coop installs Linux
Secret Service support in the guest image, writes Codex's
cli_auth_credentials_store = "keyring"setting, launches Codex through a
D-Bus/GNOME Keyring wrapper (/usr/local/bin/codex-account, which the
in-guestcodex-yoloshortcut also uses and which execs Codex unchanged when
the mode is off), suppresses everyOPENAI_API_KEYforwarding path, and stops
copying hostauth.jsoninto the guest. Sign in withcoop codex -- login --device-auth;loginandlogoutnow run without the sandbox-bypass flag,
which is meaningless on subcommands that never start an agent session.
Existing images must be rebuilt withcoop setup --rebuildbefore using this
mode. A restart reuses the old guest disk, so an existing VM also needs
coop restore <vm> --image <image> --reprovision(or a destroy and
recreate) to pick up the new guest packages. -
coop restore --reprovision— start over without re-typing anything
(#432) — Replaces a clobbered or bloated guest filesystem with a fresh copy of
the instance's image, then provisions it as a first boot:/workspaceis
restored from the source the instance recorded — re-synced, re-cloned or
re-mounted — agents are re-bootstrapped, and plugins, marketplaces and MCP
servers are reinstalled. The instance is left running, so no follow-up
coop startis needed. It keeps its name, index, IP, image, disk size, port
forwards and guest env — including a devcontainer'scontainerEnvand
forwardPorts— so those flags do not have to be remembered. GitHub PATs and
provider credentials live in the host-side secret store and are untouched.
Extra--extra-mountdirectories,--exclude-gitand a devcontainer's
postStartCommandare not replayed, because coop does not persist them.This is what
coop restore+coop startcould not do: a restart skips the
workspace sync and the plugin install on the assumption that both survived on
the guest disk — which holds for acoop commitcheckpoint, but not after the
disk is replaced with a base image. Plainrestore+startremains the
checkpoint rollback;--reprovisionis the base-image case.--imagebecomes optional under--reprovision, defaulting to the image the
instance already records; it stays required otherwise.-yskips the
confirmation and is required off a TTY.-y,--no-agentsand--no-prompt
are rejected without--reprovision, since none of them mean anything to a
plain disk swap. Unlike a plainrestore, which requires a stopped instance,
--reprovisionalso accepts a running one and stops it itself.coop restore
without--reprovisionis unchanged. -
Credential-injecting proxy — keep the model API keys out of the guest
(#411) — New opt-in[proxy]config. When set, coop runs a small host-side
reverse proxy (coop-proxy, a new binary shipped in the same tarball) — one
process per (VM, provider) — for the lifetime of a remote-mode VM: the guest
is pointed at it and holds only a per-instance capability token, while the
real credential stays on the host and is injected onto requests upstream.- Claude Code (
[proxy.anthropic]) is pointed at the proxy via
ANTHROPIC_BASE_URL; supports an API key (x-api-key) or a Claude
setup-token(Authorization: Bearer). - Codex (
[proxy.openai]) is pointed at the proxy via a
[model_providers.coop_local]block (Responses API) with the capability
token as the provider bearer; OpenAI API keys inject as
Authorization: Bearer. In proxy mode Codex's~/.codex/auth.jsonis no
longer staged onto the guest disk; Codex subscription is out of scope — use
an API key.
The raw
ANTHROPIC_API_KEY/OPENAI_API_KEYis no longer forwarded into the
guest, so a prompt-injected or rogue agent cannot read a usable key.
Resolution fails closed — a bad credential aborts the boot rather than booting
without injection.coop model <vm> localtakes precedence and tears the
proxy down. The proxy binds host loopback and is reverse-tunnelled (ssh -R)
into the guest, so it works on both backends (Firecracker and Lima). GitHub
is a tracked follow-up.coop updatekeepscoopandcoop-proxyin
lockstep. - Claude Code (
-
The credential proxy is jailed (#411) — the host-side
coop-proxy
process runs confined so a proxy exploit cannot write files or execute
programs, and — on a new enough kernel — cannot reach any host beyond the
upstream:443and DNS:53. On Linux it self-applies Landlock before
serving, tiered by kernel capability: filesystem-write and program-exec are
always denied (kernel ≥5.13) and fail-closed — the VM start aborts rather
than running the proxy without that floor; TCP egress is port-scoped only on
kernel ≥6.7, and on kernels 5.13–6.6 (which have no Landlock network rules)
that scoping is dropped and egress is left open. On macOS the launcher wraps
it insandbox-execwith a Seatbelt profile. The jail is port-scoped, not
host-scoped (upstream identity is enforced by the proxy's TLS verification),
and does not restrict UDP on Linux; see
docs/trust-model.md. -
Per-VM credential overrides +
coop proxy status(#411) — A single VM can
use a different credential than the[proxy.<provider>]default — for
per-project billing, scope, or revocation — viacoop proxy setup [--openai] --vm <name>. The override is stored in that instance's state
(<inst.dir>/proxy.json), not a growing config table, and its secret is
namespaced separately. Resolution is override → default → off.coop proxy status [--vm <name>]shows what each VM resolves to, with credentials
redacted. -
coop proxy setup— store a provider credential like a GitHub PAT (#411)
— Mirrors thecoop githubPAT wizard: paste a credential, pick a secret
backend (macOS Keychain / Linux secret-service / 1Password / 0600 file), and
coop stores it and writes thecmd:reference into[proxy.<provider>]— the
credential is never plaintext in the config. Anthropic is the default;
--openaiconfigures Codex. The secret store is namespaced per service, so
proxy secrets live under their own directory rather than among the GitHub PATs. -
coop editor— VS Code or Zed —coop vscodeis nowcoop editor
(the old name remains as an alias).--editortakescodeorzed;
when omitted, coop tries VS Code first, then Zed. Zed connects with
zed ssh://coop-<name>/<path>, reusing the same~/.ssh/configalias
block that VS Code's Remote-SSH uses. -
coop up --new-instance— a second instance for the same project —up
normally reuses the instance recorded for a project directory or--git-repo
URL.--new-instanceskips that lookup and creates a sibling instance
instead, so two agents can work from one source tree. It requires--name,
since the project-derived name is already taken. Once a project has siblings,
coop upreports the ambiguity rather than picking one, so address them by
name (coop start <name>,coop shell <name>).
Fixes
-
Firecracker guests can no longer reach each other by IP — Instances on the
same host share thebr0bridge and a single subnet, so any guest could reach
any other guest's SSH and forwarded ports. coop now marks every TAP as an
isolated bridge port and inserts aFORWARD -i br0 -o br0 -j DROPrule; both are
needed, since the bridge flag alone leaves a guest able to route around it via
the host's bridge address. Guest→host and guest→internet are unaffected. A
host that cannot apply the flag fails the VM start instead of booting an
unisolated guest; this needs Linux ≥ 4.18 and iproute2 ≥ 4.19. Restart any running VMs after
upgrading — isolation applies per start, and because the kernel requires
both ports to be isolated, one pre-upgrade VM leaves the whole bridge
unisolated. This blocks reachability, not impersonation; see
docs/trust-model.mdfor the residuals. macOS is
unaffected — Lima gives each guest its own user-mode NAT. -
Guest transports fail instead of hanging when a VM stops responding — A
paused VM, a wedged sshd, or a lost TAP device leftssh,scp, andrsync
calls blocked on a dead socket with no deadline, so lifecycle commands,
coop exec, andcoop push/pullhung until interrupted. Every transport
now derives from one option list that setsBatchMode, a connect timeout,
and a liveness probe, so a guest whose sshd stops answering fails after ~90s
— the bound interactive sessions already had. -
The guest hostname resolves, so
sudostops warning — Instance creation
renamed the Firecracker guest toclaude-<name>in/etc/hostnamebut left
the image's127.0.1.1 claude-vmentry in/etc/hosts, so everysudoin
the guest printedsudo: unable to resolve host claude-<name>before running.
Both files are now written together at create and restore, and the guest
hostname is clamped to fit the kernel's 64-byte hostname limit so long
instance names still get a resolvable name. No image rebuild is needed — the patch is
per-instance, and the image's own entry is what gets overwritten — but
patch_guest_networkruns only on create and restore, so an existing VM
keeps the stale entry untilcoop restore <vm> --image <image>or a destroy
and recreate. -
Fail closed on an unmanaged
CODEX_HOMEin ChatGPT auth mode (#441) —
The guest wrapper now refuses an explicitly setCODEX_HOMEwhen coop's
managed~/.codex/config.tomlselects keyring storage. This preventscodex loginfrom silently writing a plaintext refresh token to the alternate
directory, including a workspace path that syncs back to the host. -
Install Codex's complete runtime package (#442) — Recent Codex releases
use a companioncodex-code-mode-hostexecutable, but coop installed only
the rawcodexbinary, causing Code Mode to fail closed at startup. Image
builds andcoop agent update --codexnow verify and install the upstream
package with its host and runtime resources intact, root-owned and behind a
shared current-release link. -
install.shandcoop updateverify provenance without a GitHub
credential (#421) — Verification rangh attestation verify --repo trailofbits/coop, which reads the Sigstore bundle from the attestations
API.ghrefuses to run that command unless it is logged in, and then
attaches its stored token, so a token with no SSO session for the
trailofbitsorg gotHTTP 403and the install was refused; the same path
failed outright for anyone withghinstalled but not authenticated.
Releases now publish the provenance bundle as anattestations.jsonlasset,
and both clients fetch it with an unauthenticated request and verify against
it with--bundle, which makes no attestations-API call and needs no
credential. Releases published before the asset existed are still verified
through the API path, unchanged.