Developing
lnk-link-run is Link Run: an MCP server over stdio that runs an
agent's programs, each call in a sandbox of its own. How to use it:
Link Run.
Build and test
From tools/, in the public workspace beside the core (LINK_CORE
for a core in another place):
cargo build -p link-plugin-link-run
# the sandboxed test skips without bubblewrap, unless LNK_REQUIRE_SANDBOX=1
cargo test -p link-plugin-link-runHow it's put together
serve --files <folder> --programs-path <PATH> --timeout <duration> --output <bytes> --program <program>... -- <sandbox>... is the tool, and Link
gives it everything at a start.
What Link gives it
The programs of the sandbox file's [commands] this machine has, the
spec's [tool.run], and the sandbox, lnk sandbox run with the flags
the sandbox file makes. No number or grant is the program's own, and
with no sandbox after -- it refuses to start.
A call
A call runs <sandbox> -- <program> <args>, or with --var NAME=VALUE
given, <sandbox> -- /usr/bin/env NAME=VALUE... <program> <args>,
since the sandbox clears the environment: Link gives an agent with a
desktop
its DISPLAY and XAUTHORITY so.
Its program and process group
The program is found on --programs-path, in the files folder or a folder of
it, in a process group of its own. At its timeout the group is killed,
and once the program exits, what it left running is too, a second
later.
Its output
Each of its output and its errors keeps its start and its end, half of
--output each, and how much there was. The answer's text holds them
once; its structured value has the exit, the sizes and the time, not
the text again. Calls on one session run at once, each on a thread of
its own.
tests/command.rs
It runs the tool without a sandbox, and on Linux under lnk-sandbox run from the core.
Background commands
src/background.rs. With --state (and --way-in, --as <agent id>
with --keyring <dir> or Link's keyring, --background,
--background-timeout, --background-output, --background-keep,
--hops), serve offers start, list, read and stop too.
start
It checks the call as command does, then, under a lock on
<state>/start.lock, counts what runs, writes the record
(<state>/<id>.json, outside the files folder: no sandbox writes it)
and starts lnk-link-run supervise <id> --state <state>. The conversation and
hops come from the call's _meta (link.local/thread,
link.local/hops): without a conversation, or at --hops, it's
refused.
The record's lock
start takes the record's lock (<id>.lock, flock) first and hands
it to the supervisor as its input, so no sweep ever sees it free while
it starts.
supervise
It holds the lock for its whole life, which the kernel releases however
it dies. It runs the sandbox in a process group of its own, with
PR_SET_PDEATHSIG on Linux, so the sandbox, which bubblewrap ends with
it, dies with the supervisor.
gate
The sandbox starts through lnk-link-run gate, which waits on a pipe
until the record names its group, then becomes the sandbox, the same
process: a supervisor killed before then leaves nothing running that a
sweep can't find.
Its output file
Its output goes to background/<id>.out, opened with openat and
never through a link. Past --background-output, the newest half is
copied beside and moved in place, and the record counts what was
dropped, for the file it names (its inode): a read that finds another
file, cut since, loads the record again, so it never reads at the old
offsets.
Its end
It stops the group at its timeout or on <id>.stop, records the end,
then hands the bridge's way in a message from run:<id>, a bridge's
call proved as its agent (--as) with the agent's key from the
keyring, read then. The record is marked told before, and unmarked only
when the way in can't be reached. SIGTERM, SIGINT and SIGHUP, the
agent's service stopping, end it lost, untold.
sweep <state>
Run by the bridge as it starts and by serve at each start, it takes
each record's lock. A free one means its supervisor is gone. A running
record is then marked lost, and what's left of its group is killed when
it's surely its own: the same boot, and its leader the one recorded or
gone. Ended records are told, tried again for ten minutes, and those
told past their keep are removed with their output.
stop --state <dir> and remove --state <dir>
stop (lnk agent stop) and remove (lnk agent delete, a move)
stop what runs, and remove then removes everything.
tests/background.rs
It runs them as real processes: outliving the call and its server,
told once through a stand-in way in, a stop leaving nothing, a
supervisor killed or sent SIGTERM, the caps, the keep, and on Linux all
of it under lnk-sandbox run.