Skip to content

The zones run under microsandbox, with no account - #79

Merged
ALLiDoizCode merged 22 commits into
mainfrom
zones/microsandbox
Oct 13, 2026
Merged

ALLiDoizCode merged 22 commits into
mainfrom
zones/microsandbox

Conversation

@ALLiDoizCode

@ALLiDoizCode ALLiDoizCode commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #78. Part of #57.

Docker Sandboxes (sbx) refuses every command until the person signs in to a Docker account, so the one-line install still ended in a browser sign-in. Both zones now run under microsandbox (msb 0.7.8, Apache-2.0, KVM through libkrun): no account, no root, no service. sbx is replaced, not kept beside it. ADR 0004 records the decision.

What changes

  • install.sh puts a pinned microsandbox in ~/.local/share/spaceturtle/msb, checked against its checksum. That home is spaceturtle's alone. The next steps it prints are start, key, session.
  • The connector's zone is made with the wallet's storage mounted as toon's home, a disk of its own for the container engine, /run in memory, the public internet out, and the two ports in from the machine's loopback.
  • The agent's zone has a wall: name resolution, the two ports, the model provider's host, the network's connector, and api.github.com and codeload.github.com for the skills. Everything else is refused. Under sbx the machine's policy also let it reach about 190 hosts.
  • A wall and the ports are fixed when a zone is made. A session that needs another wall (a model of another provider) makes the agent's zone again and carries the agent key across in memory. What the agent keeps is now in mounted directories, so it outlasts the zone, and upgrade no longer loses it.
  • One session at a time, and the agent's zone is stopped when its session ends.
  • The unit no longer holds a session open to keep the zone alive. It starts the zone after a restart and starts the supervisor again when it ends.
  • The image drops the proxy line for the overlay client and finds the zone's own address differently.

Run for real

On devnet, on this machine, signed out of Docker, beside the existing Install:

Check Result
install.sh's microsandbox step: download, checksum, layout Yes
spaceturtle start, a whole Install 39 s
Unattended sessions, skills loaded through the wall Yes, about 17 s each
A session on a terminal docker-agent starts; I did not drive a conversation
The wall: 6 allowed targets answer, 6 others are refused (machine port 22, LAN, other hosts) Yes
Model of another provider: zone made again, key carried Yes, 8 times
stop and start, 4 times Relay reachable as soon as start returns
upgrade Passphrase carried, wallet files unchanged, apps back
Untrusted app by the agent key: waits, approved, runs under runsc Yes
The unit under real systemd (systemd-run --user): stopped zone, ended supervisor Back in 9 to 15 s
A zone image pulled from ghcr with no account Yes

Two bugs the run found are fixed here: the container engine did not start after a zone restart (stale files in /run), and start returned before the node answered.

Not verified

  • A real restart of the machine.
  • A host user whose uid is not 1000. The mounts are shown to the zone's user with uid=1000,gid=1000, which I could not test with another uid.
  • The released images under upgrade's --pull always.
  • microsandbox over days, and whether msb sends telemetry.

Known limits

  • A host in the wall is held to the addresses its name resolves to, not to the name. Holding it to the name needs microsandbox to open the zone's TLS (--tls-intercept), which worked in one probe and is not used.
  • A provider the launcher does not know needs SPACETURTLE_MODEL_HOST, which the timer's unit does not carry.
  • An Install made under sbx is not moved: its passphrase is inside the sbx zone. No release shipped a working sbx launcher, so there is no migration.

Added after the first review

  • microsandbox's home must be 54 characters or less (its socket paths). The default is now ~/.local/share/spaceturtle/msb, with ~/.spaceturtle-msb when that is too long; the installer and the launcher say the limit.

  • The README presents an agent and its harness, not a panel. The GIF is removed from the README and the watching guide.

  • The chat opens on the README's banner, moving. docker-agent draws its own name on an empty conversation and takes nothing animated in its place, so the agent's zone image builds docker-agent 1.150.0 again with one patch, --banner-file, and holds a film of frames made from docs/assets/spaceturtle-banner-dark.svg (ADR 0005). The patch is offered upstream as feat(tui): --banner-file replaces the startup banner, with animated frames docker/docker-agent#4578. The agent image build takes about four minutes longer.

  • A zone without its ports is refused. microsandbox opens a zone's published ports when the zone starts; if another program holds one then, the zone runs and nothing listens. This happened on a real Install (the old sbx zone still held 47100), and the agent found no node. start and session now check both ports and say to stop and start.

  • spaceturtle alone is the conversation. With nothing after it, the launcher starts a session; spaceturtle session is the same.

  • The stars fill the chat. The film is a field 240 characters wide and 64 high with the turtle in its middle, and the patch crops it to the chat area.

  • The sidebar lists the node's apps. The patch gains a second flag, --sidebar-section, offered upstream as feat(tui): --sidebar-section shows a command's output in the sidebar docker/docker-agent#4585. The launcher gives it spaceturtle-apps, which asks toon status with the session's agent key.

Not in this PR: with an agent key, toon status does not say an app's ILP address, so the agent guessed g.whoami and was rejected. That is toon-protocol/toon_cli#252, fixed in toon-protocol/toon_cli#253; the pin here moves when a toon release holds it.

Run for real since: the banner plays in a real session and stops on exit; an unattended session on the rebuilt image reaches the node and works.

Gate: tests/run, 509 checks.

🤖 Generated with Claude Code

ALLiDoizCode and others added 22 commits October 12, 2026 19:39
toon 0.2.0 is the first release with a scope and a spending limit for each
agent key (connector#1485), toon without a home (toon_cli#208) and apps in
hardened containers (toon_cli#209).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… docker-agent (#57)

What the first run on devnet found, each fixed here:

- sbx stops a zone 30 seconds after the last command attached to it ends, and
  nothing starts its daemon with the machine. A systemd user unit now holds a
  session open in the connector's zone with the supervisor in it, and brings
  the zone back after a restart.
- toon's home is ~/.toon/agent-node, so the wallet was not in the mounted
  directory. A stale supervisor socket there is accepted by connect, so
  connector-start removes it before toon up.
- The agent's zone could not reach the node. connector-start forwards the
  supervisor's orders and the connector, the launcher publishes both on the
  machine's loopback, and the key file's addresses are rewritten on the way in.
- Apps were reached through the sandbox's proxy, which cannot reach them.
- sbx has no 'start', no 'run --image' or '--allow-host', and refuses a second
  'policy init'. docker-agent has no '--max-cost': the ceiling is its budget.
- toon's flags and documents: agent-key create --out, wallet backup --out,
  agent-key set --max-per-day, pending_apps, the network in state.json.
- The agent's tools are the commands toon answers with an agent key, and one
  that pays an app behind another connector.
- An image off toon's trusted list gets gVisor through untrusted-runtime.
- A ChatGPT account can be the model provider.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The unit that keeps the zone running is enabled on every start, and started
whenever it is not holding a session. The forwarders listen on the zone's own
address, which no app's network reaches. A second start no longer pushes the
next session out. The port setting must be a number, the images workflow
checks jq is in the agent's zone, and the agent's instruction says that adding
a peering opens a channel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Which chain an Install is on is configuration: a profile and whatever of it a
flag replaces, mostly the RPC endpoint. start takes toon init's own flags and
passes them as given, keeps them with the Install, and adds --evm-chain-id,
which asks the node's endpoint for its chain id before anything is funded or
started. 'spaceturtle mainnet' is gone: it ran two toon commands that do not
exist. Mainnet still asks for the typed word, and funds nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
install.sh, piped from curl, checks the machine, installs the launcher and the
agent definition of the latest release, links spaceturtle into ~/.local/bin,
and installs the pinned Docker Sandboxes when there is no sbx. The launcher
finds itself through a link, and pulls the zone images of the release it was
installed from in place of latest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Docker Sandboxes refuses every command until the person signs in to a Docker
account. Both zones are now virtual machines under microsandbox (msb 0.7.8),
which needs KVM and nothing else. install.sh puts a pinned copy in
~/.local/share/spaceturtle/microsandbox, checked against its checksum, and the
launcher drives that copy with a home of its own.

- The connector's zone is made with the wallet's storage mounted as toon's
  home, a disk of its own for the container engine, /run in memory, the public
  internet out and the two ports in from the machine's loopback.
- The agent's zone has a wall: name resolution, the two ports, the model
  provider's host, the network's connector and the two hosts the skills come
  from. A wall is fixed when a zone is made, so a session that needs another
  makes the zone again and carries the agent key across in memory. What the
  agent keeps is in mounted directories that outlast the zone.
- One session runs at a time, and the agent's zone is stopped when it ends.
- start waits until the supervisor answers, not only until it is there.
- The image drops the proxy line for the overlay client and finds the zone's
  own address without the name of the host.

Run for real on devnet beside the existing Install: start in 39 s, sessions,
the wall, stop and start, upgrade, an untrusted app approved under gVisor, and
the unit under systemd bringing back a stopped zone and an ended supervisor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ADR 0004 records the move from Docker Sandboxes and what it changes: no
account, a wall of the agent's zone's own, and walls and ports that are fixed
when a zone is made.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A wall's rule names only a host with a domain: an address, or one of
  microsandbox's words for a group of addresses, is refused as the model's
  host and skipped as the connector's.
- The ports an Install was made with are kept in the settings, so the units,
  which carry no environment, use the same ones.
- The model key is exported by the shell, not passed through `env`, whose
  arguments held it for a moment.
- upgrade pulls both images before it removes anything.
- A new agent's zone that could not be given its key is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
microsandbox refuses a home of more than 54 characters, and the default was 59
for a user name of twelve. It is now ~/.local/share/spaceturtle/msb, gives way
to ~/.spaceturtle-msb when that is still too long, and the launcher and the
installer say the limit before anything is made.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The install line leads, a table says what the harness gives the agent, and the
panel is the optional part it now is. The panel's GIF goes: it showed the old
shape.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The definition's welcome message is drawing 1 of TurtleIcon.qml, which docker-agent
shows where its own banner would be, and the launcher names the chat spaceturtle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The launcher draws the banner's turtle and stars in the terminal for the two and a half
seconds a session takes to start, and the definition's welcome message is a still of it,
in place of the icon. SPACETURTLE_BANNER=off starts a session without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… ports is refused (#57)

docker-agent draws its own name on an empty conversation and takes nothing in its place
that moves. The agent's zone image now builds docker-agent of the same release again with
one change, --banner-file, and holds a film of frames made from the README's banner
(ADR 0005). The launcher's own animation and the welcome message's still give way to it.

A zone whose ports were held by another program when it started ran with nothing
listening, and the agent found no node. start and session now say so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With nothing after it, spaceturtle starts a session. The banner's film is now a field of
stars 240 characters wide and 64 high with the turtle in its middle, and docker-agent's
patch cuts it down to the chat, so the stars reach every edge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docker-agent's patch gains --sidebar-section, which shows a command's output as a
section of the sidebar, and the launcher gives it spaceturtle-apps: each app behind the
agent node's connectors, whether it runs, and the apps that wait for the Observer, as
toon status tells the session's agent key (ADR 0005).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mes the second pull request (#57)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Token Usage in docker-agent's sidebar counts the model. What the agent may pay through
the connector is the agent key's spending limit, which now has a section of its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
status and the chat's sidebar show what others have paid the node on its inbound
channels, how much of that has landed, and the fees and packets since the connector
last started. An agent key is told none of it, so the launcher reads it in the
connector's zone and, during a session, writes it where the sidebar can print it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ALLiDoizCode
ALLiDoizCode changed the base branch from install/one-command to main October 13, 2026 15:53
@ALLiDoizCode
ALLiDoizCode merged commit 0d5c9d7 into main Oct 13, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant