Command overview
Some agents run their hooks in a sandbox that confines writes to the session workspace: DeepSeek Harness inworkspace-write, Gemini CLI and Qwen Code in a
container sandbox, or an operator’s own sandbox that mounts nothing of Beacon’s.
A hook there cannot reach the ingest socket, the runtime log, or the account
spool. Instead of dropping the event, it stages it in the workspace spool:
.gitignore inside
.beacon/spool/ keeps it out of git add, whatever the repository’s layout.
Beacon later drains it into the runtime log with the harness.collection_method=hook
label the event was captured under. Hooks never stage:
- actions only Beacon writes, such as an auto-share request, which are reported as not recorded, as before;
- as root in a workspace another account owns, such as a rootful container over your checkout: you could neither drain nor delete root’s files. Run the container as your own uid, or mount the ingest socket. Root that owns the workspace (a rootless container) stages as usual;
- in a working directory that exists only on the sandbox’s own root filesystem, where the spool would disappear with the sandbox;
- next to a
.beacon/spool/.gitignorethat does not ignore everything there. A repository can commit its own to switch the protection off; Beacon leaves it alone and stages nothing beside it.
beacon endpoint dsh sync finds its sessions through its
own session store.
Command syntax
Commands
Flags
When staged events are drained
Staged events reach the runtime log in one of three ways:- The user-mode sweep. The scheduled session sync and the install backfill drain every spool they find.
beacon endpoint spool drain, run by hand or from your own scheduler.- The next hook of the same session that gets through, for example once the session leaves the sandbox, delivers what that session staged first.
Drain spools
drain refuses to run as root: run it as the account that owns the workspace.
It appends through the ingest socket like a hook, so an ordinary account does
not need write access to the system log. If that account cannot read the system
log, Beacon cannot list the sessions it has recorded; drain prints a note and
still drains configured roots and --workspace. If it cannot read the system
config.json (root-only when it holds a destination secret), Beacon does not
know which log to write: drain asks for --log-path, and status lists only
--workspace spools.
status lists each session with staged bytes: workspace, harness, session, and
size. beacon endpoint doctor reports
the same state as workspace_spool_pending_drain.
Security model
Anything running in a workspace can write to it, so Beacon treats every staged line as untrusted:- Lines are checked one by one. A line is appended only if it is a valid
hook or plugin event from Beacon, for the harness and session whose spool
holds it. Actions only Beacon writes, such as
session.share_requested, are refused. Everything else is counted as rejected and removed. Each admitted line is written again through Beacon’s own encoder, so it is redacted and size-capped like any other event. - Only known sessions are drained. Beacon drains a spool only for a session
it has already recorded in the runtime log, with that workspace as its working
directory. A cloned repository that carries a planted
.beacon/spool/<harness>/<id>/is never drained. - Configured roots are vouched for by the operator. Sessions under
workspace_spool_rootsor a--workspaceare drained even if Beacon has not recorded them, because the operator named those directories. Add--harnessand--sessionto vouch for one session only. - Links are never followed. Every directory below the workspace must be a
real directory and
pending.jsonla regular file, so a sandboxed agent cannot plant its session directory as a link to the account’s own spool and have a drain outside the sandbox empty it. Apending.jsonllarger than a spool can grow (8 MiB) was written by hand and is left unread, so it cannot tie up a sweep. - Root never drains for a user. Neither the system-mode sweep nor
beacon endpoint spool draindrains as root.
Configuring roots
For sandboxes whose sessions Beacon has not recorded yet, such as a container sandbox where no hook ever reached the log, list the workspaces inworkspace_spool_roots in the endpoint config.json. Each entry is a glob of
workspace directories:
config.json
<workspace>/.beacon/spool/<harness>/<session>/ under a matching
directory is drained.
DeepSeek Harness
DeepSeek Harness hooks stage in.beacon/spool/deepseek_harness/<session>/.
Spools an earlier hook left in .beacon/dsh-spool/<session>/ are still drained.
beacon endpoint dsh sync drains them too, and doctor
reports them as dsh_spool_pending_drain.