envend
guide

The CLI

envend is the one thing you install on the machine you work from. It is a single static binary and it is the whole client: the tunnel, the mount, ssh, and every command that changes a sandbox. Every command takes a sandbox's name — never a repository's, because several sandboxes of one repository run at once, which is the point of them.

Install

cd cli && go build -o envend .

Or take a binary from a release: darwin and linux, amd64 and arm64, built on an envend-v* tag.

Point it at a gateway

envend login https://gateway.envend.dev --token-stdin

The token is read from standard input, never from argv, so it does not land in your shell history or in another process's view of the process table. It is written to ~/.envend/config at mode 0600. ENVEND_GATEWAY and ENVEND_TOKEN override the file when they are set — a config file is a standing preference and an exported variable is a decision about this shell.

Every command

CommandWhat it does
envend login <gateway> --token-stdinSave the gateway and your token.
envend up <profile> [--detach]Create a sandbox from a profile. Prints the generated name on stdout, then follows the setup hooks on stderr and exits 0 when the workspace is ready, 1 when a hook failed. --detach returns straight after the name.
envend listEvery sandbox, with state, idle time, what its backend keeps, and whether anything will ever stop it.
envend status <name>Three lines: whether your tunnel is running and on which local port, whether /work is mounted and where, and the gateway's own row for that sandbox.
envend logs <name> [--since N] [--no-follow]The setup hooks' own output, live, ending when the workspace is ready or failed. --since 0 is the whole disk's history, including boots before this one.
envend mount <name>Put its /work at ~/envend/<name>/work. See below.
envend unmount <name>Take it away again, and stop the tunnel.
envend ssh <name> [--host] [-- <cmd>]A shell, or one command. Without --host it goes through the workspace's exec prefix; with it, to the VM. Everything after -- belongs to the command, so envend ssh dev -- grep --host x sends that --host to grep.
envend instructions <name>Print the block a harness needs. envend mount already writes it beside the files.
envend share <name> <port>Serve one port on a public HTTPS URL.
envend unshare <name> <port>Stop serving it. Instant, and total.
envend link <name> <path> [--ttl <s>]A signed, expiring URL for one file under /work.
envend revoke-links <name>Invalidate every link ever issued for that sandbox, at once.
envend hold <name> [--reason <text>]Keep it awake while a long detached job runs. Holds always expire.
envend release <name>Hand it back to the idle sweep.
envend sleep <name>Stop it now. The next command wakes it.
envend destroy <name>Delete it, and its /work, for good.
envend tunnel <name>The mount's tunnel, in the foreground. envend mount runs this for you.
envend proxy <name>ssh's ProxyCommand. The ssh config block runs this for you.

What envend mount actually does

Five things, in one command, because each is useless without the others:

  1. Looks up the sandbox's tunnel address from the gateway and remembers it.
  2. Fetches the sandbox's two keys — the ssh private key and the tunnel key — into ~/.envend/<name>/.
  3. Starts a background tunnel listening on a local port the kernel picks.
  4. Mounts /work over NFSv3 through that tunnel, at ~/envend/<name>/work.
  5. Writes a Host <name> block into ~/.envend/ssh_config, and adds a single Include line to ~/.ssh/config if it is not already there — so plain ssh <name> and scp work.

It wakes a sleeping sandbox on the way, and it waits — printing the hook log — if the workspace is still coming up. The mount host is always 127.0.0.1: nothing dials the sandbox directly, because it publishes no NFS port, and the tunnel on that local port is what proves who you are before the file server sees a packet.

macOS mounts without sudo. macOS lets a user mount NFS onto a directory they own, which matters because a harness cannot answer a password prompt. On Linux the kernel NFS client needs CAP_SYS_ADMIN, so envend mount has to run as root; envend does not escalate on your behalf, and says so in one sentence if it cannot mount.

Two things the mount does not do

  • Locks are local only. A lock taken on the mount is enforced against other programs on your machine and is invisible to everybody else. For one person editing their own sandbox this changes nothing; for two people on one share it is a real gap.
  • Attribute caching is the kernel's. A file changed in the sandbox over ssh can look stale on the mount for up to sixty seconds.

Waiting on a sandbox from a script

envend up puts the name and the other fact lines on stdout and the hook log on stderr, so reading one does not mean parsing the other:

name=$(envend up omega | head -1) && envend ssh "$name" -- mix test

Waiting is bounded at twenty minutes and says so, and the log stream is resumable, so the cost of hitting that bound is one more command. envend ssh --host never waits at all — it is about the machine rather than about the work, and it has to stay open when the workspace is what is stuck.

Where things live

~/.envend/configGateway URL and token, mode 0600.
~/.envend/<name>/One sandbox's two keys and its running tunnel's port.
~/.envend/ssh_configA Host block per mounted sandbox, included from ~/.ssh/config.
~/envend/<name>/workThe mount point. Under your home rather than /mnt, so creating it needs no privilege.
~/envend/<name>/ENVEND.mdThe instruction block, written beside the files.