ADR 0244: Machines join Clankie at an access level
Status: accepted (James, 2026-10-07). Tracked by
VUH-1792. Ships the join
transport that ADR 0212 reserved for later.
Amends ADR 0199's
"a hosted body has no desktop at all". Leaves who may ask for machine tools to
ADR 0133. The app side is
app ADR 0066 (the web app is the main app; the desktop is the web app plus
the pet).
Context
Hosted and self-hosted Clankie are the same being: the same service, app, Discord, memory and workers. What differs is where his body runs. A self-hosted Mac lets him act on that Mac. A hosted owner has no way to let him act on their own laptop. Machines today are the local Mac and ssh hosts (ADR 0212). A laptop with no ssh route can only be a device you talk through.
Access is also all-or-nothing. On the local Mac the service runs as the owner with an unsandboxed shell (ADR 0086). Approved workspace directories (ADR 0193) narrow where workers start, but they are not an OS boundary. Some owners will not want Clankie to have full control of a machine.
Decision
Any machine can join. clankie join on a laptop or PC shows a code. The
owner approves it from an existing device, and the machine dials out through
the gateway to Clankie, hosted or self-hosted. It needs no inbound port. The
desktop app offers the same thing as "Let Clankie use this computer", and the
TUI offers it when connected to a hosted Clankie. A phone is never a machine.
On mobile the app is the whole surface.
Each machine has an access level the owner chooses.
| Level | Clankie may |
|---|---|
| portal | talk to the owner there; no action on the machine |
| workers | hire agents into Herdr sessions in owner-approved directories |
| shell | use his own shell on the machine (ADR 0086) |
| screen | see and drive the desktop (desktop control) as a computer host |
Each level includes the ones above it. The level says what Clankie may do on a machine. ADR 0133's grants still decide who may ask him to. A joined machine enforces its own level and refuses anything above it.
Access contract (VUH-1801)
The four names are the complete ordered vocabulary. Owners choose one level,
not a list of tools or arbitrary commands. portal is the default for an
unknown or newly registered remote machine. The local self-hosted machine
retains screen when no level was stored, preserving existing installations.
An invalid level or unavailable policy never grants an operation.
Machine permission is a ceiling independent of speaker authority. Discord ownership and room skill grants follow ADR 0251: becoming an owner does not raise a machine's level, and a room skill never grants shell or screen access. Worker directory grants and native pane proof remain additional requirements. A bounded service adapter using fixed, service-owned I/O is a product capability, not the caller's general shell. Its skill, actor and audience checks stay separate from this machine ceiling.
The owner API, paired-device command, CLI and TUI use the same level contract. The inventory reports the selected level and whether enforcement is a service preference or a host boundary. Changes require owner authority; workers and joined clients cannot raise their own level. The local default is a specific migration rule, never a default for unknown remote hosts.
Enforcement checks current policy at execution, including after queued work waits. Catalog visibility is not authority. Lowering a level stops new effects above it, but does not terminate existing workers or recall completed effects. Recovery/stop paths remain available so revocation cannot strand an active screen lease. Refusals name the machine, selected level and required level.
Join transport and its authenticated host registration are VUH-1800. A joined receiver must check its own owner-approved ceiling before dispatching a typed worker, shell or screen operation; an incoming level or service assertion cannot raise that ceiling. Until that receiver ships and is exercised, a service-side policy check is not joined-host enforcement evidence. Similarly, lowering the self-hosted Mac remains a service preference until VUH-1804 adds an OS boundary. A shell with the owner's credentials can otherwise bypass Clankie's own tool restrictions.
A self-hosted Mac keeps full access by default. Existing installs keep working. An owner can lower it. A lowered local level is called a sandbox only when the OS enforces it (a separate user, a sandbox profile or a VM). The service runs as the owner, so a limit kept only by Clankie's own tools is a preference, not a boundary, and docs say which one applies.
A hosted owner can lend a screen. A joined machine at the screen level is a
ComputerAdapter host for a hosted Clankie. Consent is per session. The
desktop pet shows that Clankie is driving and offers a stop. Input stays off
until the owner allows it. ADR 0127's stops hold. This differs from giving a
hosted Clankie a computer of his own
(VUH-1619).
Consequences
- Hosted and self-hosted owners get the same machine model. Only the default for the local Mac differs.
joinneeds gateway relaying for machine traffic and an approval flow on an existing device (VUH-1800).- Levels need one vocabulary across CLI, TUI, Settings → Machines and docs (VUH-1801).
- Lending a screen needs capture and input bundled per OS. Today's adapters borrow Peekaboo and Codex's Windows tools (VUH-1803).
- A real self-hosted sandbox is OS work, not a settings flag (VUH-1804). Until it ships, docs must not call a lowered local level a sandbox.
Existing SSH machines with a saved enabled fleet connection and no recorded
access choice migrate to workers; the next settings write persists that
choice. Explicit levels (including portal) stay unchanged. New registrations
and unknown hosts remain portal; SSH alone grants no shell or screen access.
Access refusals are retained per machine and required level and appear in
clankie doctor / clankie doctor --machine FLEET --json. The authenticated
read is GET /v1/machines/access-refusals, also available as
clankie machines access-refusals --json; it names the machine and owner fix.
Resolved refusals leave the doctor view after the owner's grant covers them.
Amended 2026-10-16 (VUH-2319): a joined machine's shell level now grants
Clankie's own shell there, through one tool, joined_machine_shell. It is
offered only in the owner console lane with machine tools. Discord lanes,
including owner-only rooms and DMs, and peer seats never receive it. Within that
lane, only turns the owner started may use it. Autonomy, Linear wakes and worker
reports are refused, so untrusted input never reaches another machine. James can
widen this later. Input steered into an owner's running turn still inherits its
owner status.
Each call:
- rechecks the machine's live level, with no cached grant;
- starts in a folder approved at join; the shell itself is not an OS boundary (VUH-1804);
- relies on the receiver's ceiling, output and time bounds, single execution and revocation.
The receiver runs each command in its own process group, so revocation, the time
limit or a lowered level stops what it started. A deliberately detached process
(setsid) escapes, and on Windows children left after the shell exits are not
stopped (no Job Object). Its first output line is its own exit or stop, so command output cannot
forge the audited exit. Older receivers report success only for exit 0.
At screen, the shell can reach the screen through the machine's own permissions
without the per-session screen consent. That follows from screen including
shell. Screen lending without shell is not expressible in the ordered levels;
an owner who wants neither chooses workers. Whether to add consent-only screen
lending is an open owner decision.
Each call is recorded before anything is sent; a call that cannot be recorded
does not run. The record holds the lane, origin, conversation, machine, folder,
command digest and the reported outcome. unknown means no result arrived in
time. The command text still lives in the session transcript; the digest is a
convention, not secrecy. It is readable through
GET /v1/machines/shell-audit, clankie machines shell-audit and doctor.
Hiding shell for joined machines was rejected: screen includes it, so one
hidden rung would break the single ordered vocabulary above.
Amended 2026-10-21 (VUH-2607): at workers, Clankie himself hires onto a joined
machine. His own hire_agent with fleet: "join-UUID" starts a native Claude
worker there through the same bounded workers adapter the joined lead uses, and
the worker reports into the chat that hired it; message_seat to the returned
join-UUID/PANE seat reaches it. The hire carries the same limits as his joined
shell: the owner console lane with machine tools, turns the owner started only,
a live level of at least workers, and a working directory inside a folder
approved at join. A folder outside those is refused as unknown_directory,
another harness as harness_unavailable, and a lower level by the access
refusal above. This needs no SSH fleet, and his shell there stays refused at
workers.
Amended 2026-10-21 (VUH-2609): the hire may also start a Codex worker, signed in with that machine's own Codex login. Claude takes its session id at launch; Codex mints its own, so the trust shape differs only in how the session is bound:
- The home's launch id is a nonce for Codex. The machine's adapter checks that the new pane holds no native session before launch, then admits the first Codex session that appears in it. The home binds the worker to that pane and session once, and refuses any later admission of another pane or session.
- Before every request, the worker's bridge compares its pane's live session with the one the home admitted, read fresh from the home. Claude's is compared with its launch id as before. Herdr keeps an agent's first session, so the check only ever refuses a pane now held by another agent.
- Codex launches with
--no-daemon(its own process, so Herdr learns the session from its first turn) and the brief as its first turn. Its MCP worker bridge receives only the variables it names. - A message to a Codex worker is refused: its bridge delivers messages only as
Claude channel events, and nothing is typed into its terminal. A later option
runs a local
codex app-serverand sends messages as turns.
The launch's harness field is additive: a Claude launch omits it, so receivers
from before VUH-2609 keep working.
