Give every member a persistent master terminal - #61
Merged
Conversation
One long-lived container per member ("your environment"), reachable from
the CLI and a dock under the run board. The standard image with the
member's home mounted at $HOME; /bin/sh -l fallback when the image has no
bash.
- domain.Terminal + member_terminals store methods (table shipped with the
Step 1 migration); scheduler EnsureTerminal/EnsureTerminalTab/
TerminalStatus/StopTerminal with per-member locking, container
supervision (exit destroys and prunes so the next open recreates), and
startup re-adoption of surviving containers.
- Tabs are TTY execs in the same container: main plus up to five named
tabs ([a-z0-9-]{1,32}), each a ptyhost session keyed
terminal:<member>:<tab>.
- New SSH subsystem aether-terminal with membership-only live revocation;
control methods terminal.status/terminal.stop; member.remove stops the
terminal and removes the home (retained if the stop fails).
- Local gateway GET /ws/terminal?tab=; ws capability list gains terminal;
cli TerminalStream; new aether terminal [--tab] / status / stop.
- aether agent add and the GUI wizard type the harness install script
into the live terminal (static instructions as fallback).
- Web: env-terminal store slice, board terminal dock (shared Dock +
xterm-host, empty state, stop confirm), wizard live setup.
- A live terminal attach holds the self-update idle check; the container
itself does not (it is re-adopted after restart).
- Docs: new docs/terminal.md; local-gateway, environment-home, harnesses,
quickstart updated.
- Integration: TestIntegrationMasterTerminal drives start, tabs, restart
re-adoption, and stop against real Docker.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements plan Step 2 (the master terminal): Group 1's second and final PR, on top of #50.
What changed
$HOME, created on first open and re-adopted across server restarts./bin/sh -lfallback when the image carries no bash.mainplus up to five named tabs ([a-z0-9-]{1,32}, cap errorterminal: at most 6 tabs), each aptyhostsession keyedterminal:<member>:<tab>with its own transcript. Exec falls back to/bin/sh -lon exit 126/127.aether-terminal: the authenticated member can only ever open their own terminal; membership is re-validated live (removal closes the socket with the membership-revoked exit status). Control methodsterminal.statusandterminal.stopare self-scoped.member.removestops the terminal and removes the member home (the home is retained when the stop fails, so a live bind mount is never deleted underneath a running container).GET /ws/terminal?tab=<name>(1008 on membership loss, 1000 on shell exit); ws capability list gainsterminal.aether terminal [--tab <name>],aether terminal status,aether terminal stop.aether agent add <shipped>opens the terminal and types the vendor install script; static guidance remains the fallback.terminalcapability; the agents wizard embeds the live terminal for setup with a static fallback.docs/terminal.md; local-gateway, environment-home, harnesses, quickstart updated.Verification
Plan verification item 3 (master terminal), plus the Step 1 leftovers:
go test -race -tags integration -run TestIntegrationMasterTerminal ./internal/server/(real Docker):Drives the full lifecycle: first open starts the container (
echo home=$HOMEprints/root), a second tab shares it, detach + server restart re-adopts the running container (terminal.statusstillrunning, tabs intact, fresh attach answers),terminal.stoptears it down and the next status reports not running.Membership revocation:
go test ./internal/sshd/ -run TestTerminalDropsOnMembershipRevocation(removed member's terminal socket closes with the membership-revoked exit status).Scheduler lifecycle unit tests: exit-then-reopen recreates (exited container destroyed, row pruned), tab regex/cap with the literal error, sh fallback, startup re-adoption,
BuildEnvironmentPlan(nil ws, terminal)uses StandardImage.Repo checks after rebasing onto current main (af3adb1):
make fmt-check,make vet,make lint: 0 issuesmake test: 39 packages ok; three pre-existing machine-local failures that fail identically on a cleanorigin/maincheckout on this machine (internal/localgwTestUpdateApply*- a real desktop app is installed;internal/selfupdateTestOwnedOnlyByWalksToTheTopfrom Update a root-owned CLI from the dashboard through the macOS admin dialog #49); CI passes themmake test-integration: ok apart from the same machine-local casesmake public-audit: PASSweb/:bun install --frozen-lockfile && bun run typecheck && bun run test: 50 files, 520 tests passedAdversarial review findings fixed before this PR:
Wait) and destroys corpses, supervision destroys on exit, with a regression test.errors.Is) instead of message substring.