One server, several people, everyone's agents side by side. Nothing here is a separate mode: a solo setup is a team of one, and turning it into a team is adding members.
Prerequisite: the server is up and someone has linked to it, which made that person the admin. See quickstart.md.
Joining
Over a tailnet (primary)
The teammate joins your tailnet, then:
aether link my-server
That is the whole procedure. Tailscale proves who they are, the server registers them as a pending collaborator, and an admin approves:
aether member list
ID NAME ROLE PENDING
01m04mfxyf0rhwegab83btsz2y admin admin
01m04mqes1z7wsdk4s90tx0pgg dana collaborator pending
aether member approve 01m04mqes1z7wsdk4s90tx0pgg
Until then their commands fail with membership pending admin approval.
Approval is per person, once. --tailnet-auto-join on the server drops the
approval step entirely, for teams whose tailnet already is the team.
Full detail on tailnet identity, tagged nodes, and revocation is in networking.md.
By invite code (fallback)
For people connecting from outside a tailnet. An admin mints a one-time code:
aether invite --ttl 3600 # seconds; default 86400
The teammate redeems it, which registers their SSH key and burns the code:
aether link my-server:2222 --invite <code> --name "Dana"
linked to my-server:2222 as Dana (collaborator)
Invited members are collaborators immediately - no approval step, because the code was the approval. Nobody needs shell access to the server box to join.
Roles
Three capabilities on runs - view, steer (attach-write, inject,
approve, pause), kill - plus push to the workspace repo and
workspace_admin, bundled into roles:
| Role | Own runs | Others' runs | Workspace |
|---|---|---|---|
| viewer | - | view | read the feed |
| collaborator (default) | everything | view, steer, kill | launch runs, push, use templates |
| admin | everything | everything | members, workspaces, budgets, templates, settings, server self-update |
The viewer row is a real choice, not a placeholder: aether member role <id> viewer assigns it. It is for the person who should watch the work and read the
feed without being able to start, steer, or kill anything.
Everyone is a collaborator by default, on purpose. Teammates can steer each other's agents with zero setup; every privileged act is attributed in the workspace timeline instead of being prevented.
The first identity to link a fresh server becomes the admin and everyone who joins afterwards is a collaborator, but an admin can change that:
aether member role 01m04mqes1z7wsdk4s90tx0pgg viewer
set 01m04mqes1z7wsdk4s90tx0pgg dana to viewer
member list, member approve, member color, member git, member role
and member remove are the member surface. Two rules hold no matter what you
type. The last admin can neither be removed nor demoted - refusing to delete the last admin and refusing to demote the last admin - because a
server with no admin has no way back. A role change lands on connections
that are already open, not at next login: the role is re-read from the
store on every request, so a demotion takes effect mid-session. A live write
attach is re-checked every few seconds and dropped when steer goes away -
detached: you can no longer steer this run - and aether attach --read-only still shows the terminal afterwards. member remove first stops
and destroys that member's environment terminal, then deletes the member row
and erases their member home. If cleanup fails, the command returns the
runtime error and leaves the member in place so an admin can retry. It
refuses while any row still references the member - runs, schedules and
pushed profile snapshots reference members with no cascade, so the delete
returns in use - and an admin deletes those first; what a run wrote stays
in the data directory until the run is deleted. A successful removal ends
every attach and live sync of theirs.
Setting someone to the role they already hold is a harmless no-op, and a pending member's role can be changed before they are approved - approval and role are separate questions.
Admin-only commands answer with a clear error otherwise:
aether: rpc error -32001: workspace.add requires the admin role
aether server update is gated the same way: rpc error -32001: server.update requires the admin role for anyone but an admin. Any member can still read
aether server update --status. See install.md for
the command.
Workspaces
A workspace is the whole shared scope. It is a repository plus workspace settings; a run and its shells use the selected agent account's saved image, or the server's standard image when that account has not saved one. See environments.md for image selection and saving. Everything the team shares hangs off the workspace: runs, the event feed, the approval inbox, presence, templates, schedules, costs, and the budget. One workspace normally covers one project.
Creating one is an admin operation:
aether workspace init myproject [--base <branch>]
aether workspace add myproject [--base <branch>]
Four settings belong to the workspace rather than to any run in it:
The base branch is what every new run's worktree is cut from.
--basesets it at creation; it defaults tomain.The checkout Origin is the git URL every new run checkout gets as its
originremote - the place a run branch can be pushed for review. Normally the firstaether link --repo(or the Repository step in the local dashboard's onboarding wizard) reads that clone's ownoriginand records it. Recording happens only when the caller may push - a viewer may not - and the clone's origin is one the server accepts. When either does not hold, the link succeeds and nothing is recorded; set it later withaether workspace origin. First one wins. A later link from a teammate whose clone points somewhere else does not overwrite it, so one member's fork cannot silently redirect everyone's runs. Change it deliberately:aether workspace origin # show aether workspace origin https://github.com/acme/myproject.git aether workspace origin --clearA
github.comorigin in scp-like or ssh form -git@github.com:acme/app.gitorssh://git@github.com/acme/app.git- is recorded ashttps://github.com/acme/app.git. gh's credential helper authenticates HTTPS only and nothing in the member home authenticates SSH, so the HTTPS form is the one a run can push to. Every other host is recorded verbatim, and an SSH origin on another host cannot be pushed to from a run: give the workspace an HTTPS URL if runs need to push there.The remote is set when a run's checkout is created, so a change reaches new runs only; runs already going keep the URL they were given. With no checkout Origin recorded, run checkouts have no usable
origin, butaether pullstill brings their server branch into your clone.The source mirror is optional and separate from checkout Origin. A workspace with no mirror is local-only: collaborators and the daemon may push its base branch in the normal way. An administrator can configure a read-only upstream source:
aether workspace mirror configure --workspace myproject \ --source https://github.com/acme/myproject.git --branch main --auth public--branchdefaults to the workspace base branch. Public mode fetches credential-free HTTPS. For a private GitHub repository use--auth deploy-keywith its HTTPS URL; Aether prints a public key and the exacthttps://github.com/<owner>/<repo>/settings/keys/newURL. Add the key as a read-only repository deploy key, then verify it. For generic SSH, use--auth deploy-keywith anssh://source and--known-hosts-file <file>containing the verified host key. The private key is never printed or sent to a member.
Configuration starts pending. In the dashboard's Workspace Source control panel, use Verify or Refresh; from the CLI:
aether workspace mirror refresh --workspace myproject
aether workspace mirror status --workspace myproject
The panel shows source, branch, accepted and observed candidate commits,
and check times. A forward-only source update becomes ready and moves
the mirrored base. A rewrite or local/server divergence retains the
candidate without moving the accepted base; an administrator must review
and explicitly Adopt candidate (aether workspace mirror adopt --workspace myproject --generation <n> --yes). Disable requires
confirmation and returns the workspace to local-only; remove any GitHub
deploy key separately because disabling cannot revoke it remotely.
The steering policy decides whether collaborators may steer and kill each other's runs. It is permissive by default; an admin restricts it to owners and admins:
aether workspace settings # show aether workspace settings --steer-others admins-only aether workspace settings --steer-others everyone # back to the defaultThe dashboard's Workspace settings dialog has the same switch. A member refused by it reads
workspace restricts steer of others' runs to their owner and admins.aether protect <run>does the same for one run alone, whatever the policy;aether unprotect <run>lifts it. Both are for the run's owner or an admin, and both land on anyone already attached.
Scoped commands - run, budget, cost, inbox, who, timeline,
template, schedule - take --workspace <name-or-id> and default to the
only workspace when there is exactly one. That is why the solo path never
types it. With more than one they insist:
--workspace is required when more than one workspace exists.
Two commands sit outside that rule. aether runs takes no --workspace at
all: it lists every run you can see, so a teammate's work in another workspace
is never hidden from you. And aether profile push --allow-secret requires
--workspace outright, with no default, because the override is only worth
recording against a named timeline.
Every member links their own clone to the server:
aether link my-server --repo ~/code/myproject
which adds the aether git remote. Run branches (aether/run-*) are
server-owned - clients cannot force-push or delete them, because the branch is
the artifact. Every other branch behaves like a normal git remote.
The checkout Origin does not change workspace base ownership. For run
completion, Aether publishes each run's aether/run-* branch to the workspace
repo; candidate delivery is a separate review path, not another run-branch
publication. aether pull still brings a published run branch into your clone.
Pushing that run branch to checkout Origin is somebody's own act: the agent
inside the run can git push origin <branch>, or you can push it from your
clone after reviewing and merging.
Candidate integration
Candidate integration is the review boundary for combining retained run
submissions. It does not change run ownership or the mission scheduler. A
candidate is prepared from an ordered list whose entries carry
workspace_id, run_id, evidence_ref, and retained_revision, plus a full
target_ref in the form refs/heads/<branch> and its exact
expected_target_revision. Optional required_sources names are checked
while the packet is copied; a required unavailable or truncated source fails
preparation rather than becoming an empty observation. The candidate stores an
evidence snapshot and candidate-owned Git refs and transcript artifacts, so
later source cleanup does not change what was reviewed.
Packet snapshots are bounded to 1 MiB total per candidate.
Assembly happens in a server-owned isolated checkout, never in a live run checkout. Inputs are applied in their submitted order while preserving each source base. A conflict leaves the journal and checkout available for explicit file resolutions; the candidate freezes only after every input is applied. Once frozen, its candidate revision and inputs cannot be edited. The review surface labels evidence as observations: a server verification records the exact argv, observed image, runtime identity and working directory, bounded resource and timeout details, setup/environment provenance, exit and bounded output, and checks that the frozen tree was not changed, but a passed verification is not proof of semantic correctness.
Delivery is a human gate over the exact candidate revision, verification IDs,
target ref, expected target revision, and action. The caller and approver must
still be active members with the existing Push capability when the action
is performed; no candidate operation grants a new permission. On a local
target, update_ref uses an atomic expected-old compare-and-swap and refuses
if the target moved. A mirrored target cannot be updated directly: proposal
creates the public refs/heads/aether/proposal-<request-id> ref and a private
receipt without changing the upstream-owned base. A human fetches that proposal
and pushes it through the normal protected upstream review route; proposed
does not mean landed.
Candidate-owned evidence has its own bounded lifetime: candidates live for 30 days, and verification results and delivery requests expire no later than the candidate (verification validity is 24 hours). Original evidence packets may be deleted or expire after ownership transfer without deleting the candidate's copies. A missing or checksum-mismatched owned artifact makes the candidate unavailable and blocks verification and delivery; it is never silently reconstructed from the original packet. Expiry or deletion first fences new actions, then cleans owned resources; recoverable resources remain when that transition or cleanup cannot be completed. See integration.md for the method-level contract and failure-handling.md for restart and cleanup behavior.
In a local-only workspace, the base branch is client-writable. It is
usually already there by the time the second member links: whoever created the
workspace pushed it. Nothing guarantees that, so the local dashboard wizard's
Push now button (step 4 of quickstart.md) compares your
clone with the workspace's copy of the branch before it pushes, and reports
what it found instead of failing with git's
! [rejected] main -> main (fetch first):
- Not there yet - nobody has pushed the branch. Your clone seeds it, exactly as the first member's would.
- Level - both sides are the same commit. Nothing to push; carry on.
- Your clone is ahead - you cloned, then committed. The push runs as usual.
- The workspace is ahead - your clone is missing commits a teammate pushed. Fast-forward my clone moves your branch up to the workspace's commit, fast-forward only: no merge commit, and the commits you made are never rewritten. With another branch checked out, only the branch ref moves and your working tree is left alone; an uncommitted change the fast-forward would overwrite stops it, in git's own words.
- Diverged - you both committed since. Aether does not force-push and
does not merge for you; the wizard prints the
git fetch,git log,git rebaseandgit pushcommands and you decide.
In a mirrored workspace, the source mirror owns the base branch. git push aether <base> and the dashboard's Push now/repo.push are rejected by
design, as are local-daemon base pushes:
aether: rejected write to protected ref refs/heads/<base>: this mirrored base is owned by the upstream; use the configured upstream to update it
Do not work around that rejection: an administrator refreshes the mirror, reviews a retained candidate, and uses Adopt candidate only when a rewrite or divergence is intentional. Runs always branch from the accepted mirror commit captured at launch.
Working together
| Command | What it does |
|---|---|
aether runs |
Every run you can see, colored by owner, with conflict warnings. Prints a notice when any run is waiting on a human; --attention lists only those. Archived runs are hidden; --archived lists only those, with their deletion date. |
aether who |
Who is online and which runs they are watching. |
aether attach [--read-only] <run> |
Raw PTY passthrough. Multiple people can attach at once; write access needs steer, and without it the attach falls back to read-only by itself. |
aether inject <run> "..." |
Push an instruction into a running agent. Renders as a banner in your member color. |
aether pause / resume / kill <run> |
Suspend, thaw, terminate. Worktree and transcript survive a kill. |
aether delete <run> |
Stop the run if it is live, then remove its checkout, transcript, evidence, and run records. Needs the same permission as kill. A published run branch stays in the workspace repo and the timeline keeps the history. |
aether archive <run> / unarchive <run> |
Hide a finished run from the board and default aether runs, or restore it. Needs the same permission as kill; only a merged, abandoned, failed, or interrupted run can be archived. Archiving itself removes nothing; the server deletes the run on the printed date. See failure-handling.md for what the checkout TTL GC reclaims sooner. |
aether protect / unprotect <run> |
Limit steering and killing one run to its owner and admins, whatever the workspace policy says. |
aether handoff <run> <member> |
Transfer ownership and notification routing immediately. The recipient needs no acceptance handshake; Aether records the actor and both owners and captures handoff context. The agent account and its cost attribution do not change. |
aether close <run> --outcome merged|abandoned |
Record the finish outcome and clear a finished run off the attention board after its automatic evidence capture. |
aether inbox |
The shared approval queue; aether inbox approve|deny <request-id> decides, and any steer-holder can. --all includes decided requests. |
aether timeline |
The workspace's whole history; filter with --run, --member, --type, --limit, export with --jsonl. |
aether cost --runs |
Token spend per member and per run. |
aether budget |
The workspace's spend cap and what has been used. |
aether sync --live <local-dir> <run> |
Live-overlay a local directory onto a run's worktree. Local edits that collide are preserved as *.aether-conflict files. |
| `aether forward <run-id | terminal> |
aether member git [--name <name>] [--email <email>] [member-id] |
Show or set the name and email every commit made for that member is authored as. Members set their own; an admin can set anyone's. |
aether github connect |
Finish connecting GitHub after gh auth login in your environment terminal: sets up git credentials there, generates and registers a commit signing key. See environment-home.md. |
aether workspace origin [--workspace <name-or-id>] [<url>|--clear] |
Show or set the checkout Origin run checkouts use for pushing review branches. Needs the push capability. |
aether workspace mirror status|configure|refresh|adopt|disable |
Admin-only source-mirror lifecycle. Configure takes --source, optional --branch, --auth public|deploy-key, and optional --known-hosts-file; refresh/adopt/disable require --workspace. |
aether account list / share <member> / revoke <member> |
List usable agent accounts, or grant and revoke access to your own account. |
| `aether files ls <workspace | run> [path]/aether files cat <workspace |
Handoff and finishing runs
Handoff is an immediate transfer. aether handoff <run> <member> changes the
run owner and notification routing without waiting for the recipient to accept.
The timeline and Run Room record the handoff actor, outgoing owner, and incoming
owner. A system entry points to the handoff evidence packet, or says that
evidence is unavailable when preservation did not succeed. The transfer does
not switch the selected agent account or its cost attribution.
Aether captures an evidence packet automatically when a run is handed off and when it finishes. The finish capture happens before automatic checkout or transcript cleanup. A packet is a factual record of the run at capture time, including its objective and identity, capture time and event-log boundary, base and retained Git revisions, changed-file facts, source availability, unresolved facts, next action, and provenance. Source metadata says when a source is unavailable or truncated and gives the reason when one is known.
The packet retains the repository state in a private Git evidence commit, so
the diff remains available after the live checkout is removed. A rendered patch
is bounded to 1 MiB per request and reports truncated when that bound is
reached. A copy of the PTY transcript is retained separately, capped at 16 MiB.
The packet and its retained Git and transcript objects expire after 30 days.
After expiry they are unavailable rather than silently replaced with a partial
result.
Evidence is provenance, not independent verification. It records what Aether observed or what a harness, agent, or member reported; it does not prove that a command succeeded or that the result was reviewed. The Git tree and transcript are captured sources, not one atomic snapshot of the container, its processes, environment, or credentials. Treat a missing source as unavailable and a truncated source as incomplete.
If required preservation fails, Aether reports the failure and keeps the recoverable run resources instead of deleting the checkout or transcript silently. A handoff itself is not rolled back solely because its evidence packet could not be captured. Unresolved facts remain inspectable in the Run Room's evidence drawer. Use a fact's Answer action to open the composer with that fact prefilled, then edit and send a normal room comment. Evidence facts do not enter Needs you; only unanswered Run Room questions do. This is not a separate action inbox, blocker, or task model.
Task templates and schedules
A template is a saved launch: agent, task, mode, and parameters. Save one, launch it by name, or put it on a cron schedule:
aether template save nightly-triage --agent claude --task "triage new issues" \
[--mode headless] [--param key=value] [--budget <tokens>]
aether template list
aether run --template nightly-triage [--param key=value]
aether template delete nightly-triage
aether schedule list
aether schedule set nightly-triage "0 6 * * *"
aether schedule delete nightly-triage
Cron expressions are standard five-field syntax or an @descriptor, in UTC.
A schedule that was due while the server was down does not catch up; it waits
for the next occurrence.
Scheduled runs use the same launch gate as manual runs. A mirrored workspace refreshes its source before each scheduled launch; an offline, authentication, missing-source, rewrite, divergence, or other failure means that occurrence does not create a run. Fix the mirror or use the explicit one-shot cached-base retry for a still-unchanged accepted commit; the next scheduled occurrence does not silently reuse a cache.
Attribution
Every member gets a stable color from a colorblind-safe palette at join time
(aether member color <#rrggbb> [member-id] overrides it). That color is the
same everywhere: run rows in aether runs, inject banners in transcripts,
timeline dots, overlapping diff hunks, dashboard cards. "Whose agent is doing
what" is meant to be answerable at a glance from any screen.
Every privileged act - steer, kill, approve, handoff, settings change - is stamped into the workspace timeline with the actor. A server update is stamped into every workspace's timeline, since it affects all of them. Permissive by default, always attributed.
That attribution reaches git too. Each member has a git identity - the real
name and email their commits are authored as - collected by the local
dashboard's onboarding wizard and shown or changed with aether member git. The
agent in a run container commits with the identity of the member who launched
it, baked into the container when it is created, and the commits Aether makes
itself when a run finishes, is killed, or is recovered are authored as the
run's current owner with Aether as the committer. A member who has set no
identity keeps the fallback: their display name, or their member id when
the display name cannot be a git author name, at <member-id>@aether.local,
which maps to no upstream account.
Once that member has connected GitHub, those commits are signed with the
key in their environment home - the agent's own, from the home's
.gitconfig, and Aether's, with the same key read on the server. GitHub
shows a signature as Verified only when the committer address is a
verified address on the account that registered the key. So the agent's own
commits show as verified when the member's git email is on their GitHub
account, and Unverified when it is not - the <member-id>@aether.local
fallback above never is. Aether's commits are committed as Aether, so they
stay unverified either way;
security.md has the
detail.
Whoever else steers a run is credited as a co-author of it. Steering is
injecting a message or typing into the run's own agent terminal - a shell
tab in the run's container is not steering - and it adds that member, once,
to the run's steerers. Every commit Aether makes for the run then ends with
one Co-authored-by: trailer per steerer, deduplicated by address, so two
members sharing one address produce a single line. The run's own agent gets
the same list in /run/aether/co-authors and is told in its task prompt to
end its commits and pull requests with those lines - see
coordination.md. Aether's own commits credit everyone who
steered the run; the file the agent reads credits everyone the run involves,
its owner included, less the address that container already authors as. A
handoff moves the roles the server still decides: the incoming owner becomes
the author of Aether's own commits and the outgoing one joins the steerers.
Two limits, both from one fact: a container's GIT_AUTHOR_* and
GIT_COMMITTER_* are fixed when it is created, from the member who launched
it, and never move while it lives. Changing a git identity while a run is
live refreshes that run's /run/aether/co-authors, but the agent's own
commits in that run keep the identity it started with; later runs use the
new one. And after a handoff the agent's own commits are still authored as
the outgoing owner, while Aether's end-of-run commits are authored as the
current one - which is why the incoming owner is on the co-author list at
all, and the outgoing one is not.
One consequence worth naming: a member who changes their git identity while they own a live run is credited on that run's remaining agent commits under the new address, because the container is still authoring under the old one. The commits carry both addresses rather than losing the change, which is the useful answer.
Conflict radar
Per-run diff snapshots feed an overlap index. When two runs touch the same
file, aether runs shows it in the OVERLAP column and the dashboard puts a
chip on both run cards naming the file and the other member. It is early
warning, not locking - nothing is blocked.
On top of that, overlapping runs can message each other directly through their
agents (on by default; --conflict-coordination=false turns it off). See
coordination.md and mcp-bridge.md.
Budgets
aether budget # show
aether budget set --limit 25 --warn 20 # hard cap and soft warning, in USD
aether budget set --override # admit new runs past the cap
aether budget set --override=false # and stop again
An omitted flag keeps its current value, so --warn alone edits only the
warning threshold. --limit 0 clears the budget.
At the cap, new runs are refused and running runs finish. Note that runs whose
harness reports no token usage are counted as unmetered - aether cost says
so explicitly, and the totals are a floor rather than the real spend.
Deleting a run keeps its cost counted. aether delete removes the run's own
record, but its numbers stay folded into its workspace's and its member's
totals, so aether cost, aether budget, and the cap itself see the same
spend before and after the delete. Removing the member with member remove
does not clear that folded spend either: it stays in the workspace's total
under the member's now-gone ID.
Agent accounts
Each member runs aether agent add <name> once, then opens aether terminal
to install the agent and complete the vendor login. The member's server-side
home is mounted into every container that person receives, so all their runs
share the same login and installed files. See harnesses.md.
A member may explicitly let another collaborator launch against that account:
# Account owner
aether account share <member-id>
aether account list
# Recipient
aether run "triage the failures" --agent codex --account <owner-member-id>
# Account owner
aether account revoke <member-id>
The dashboard exposes the same existing account-sharing controls on Members and an Account picker in the launch dialog. The run is owned by the authenticated launcher, whose identity is used for the timeline and Git author. The selected account supplies its saved environment, complete home and credentials, configuration roots, custom harness definitions, vendor quota, and cost attribution.
Sharing is directional. It does not let the recipient open the owner's environment terminal, and admins get no implicit account access. It does let a root process in the recipient's run read or change every file and credential in the shared home. Revocation blocks new launches and relaunches but does not stop existing runs; stop them first if access must end immediately.
The dashboard's bottom-left status bar reads the selected account's
subscription quota through the read-only account.usage RPC. An empty
account_member_id means the caller's account; selecting another account
requires the same explicit share as a launch, and an admin has no implicit
access. The server rechecks that grant after each provider read, so a revoke
during a request cannot return the owner's data. This read never copies a
credential to the client or to a run. Native OAuth credentials are read only
for Claude Code and Codex; API-key logins and other harnesses are reported as
unsupported, and native reauthentication remains the owner's terminal action.
Normal successful usage is cached for 60 seconds. refresh:true bypasses that
success cache only within a 10-second request floor; failures wait at least
60 seconds and honor a provider retry deadline. A stale row keeps only a
same-credential successful window, reports its error and stale status, and is
not presented as current after a reset has passed without a fresh measurement.
Mission identity and current authority
Mission work does not introduce a second identity or credential boundary. Record these roles separately:
- Actor: the authenticated human, originating run, or server action that performed an operation. An integrator run is the actor for dispatches it makes.
- Authorizing human: the human who authorized the mission or consequential action.
- Run owner: the member responsible for the run's workflow and notifications.
- Account owner: the member whose selected account supplies the run's image, home, configuration, and native credentials.
The actor is not rewritten as the authorizing human, run owner, or account owner merely because the operation was performed on somebody's behalf. A mission's finite concurrency and total-attempt limits bound Aether admission; they do not narrow what the selected whole-home credentials can do inside the container.
Release B rechecks the current member role, account-sharing authority, mission assignment, and control/assignment generation at each consequential operation. Presence, a skill, an integrator label, or a run ID does not grant authority. There is no separate eligible-controller administration, grant expiry, or per-worker approval product, and taking control does not grant access to another member's account.
The durable worker takeover hold has its own generation and is independent of
the ephemeral control lease. mission.worker.release is a human-only
compare-and-swap action: the caller supplies the worker run ID and observed
expected_takeover_generation, and Aether rechecks current steering authority
and control admission before clearing the hold. A stale generation, revoked
authority, or foreign holder is refused without clearing it; an integrator or
worker cannot release the hold through its assignment socket. Releasing a
hold changes control state, not account sharing.
Mission progress has the same evidence boundary as the dashboard: a worker report or process success does not make a task Done. The current task revision must have an accepted submission with required evidence available, and any scope deviation must carry an explicit disposition. The resulting evidence remains provenance of what Aether captured or a participant reported, not independent verification.
Agent configuration is not watched or inventoried automatically. In the local
dashboard's Agents step, choose one local directory with the browser directory
picker, preview it, and explicitly import it once. Known credential names and
runtime/history defaults are skipped locally; remaining bytes are uploaded
and server-scanned, so do not assume all secret content stays local. The
server-hosted dashboard has no local directory picker; use aether gui for this
step. The import writes the authenticated member's persistent home immediately,
including for active runs using that account. An agent may need to reload its
configuration.
The Files view browses and edits that own-member configuration beside
workspace base and live-run files. It has explicit Save or
Commit to config.* method
requires Launch and targets only the authenticated member's own home; an
admin cannot select another member.
New browser-import files are mode 0644; executable mode and symlinks cannot
be represented by the browser. Imports preserve empty and arbitrary binary
regular files under the 1 MiB/file, 20 MiB decoded aggregate, and 2,000-file
limits. The server rejects unsafe paths, symlink components, hardlinks, and
nonregular files. A shared account uses the same read-write home rather than
an isolated per-run copy. A snapshot pin records launch provenance, not an
isolated writable home or a promise that home edits wait for later runs.
Editing does not rebuild the installed-agent image.
The manual CLI profile surface remains available for operators who need snapshots or rollback:
aether profile push --agent claude
aether profile status --agent claude
aether profile rollback --agent claude <snapshot-id>
profile push is explicit and not run by the sync daemon. Its repeatable
secret flags are --skip-secret <file> and
--allow-secret <file>; the latter requires --workspace <workspace>:
aether profile push --agent claude --skip-secret <file>
aether profile push --agent claude --allow-secret <file> --workspace <workspace>


