Each member has one server-owned home directory under <data>/homes/<member>.
Aether mounts it read-write as $HOME in the member's environment terminal and
every run using that member's agent account, including the run's shell tabs.
Those containers also start from that member's saved image, or the standard
image when none is saved. An explicit account share permits another member's
run to use both; see teams.md.
What persists
The home is the member's durable environment:
- Executables installed in
~/.local/bin - Vendor login state and other files written by the agent
- Configuration imported once from the browser or edited in Files
- The GitHub login written by
gh auth login, in~/.config/gh/hosts.yml - The commit signing key,
~/.ssh/aether_signingand~/.ssh/aether_signing.pub ~/.gitconfig, which carries the git identity, gh's credential helper, and the signing settings
Files outside the home live in the container layer. Save environment in the terminal turns that layer into your member image so later runs get it; see environments.md.
Setting up an agent
The local dashboard (aether gui) and CLI list both the agents Aether ships
and the ones members define. Its launch form and onboarding wizard's First run
step only offer agents whose executable is installed in the selected account's
~/.local/bin, and with none installed they say so and offer Set up an
agent rather than a launch the server would refuse; the Agents page still
lists uninstalled shipped agents so you can set them up. The server-hosted
dashboard has no onboarding wizard.
Discovery follows relative symlinks and absolute links under /root or
/home/aether within that account's home. Claude's native installer uses an
absolute link to its versioned executable. Broken links, links outside the
home, and files without executable permission are not marked installed.
Use Refresh agents after installing in an open terminal; no app or server
restart is needed.
Choose an agent once:
aether agent add <name>
The command tells you what to run. Open the environment terminal,
install the agent into ~/.local/bin, and complete the vendor login there:
aether terminal
After setup, every run using that account sees the same executable and login state. A member-defined agent also records its launch arguments for later runs. The terminal command ships in this release series.
Connect GitHub
Connect GitHub once and every run using that account can push branches to the workspace's upstream repository, open pull requests, and sign its commits. Because the home is shared read-write, active runs can see the files too after their agent reloads. Both halves happen in the member home, so no run needs its own credentials.
The login runs in your environment terminal, so that container needs
gh 2.81.0 or newer - the release that added gh auth status --json,
which is how the server reads your login back. The standard image ships a
current one. An environment saved, or a standard image pulled, before the
image started shipping gh has none; see No gh in the
environment below.
Open the environment terminal and log in there:
aether terminal
gh auth login --hostname github.com --git-protocol https --web \
--scopes admin:ssh_signing_key
--web is GitHub's device flow: gh prints a one-time code and a URL, and
you finish in a browser on your own machine. Nothing is forwarded and no
password reaches the server. gh asks you to press Enter to open the browser,
then reports that it could not open one; that is expected inside a
container: press Enter, ignore the failure, and open the printed URL
yourself with the one-time code. --scopes admin:ssh_signing_key is on top
of gh's own defaults, and it is what lets the next command register your
signing key on your account without you pasting it into GitHub by hand.
Then, from your machine:
aether github connect
logged in to github.com as octocat
signing key SHA256:2E3v9x... registered on GitHub
That one command does five things, all on the server:
Checks
gh auth statusforgithub.cominside your terminal container to confirm the login took.Runs
gh auth setup-git --hostname github.comthere, which writes gh's credential helper into~/.gitconfigsogit pushover HTTPS authenticates as you.Generates an ed25519 key pair at
~/.ssh/aether_signingand~/.ssh/aether_signing.pubif there is not one already, and rewrites the.pubfrom the private key every time, so the file it registers always belongs to the key it signs with.Writes five keys into
~/.gitconfig:user.name,user.email,gpg.format=ssh,user.signingkey=~/.ssh/aether_signing, andcommit.gpgsign=true.Registers the public key on your account with
gh ssh-key add --type signing, then reads the account's keys back withgh ssh-key listand refuses unless the fingerprint it computed is among them:github: the signing key registered on the account does not match this member's key (fingerprint SHA256:2E3v9x... not listed)
Re-running it is safe. An existing key is reused rather than replaced, and GitHub accepts a key it already holds without an error.
Seven failures have their own messages. An environment whose gh cannot do the login is refused before the login is asked about at all, naming the way out and ending with what gh - or the container that could not run it - actually printed. With no gh at all, on the server's standard image:
github: gh is not on PATH in the environment terminal; ask a server admin to run aether server update, then run aether terminal stop and open the terminal again: OCI runtime exec failed: exec failed: unable to start container process: exec: "gh": executable file not found in $PATH
Both halves are there because a newer image on the server reaches you only when you open the terminal again (environments.md). Which command the admin gets depends on the server's image; see No gh in the environment below.
With a gh too old to answer the check - Ubuntu 24.04 packages 2.45.0 - in an environment you saved yourself, the way out is yours alone:
github: the gh in the environment terminal is too old to check the login: 2.81.0 is the oldest gh that answers auth status --json; install a current gh there and save the environment again, or run aether env reset to remove the saved image and return to the standard one: gh version 2.45.0 (2025-07-18 Ubuntu 2.45.0-1ubuntu0.3)
When the image you should be on has already moved - after a reset, or a
server update - the container is the only thing left behind, and the
refusal asks for nothing but aether terminal stop and a reopen. A gh that
is on PATH but exits non-zero is refused the same way, as gh in the environment terminal would not run, ending with what it printed. The
dashboard's GitHub step checks all of this before it prints the login
command, so it shows the remedy instead of a login that container cannot
run.
Without a terminal login:
github: not logged in to github.com in the environment terminal; run gh auth login there first: HTTP 401: Bad credentials
The line ends with gh's own reason for that account - HTTP 401: Bad credentials when the token was revoked or expired - or with gh's whole
output when it reported no account at all. A gh that fails the check
outright, rather than reporting on an account, is not a missing login and
does not read as one: gh auth status exited <code> ends with what it
printed.
A login made without --scopes admin:ssh_signing_key is refused before
anything in the home changes: no key is generated and no .gitconfig is
rewritten.
github: the gh login on github.com lacks the admin:ssh_signing_key scope; run gh auth refresh -h github.com -s admin:ssh_signing_key in the environment terminal
When the server host has no ssh-keygen, the command refuses before
generating anything, because that binary is what signs Aether's own
commits; install the OpenSSH client package on the server
(install.md).
aether member git keeps ~/.gitconfig in step: once a signing key
exists, changing your name or address rewrites the identity in the home's
.gitconfig too, so the signature and the author stay the same person.
Container root in every run using this account can read and replace the credentials and the key. Read security.md before connecting an account whose reach is wider than this workspace.
No gh in the environment
The standard image has shipped gh since the v0.2.0-alpha.6 release. Three
kinds of environment can still be without a usable one.
A saved environment committed before then keeps whatever was installed when it was saved. Install gh in the terminal and save again, or drop back to the standard image:
aether env reset
A standard image is the server's, not yours, so only an admin can move it. Each release publishes its own tag and the server defaults to the tag matching its own build, so a server still on an image without gh is a server on a release without gh, and the answer is a server update (install.md):
aether server update
That assumes the server takes the default; a pinned or rebuilt
--standard-image is refreshed some other way
(environments.md). Aether picks the
command from the image the server is configured with, so the one to run is
the one in the refusal.
Either way the new image is not yours until you open the terminal again:
aether terminal stop
A gh you installed yourself into your environment home is the third,
and no image remedy reaches it: ~/.local/bin comes first on PATH and
the home is kept across every image. Aether names the file it found when
that is what answered, so the way out is to remove it and let the image's
own gh take over, or to replace it with 2.81.0 or newer:
rm ~/.local/bin/gh
Importing and editing configuration
In the local dashboard (aether gui), choose one local directory with the
browser's directory picker and explicitly import it once. The server-hosted
dashboard cannot read a directory on your laptop; use aether gui for this
step. The picker compares the selected directory basename with the roots in
config.roots: a known unique basename is selected automatically, while an
unknown or ambiguous basename requires an explicit destination. The browser
waits for that metadata and destination before previewing or reading bytes.
Changing the destination clears the old preview and re-reads the retained
local file handles with that destination's policy; stale reads are discarded.
There is no directory watcher and no AI-generated inventory.
Known credential names in any path component and *.pem files are always
skipped locally. Runtime/history paths come from the selected root's
runtime_ignores metadata and are skipped with exact, case-sensitive
root-relative component-prefix matching. Remaining bytes are uploaded and
scanned by the server, so secret content is not guaranteed to stay on the
browser machine. Empty files and arbitrary binary assets are preserved. The
limits are 1 MiB per file, 20 MiB decoded total, and 2,000 files.
New browser-imported files use mode 0644; existing files retain their current
modes, including restrictive server-side umask modes. Executable mode and
symlinks cannot be represented by the browser. Server-side validation rejects
unsafe paths, symlink components, hardlinks, and nonregular files, while
preserving directory and staged-file ownership.
The import writes the authenticated member's own persistent home. Because that home is mounted read-write in the environment terminal and in every run using the account, imported or edited files are visible immediately, including to active runs; the agent may need to reload. An account share intentionally gives another member's run the same home, not an isolated per-run profile. A snapshot pin is audit metadata, not a private writable copy, and changing configuration does not rebuild an installed-agent image.
Use Files to browse your configuration beside workspace base and live-run
files. Configuration edits are complete UTF-8 text up to 64 MiB; binary and
oversized files are read-only. New configuration files accept nested relative
paths and refuse to overwrite an existing file. Save explicitly with Save or
Ctrl/Cmd-S. Dirty tabs remain in memory across routes, and the browser warns
before unloading them. There is no autosave or force-save. A failed or stale
save keeps the draft; Reload from server deliberately discards it.
All config.* methods require Launch and target only the authenticated
member's own home; there is no admin/member selector.
For the separate workspace explorer, base-branch saves are Commit to
Migration
On upgrade, legacy content in
<data>/homes/<member>/<harness>/<home-relative-path> is moved to
<data>/homes/<member>/<home-relative-path> for known harness names. Empty
harness directories are removed. Existing files win if two paths conflict.
Old per-workspace executable snapshots are removed. The migration is
idempotent and does not move files outside the member home root.


