Setup
Setting up the daemon
claweed is the session broker — it runs on every box that hosts sessions.
Install it, then run claweed setup once, as your own login. It takes
no arguments and does two independent things: doctor --fix, then a report of your
tenant's key state with the guidance for enrolling one.
$ curl -fsSL https://release.clawee.org/claweed/install.sh | sh
$ claweed setup # first run, as your own login: doctor --fix, then tenant status + knock/approve guidance
The two halves are independent, and only the second is yours to complete: a host gap
--fix could not repair is reported and setup carries on with the parts that need no
privilege, so setup exits non-zero only when the tenant bootstrap itself failed.
That bootstrap creates nothing — a tenant's trust list is built by a client knocking
with its own clawee-generated key and you approving it here. If your tenant already has keys it
says so and stops; if it has none it prints the two ways to get one:
tenant "alice" has no enrolled keys — knock from a clawee client and approve it here: claweed devices approve or enroll one directly: claweed tenant add --data-dir ~/.clawee/data <pubkey|@file>
Under sudo it answers for you, not for root. A data root is per user, and
since claweed v0.2.19 the resolution takes the tree of whoever the run is for: an
explicit --data-dir, else the human the elevating tool recorded
(SUDO_UID / DOAS_USER), else the account the process is — root's own
included. Through v0.2.18 it refused there instead, which left one deployment with no repair
path at all: on a host with no boot unit, sudo claweed doctor --fix resolved
nothing, the data-dir row read ?, tenants and sessions went unmeasured, and the
offered restart failed against a service that was not there.
sudo claweed auth is fixed with it. The tenant name used to come from the
real uid while the data tree came from the elevation record, so the two named
different accounts: the key landed under tenant root inside your
~/.clawee/data — an enrolment no child ever reads — and the directories created
along the way stayed root-owned, locking you out of your own deployment. Both now come from one
source, and a root process creating a tree inside your home hands back every directory it made.
A box you administer as root is a different case, and works: root is a login like any
other, its tree is /root/.clawee/data (the installer creates it, and its uid is
admitted to host sessions at install), and claweed setup run as root bootstraps root's
own tenant exactly as it does for anyone else. Through v0.2.14 the older refusal caught root too —
setup answered root is not a tenant, devices list said a root
process owns no data root, and doctor could not even name the folder the installer
had just made.
setup no longer imports ~/.ssh/authorized_keys.
Through v0.2.8 it offered to enrol every key in the invoking user's
authorized_keys as that user's first trusted device; v0.2.9 removed the import
(operator ruling, 2026-08-24). It was the wrong key material: a clawee client presents a key
it generated, and that is what claweed authenticates — the host's
authorized_keys holds the client machine's ssh keys, which claweed never
checks, and claweed separately puts its own key there for the pty session, a different
direction entirely. Enrollment is knock + claweed devices approve, or a direct
claweed auth / claweed tenant add, only.
Standard users, and the consented elevation
A standard (non-admin) user can run setup. Part of what doctor --fix
repairs is root-owned — most often the host's spawn-allow list, which names the uids permitted
to host sessions — and a user with no route to sudo cannot write it. claweed asks
first, and the prompt names the write:
fix: permit uid 502 to host PTY sessions by adding it to /usr/local/clawee/etc/spawn-allow, as root through an elevation prompt? [y/N]
Answer y and claweed escalates through the OS, never holding the credential itself —
the dialog, pkexec or su each collect the password on their own
surface. The rungs are tried in order and the first that produces the write ends it: on macOS
sudo -n, the macOS authorization dialog (GUI sessions only), then
su <admin>; on Linux sudo -n, pkexec (GUI only),
su, then su <admin>. The last rung is always the command printed
for an administrator to run by hand, so a declined or unavailable elevation is never a dead
end — the gap is reported and setup still exits 0.
The elevated write is one narrow verb, claweed policy allow-uid <uid>: the
host's spawn policy, root-owned — which uids may host sessions on this box. It appends
one uid and never truncates (the file usually already names other accounts, and a truncating
write would revoke them), is idempotent, and leaves the file root-owned 0644.
Mind the polarity before you run it: the list is an opt-in restriction — while the
file is absent every uid is permitted, and this verb creates it (root-owned
0644) when it isn't there. On a host that has no allowlist yet, one append turns
permit-everyone into permit-only-this-uid. Run it yourself when you would rather not elevate
from inside doctor:
$ sudo claweed policy allow-uid 502 uid 502 added to /usr/local/clawee/etc/spawn-allow $ sudo claweed policy allow-uid 502 # again — idempotent uid 502 is already permitted by /usr/local/clawee/etc/spawn-allow
Enrolling a different device's key — not this host's own login — is a separate step: see Devices & pairing.
Health checks
Doctor & status
Both sides speak the same two verbs. On the host, claweed doctor [--fix] [--yes]
diagnoses the deployment and repairs service-user-owned bits (--yes skips consent
prompts); claweed status is its read-only alias — same report, the same offer to
re-read as root what an unprivileged run could not, and no --fix.
claweed restart restarts the managed service. Run as an ordinary user on a host whose
service is root's, it offers to re-run itself through the same consented elevation as
doctor (below) once every plain attempt has failed for lack of privilege; without a
terminal it only says why it needs root. Under a guard it skips signalling the payload's pid, so a failure for lack of root goes
straight to that same consented offer. After any restart on a host with a boot unit it waits briefly
(up to 10s) for the payload, and the guard if the unit runs one, to report the installed build, exits
non-zero naming which one if not, and offers the elevation once more when a plain restart came back stale. The report's rows include the local link,
shared uploads and — on macOS — the aqua host, and
registered names the running daemon's own record of its register link.
On the client, clawee doctor [--fix] [--yes] diagnoses the local setup and always
prints the clawee + claweed versions — even when nothing connects; clawee status
probes the chain hop-by-hop (socket → daemon → relay → claweed → auth) and shows exactly where
a connection stops.
claweed doctor, run on the session host, reports the installed daemon version
alongside the version actually running — if they differ (payload, or the guard's digest), the row is a failure that names which and gives claweed restart.
Every row in the report carries one of four marks:
✓The check passed.!Measured, but not a clean pass — a caveat, or a self-reconciling mismatch. Informational: never a failure, and never counted toward an exit code.✗The check failed. Still information, not a verdict on the command: a ✗ row never makes doctor itself exit non-zero — the installer runs it as doctor … || true on exactly that contract.?Not measured — this run could not read the input. Not a fault.
? joined the other three in claweed v0.2.12, splitting the unmeasured rows out
of the caveat mark (printed ⚠ then, ! now — the report prints no emoji). The common one is the register socket, which is 0600 and
root-owned by design, so an unprivileged doctor can never read it — that is the
design working, not a defect. A report that rendered a ? prints the legend once,
directly under the check rows and above the operational and versions sections,
so you meet it in the same glance as the mark; a report with none of them omits it:
? register socket /…/register.sock: needs root to read (0600 root-owned by design) — not a fault; re-run as root to confirm ? = not measured — this run could not read the input; not a fault
The other ? you can meet is the data dir row, when no data dir resolved at
all — doctor under sudo with no --data-dir, or a login with no
resolvable home. It prints the resolver's own reason and the flag that names a tree, and the
tenants and sessions rows, which read the same root, point at it. Through
v0.2.13 that state was mis-read as a missing directory: doctor --fix tried to
mkdir an empty path and setup ended with
data dir missing; create failed: mkdir : no such file or directory over a host with
nothing wrong.
? data dir none resolved — running under sudo, so the euid default would be root's own tree, not yours (sudo from uid 501): …; pass --data-dir <dir> to inspect one deployment ? tenants no data root resolved (see the data dir row) — enrollment was not measured ? sessions no data root resolved (see the data dir row) — no session record was read
Maintenance
Update
Both components self-update in place from the signed release channel:
$ clawee update # shows the version gap; skips if current $ clawee update --auto # install the update, unattended $ clawee update --dry # report only $ clawee update --force # reinstall the latest regardless $ claweed update # same, for the daemon $ claweed update --auto # apply unattended; the installer restarts the service only if changed $ claweed setup # first run: doctor --fix, then tenant status + knock/approve guidance (see Setup)
claweed update never restarts the service itself: the installer restarts it, swaps the
payload, or — when the unit runs claweed-guard — lets the guard move itself onto the new
binary (a SIGHUP; the guard exits for the supervisor to start the installed build). Afterwards, and
also when the release was already current, update waits briefly for the service to report
the installed build (the payload's running version and, under a guard, the guard's own digest). If
it does not, or the unit failed to start, update and the installer exit non-zero, naming
payload or guard and claweed restart. The first update onto a guard that can move itself is
still a full service restart; later guard changes need no restart command.
Live sessions: a guard change ends them the way a unit restart does on Linux (the unit's cgroup is stopped); on macOS the sessions survive.
<version> means assume this host is below <version>,
so every migration rung targeting that version or newer is selected — the recorded
anchor is not read and the per-rung version gate is bypassed. Rungs targeting anything older
are skipped as genuinely done. That inclusiveness is what the normal ladder gate can't express:
it skips a rung targeting exactly the named line, so a same-version backfill never ran. It
installs nothing and records nothing, and refuses only a floor newer than the ladder's newest
target:
$ curl -fsSL https://release.clawee.org/claweed/upgrade.sh | sh -s -- <version>
Operations
Service unit
The daemon installs itself as a managed boot unit — a launchd .plist on macOS, a
systemd .service on Linux. claweed service print renders that unit to
stdout, given the same paths and run-as identity the installer already resolved — useful for
inspecting what got installed, or for a manual install on a box the installer can't reach
directly. --no-local renders a unit that binds no local link.
On macOS, claweed service print-aqua --host <path> renders the LaunchAgent
.plist of the aqua session host — what runs a session created with
clawee sessions new --aqua inside the gateway user's desktop login session instead of
the system one, for work that needs the login keychain or a GUI prompt. That user must be logged in.
Operations
Local link & shared uploads
claweed serves a local link — a unix socket that a clawee on the same machine
reaches without burrowee (the local box). The setting is persisted in the config root
and survives updates; changing it needs root.
claweed local on|off|statusServe the local link, or not (off creates the config root's no-local marker); status is readable without root.claweed socketPrint the local-link socket path (readable without root).claweed serve --no-localRun without the local link; --socket now names the local-link socket path.
Several OS users on one host can share uploads: with sharing on, each user's
uploads tree moves under one root-owned shared root, one sub-tree per user —
readable by every account on the host, writable only by its owner.
claweed shared-uploads on [--root <dir>]Create the shared root and write the config root's marker naming it (root).claweed shared-uploads offRemove the marker; files under the shared root stay where they are.claweed shared-uploads statusReport which, the root and its owner and mode — readable without root.Operations
Tenant management
claweed tenant manages your own tenant enrollment — the enrollment
claweed setup reports on but no longer creates for you, and the same one
claweed auth / claweed tenant add sugar over for a second device
(see Devices & pairing).
clawee writes config.json
or a session's meta.json in the multi-user schema, an older clawee binary
refuses the file outright rather than silently misreading it — there is no downgrade path.
Rolling a fleet back means rolling its config back with it. On the daemon side, the instance id
lives in the system config root (moved there by the v0.2.0 migration ladder) and is an
auth-challenge input: a regenerated value silently refuses every already-enrolled device.
claweed never generates one on its own — only the installer's migration adopts it —
so treat that file as load-bearing, not disposable.
Reference
Troubleshooting
Quick diagnostic rows, condensed from doctor/status on both sides:
clawee doctor [--fix] [--yes]Diagnoses the local setup and repairs what it can, and always prints the clawee + claweed versions — even when nothing connects.claweed doctorRun on the session host — reports the installed daemon version alongside the version actually running; if they differ the row fails, naming payload or guard and claweed restart.clawee status / claweed statusRead-only aliases of doctor — same report, no --fix.claweed restartRestart the managed service — offering the consented elevation when it needs root.clawee statusProbes the chain hop-by-hop (socket → daemon → relay → claweed → auth) and shows exactly where a connection stops.claweed setupFirst run for the invoking user: doctor --fix, then that user's tenant status with knock/approve guidance. It enrols no keys itself.claweed policy allow-uid <uid>Permits one uid to host sessions on this box — appends it to the root-owned spawn-allow list, creating it when absent. This is the write doctor --fix elevates. The list is an opt-in restriction: with no file present every uid is permitted, so on a host without one the first run narrows it to just that uid.Reference
CLI reference
Verbatim help pages for the daemon-operations verbs, generated by claweed docs from the binary's own help renderer.
claweed setup
first-time setup for the invoking user — doctor --fix, then tenant status + knock/approve guidance
claweed setup — first-time setup for the invoking user — doctor --fix, then tenant status + knock/approve guidance Usage: claweed setup
claweed doctor
diagnose the deployment; --fix repairs service-user-owned bits
claweed doctor — diagnose the deployment; --fix repairs service-user-owned bits
Usage:
claweed doctor [flags]
doctor
[--allowlist <path>] spawn-allow uid list path
[--data-dir <dir>] data dir (default: installed boot unit)
[--fix] repair failed checks (asks consent)
[--register-socket <path>] gateway register socket path (default: installed boot
unit)
[--service-home <dir>] service home dir (see --service-uid)
[--service-uid <uid>] service uid (default: the --data-dir owner; set by a
root re-read)
[--service-user <user>] service user (see --service-uid)
[--sessions-verdict <verdict>] relayed sessions verdict (root re-read; display only)
[--socket <path>] local-link unix socket path (default: installed boot
unit)
[--system-uid-allowlist <path>] system-uid opt-in path
[--yes] skip consent prompts (unattended --fix)
claweed status
alias of doctor (read-only) — same report, no --fix
claweed status — alias of doctor (read-only) — same report, no --fix
Usage:
claweed status [flags]
status
[--data-dir <dir>] service data dir (default: installed boot unit, then the
tree of the user this run is for — under sudo the human who
elevated, otherwise this account (root's own included))
[--register-socket <path>] gateway register socket path (default: installed boot unit)
[--socket <path>] local-link unix socket path (default: installed boot unit)
claweed update
update claweed from release.clawee.org
claweed update — update claweed from release.clawee.org
Usage:
claweed update [flags]
update
[--auto] apply unattended; restart only if changed
[--dry] print the update plan only, then exit
[--force] reinstall everything (binary + spawn policy + boot-unit) + restart
claweed service print
render the boot unit — a launchd .plist / systemd .service — to stdout
claweed service print — render the boot unit — a launchd .plist / systemd .service — to stdout
Usage:
claweed service print [flags]
print
[--claweed <path>] path of the binary the unit execs: claweed-guard, or claweed
for a kit without one (required)
[--group <group>] run-as group (optional)
[--home <dir>] service user's home dir — the unit's HOME (always required)
[--kind <kind>] unit kind: launchd | systemd (required)
[--log <path>] launchd stderr/stdout log path (optional)
[--no-local] render serve --no-local: bind no local link (claweed local
off is the persisted setting)
[--register-socket <path>] claweed --register-socket path (production)
[--root-scheme] root daemon, no run-as user (excludes --user/--group; --home
still required)
[--socket <path>] claweed --socket path (dev)
[--user <user>] run-as user (required unless --root-scheme)
claweed service print-aqua
render the aqua session host's LaunchAgent .plist (macOS) to stdout
claweed service print-aqua — render the aqua session host's LaunchAgent .plist (macOS) to stdout
Usage:
claweed service print-aqua [flags]
print-aqua
[--host <path>] aqua host binary path — the installed host beside claweed
(required)
claweed tenant
manage YOUR OWN tenant enrollment
claweed tenant — manage YOUR OWN tenant enrollment
Usage:
claweed tenant <command>
Commands:
add enroll a trust key for YOURSELF
<pubkey> the public key line itself
@<file> read the public key from the named file
[--data-dir <dir>] claweed data dir (required; `claweed auth` resolves a default)
claweed policy
host spawn policy (root-owned)
claweed policy — host spawn policy (root-owned)
Usage:
claweed policy <command>
Commands:
allow-uid permit one uid to spawn sessions on this host
<uid>
[--allowlist <path>] spawn-allow uid list path
claweed policy allow-uid
permit one uid to spawn sessions on this host
claweed policy allow-uid — permit one uid to spawn sessions on this host
Usage:
claweed policy allow-uid <uid> [flags]
allow-uid
<uid>
[--allowlist <path>] spawn-allow uid list path
claweed local
serve the local link on this host, or not (persisted; root to change)
claweed local — serve the local link on this host, or not (persisted; root to change)
Usage:
claweed local <on|off|status>
local
on serve the local link (removes the config root's no-local marker)
off do not serve the local link (creates the marker; survives updates)
status report which — readable without root
claweed socket
print the local-link socket path (readable without root)
claweed socket — print the local-link socket path (readable without root) Usage: claweed socket
claweed shared-uploads
share every tenant's uploads under one root-owned tree (persisted; root to change)
claweed shared-uploads — share every tenant's uploads under one root-owned tree (persisted; root to change)
Usage:
claweed shared-uploads <on|off|status> [flags]
shared-uploads
on create the shared root and write the config root's marker naming
it
off remove the marker; files under the shared root stay where they
are
status report which, the root and its owner/mode — readable without
root
[--root <dir>] the shared root dir (with on)
See the full generated references: clawee CLI reference · claweed CLI reference.
Docs synced 2026-10-17 against clawee v0.3.46 · claweed v0.3.29 — a partial pass over the guard-aware update and restart, the tab title that shows a session asking for you, and the installer exit codes, on top of the v0.3.42 catch-up. See docs_sync.json for what is not covered.