This checkout requires compute-api-version: 2 and resolves colors-compute
from its published SHA in deps.edn. No sibling checkout is required. Do not use the v1
instructions below to create or upgrade a deployment with this checkout.
Use the current repository README and AGENTS.md. green ssh opens a scoped
agent and connects to the owned machine's live public address without reading
~/.ssh/config. It requires backend and provider credentials plus OpenTofu;
known-host checking remains enabled. Focused convergence still uses managed
aliases. start and stop refuse. Backend buckets must already exist. Encrypted
SSH authority survives compute deletion and requires the runtime binding
COLORS_PAR_WALTER_SSH_PASSPHRASE for create and access.
green ssh-install explicitly exports the existing encrypted OpenSSH keypair
under ~/.ssh/walter/<profile>/ and creates primary and seat SSH aliases using
the live address. Ordinary SSH prompts for the key passphrase and does not cache
it in an agent or Keychain. Rerun to refresh the address and seats. Create
preserves this installed identity. green ssh-uninstall removes the owned local
aliases and export without cloud credentials; remote authority is retained.
Neither command writes a decrypted private key. Delete removes aliases and
retains the exported key until explicit uninstall.
The following instructions document the historical v1 release.
A remote development machine, with WalterUse this skill to initialize or operate a walter project in the user's current
directory. Walter provisions one machine, records it in ~/.ssh/config so
ssh <profile> reaches it, and powers it off and on.
Requirements
Babashka runs the launcher. create and delete also need OpenTofu and
Ansible. stop and start need the provider's power credentials.
converge-nix and converge-asdf need an already-created, running machine and
its managed SSH aliases, but no provider or backend credentials. With
github-account set, a real create also needs gh on the workstation — it
runs GitHub's device flow as its first action. Provider credentials use
COLORS_PAR_* variables, except OCI, which uses the profile named in
~/.oci/config, and S3, which uses OpenTofu's ambient AWS credential chain.
Non-negotiable safety rules
- Never ask the user to paste a secret into chat.
- Never put API tokens, passwords, private keys or access keys in
colors.yml, in thegreenlauncher, in shell history, or in generated examples. Every credential arrives through aCOLORS_PAR_*environment variable named after the key it fills. Suggest a gitignored.envrc.private, never an inline export a shell history records. - Public SSH keys are not secrets; private ones are.
oci-ssh-authorized-keysholds the path to a public-key file that OpenTofu reads at plan time — record the path, never inline the contents, and never read a private key. By default walter generates and manages the machine keypair~/.ssh/<profile>; never read or move those files either. - With
github-accountset, a realcreatestarts by printing a one-time code and waiting — up to about fifteen minutes — for the user to approve it at https://github.com/login/device. That is the design, not a hang: the workflow is interactive at the beginning only. Relay the code and URL to the user and wait; never try to acquire, read, or echo the token itself. - Never set
COLORS_PAR_PROFILE. Walter refuses to run when it is set, and suggesting it as a workaround defeats the guard. The profile identifies the project, and the project is the directory. If the user wants a different profile, editcolors.yml. - Do not overwrite an existing
greenlauncher orcolors.ymlwithout explicit approval. If a project is already valid, operate it rather than regenerating it. - Default to
buildandcreate --dry-run. Run a realcreateordeleteonly after the user confirms that exact operation. buildandcreate --dry-runare credential-free by design and check noCOLORS_PAR_*at all. A clean dry-run says nothing about whether real provisioning would authenticate; never report it as credential validation.- Before
delete, remind the user that a development machine holds uncommitted work and that the boot volume goes with it.compute-prevent-destroydefaults totrue; authorize an intentional delete withCOLORS_PAR_COMPUTE_PREVENT_DESTROY=falserather than editing desired state. - Never edit anything under
.colors/— it is generated output.
Read references/configuration.md before
generating or changing desired state, and before any real create or delete.
Commands
./green build # render .colors/<profile>/ only; contacts nothing
./green create --dry-run # print the graph; touches nothing
./green create # provision, and write the ssh config block
./green stop # power off
./green start # power on, and refresh the ssh config block
./green converge-nix # update declared Nix entries on every login
./green converge-asdf # install declared asdf versions on every login
./green delete # destroy, dropping the ssh block first
-f/--file overrides the colors.yml found by walking up from the working
directory.
Initialize in the current directory
- Copy the
greenpayload beside this file into the project root andchmod +xit. - Write
colors.yml. Ask for the provider first, then only the keys that provider needs —references/configuration.mdlists them. - Choose a
profileunique to this project, conventionally the directory name. It names the work directory, the OpenTofu state keys and the ssh alias. Two projects sharing a profile and a state bucket address the same state, which is how a development machine ends up managing a production server. - Ask whether the machine should have the user's GitHub identity. If so, set
github-accountto their login andgit-emailto their commit email, and tell them a realcreatewill start with a one-time device-flow code to approve from a browser. This is required before offeringemacs-config-repo,clone-orgsordotfiles-checkout— their clones authenticate through it. - The machine-access keypair needs no collection: walter generates a profile-named keypair by default (SSH Keypair Standard). Only when the user wants their own key, collect the provider's machine-key value.
- Ask whether the user wants their Emacs configuration on the machine. If so,
set
emacs-config-repoto its https git URL (git@/ssh://forms are refused) andemacs-config-destto where it must live — the default is~/.config/emacs, and a configuration expecting another path needs--init-directoryto reach it. Leave both out otherwise; the rendered playbook then does not mention Emacs. - Run
./green buildand show the user what was rendered.
What create puts on the machine
Every machine gets nix, a Ghostty terminfo entry, and kernel networking
settings for unprivileged cloudflared, unconditionally. The sysctls allow the
login user's primary group to use ping sockets and raise QUIC's receive/send
buffer ceilings, so a tunnel should run without sudo or those warnings. Tell
the user about nix rather than proposing walter changes for other tooling: once
it is there, anything else is nix profile install and needs nothing from
walter.
The terminfo is why Terminal type xterm-ghostty is not defined does not
happen. If a user reports that error — from vim, top, less or Emacs — on a
machine created before this existed, the fix is to re-run create, not to
change TERM. For a terminal walter does not cover, the one-liner is:
infocmp -x "$TERM" | ssh <alias> -- tic -x -
With github-account set, create also logs the machine's own gh in with the
token the device flow minted, makes it git's https credential helper, and
configures the commit identity — every clone below authenticates through it,
and nothing of the workstation's (no key, no agent) is involved. A machine
already logged in skips the interactive step entirely, so re-creates stay
unattended.
With emacs-config-repo set, create also installs Emacs (a full build from a
pinned nixpkgs) and clones the configuration over https with the machine's own
token — no private key is written to the machine, and the checkout can push
back. The clone happens once; a later create leaves an existing one alone,
so work done on the machine is never discarded. Offer git pull on the machine
rather than a re-run when the user wants the config refreshed.
Emacs packages are not pre-fetched. The first emacs launch fetches from
ELPA/MELPA, native-compiles and clones tree-sitter grammars, which takes minutes
and is expected. Do not report it as a provisioning failure.
nix and emacs reach PATH via /etc/profile.d/nix.sh, a login shell
mechanism: ssh walter-oci sees them, ssh walter-oci emacs … as a one-shot
command does not.
Focused tooling convergence
converge-nix and converge-asdf operate only on an existing, running machine.
They use ssh <profile> and every ssh <profile>-<seat> alias, never OpenTofu
state or a provider API. If an alias or prerequisite binary is absent, run
create; if the machine is stopped, run start.
converge-nix ensures every nix-packages entry exists, resolves the profile
elements Walter owns from nix profile list --json, and advances only stale
declared elements to the current nixpkgs-unstable. It preserves unrelated
packages installed by the user and does not remove a former declaration.
converge-asdf adds missing plugins, installs the exact asdf-tools versions,
sets them for the home, and repeats Corepack enable/reshim so a Node change does
not make pnpm disappear. It never chooses latest on its own.
Both commands support --dry-run, and every task is safe to run again.
converge-asdf reports no change once its exact versions are converged.
converge-nix reports a change when Nix advances an element; that flag follows
Nix's current upgrading wording, so an upstream rewording can misreport the
flag without changing what is installed.
Stopping and starting
The colors-compute library coordinates OCI and Vultr power operations using the immutable ID from owned remote node state. Both need a reachable state backend; unsupported providers refuse. An uncertain action retains ownership coordination for recovery. Start waits for a running state, reads the current public address, then refreshes SSH aliases. It does not recreate the VM.
OCI uses its configured CLI profile; Vultr uses COLORS_PAR_VULTR_API_KEY. Power billing depends on the provider and storage remains allocated.
When something fails
COLORS_PAR_PROFILE is set— the user has it exported, probably from another project's.envrc. Unset it; do not work around it.required credential is not set: COLORS_PAR_X— name the variable and let the user export it themselves.- Compute ownership or power refused — inspect backend connectivity and the owned deployment journal. Do not set an instance-ID override or treat an unreadable state as absent. Legacy monolithic state requires explicit migration.
gh auth login failed—ghis missing on the workstation, or the one-time code expired unapproved. Install gh or re-runcreateand approve the code; there is no token to paste anywhere.- A create failed after the code was approved — just re-run it. The minted
token survives under
~/.local/state/walter/github-token-<profile>for exactly this, so the retry does not prompt again; it is removed once a create seeds the machine. - the login approved the code as X but colors.yml names github-account Y —
the user approved from the wrong GitHub account. Re-run
createand approve from the account the machine is meant to act as, or fixgithub-account. - A contract mismatch — the pinned commit is older than this launcher.
Re-copy
greenfrom an updated skill; nothing inside the project fixes it. - Focused convergence reports that a host key changed — unlike create,
converge-nixandconverge-asdfdeliberately keep SSH host-key checking enabled. For the narrow same-address/different-key case after a rebuild, runssh-keygen -R <address-from-the-error>and retry. Use the address SSH names, not the managed alias; every seat shares that address. Hashedknown_hostsentries cannot be repaired reliably by eye.