Developing
lnk-file-tool is Link File: an MCP server over stdio that reads and
writes an agent's files, its calls in a sandbox. How to use it: Link File.
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-file-tool
# the sandboxed test skips without bubblewrap, unless LNK_REQUIRE_SANDBOX=1
cargo test -p link-plugin-file-toolHow it's put together
serve [--no-find] [--no-diffs] -- <command>... is the tool, its flags
leaving find, or each answer's diff, out of what it lists, says and
answers (Offer, src/offer.rs).
Its sandboxed process
It touches no file: it starts <command> at the first call, which Link
makes lnk sandbox run with the sandbox file's flags, then
lnk-file-tool apply --files <folder> --agent-folder <folder> --write <folder>..., and keeps it for the session (Sandboxed,
src/sandboxed.rs). A sandbox costs tens of milliseconds and six
processes to start, a call in a running one about a millisecond.
Each call
Each call is a line on its stdin, {"name", "arguments"}, and its
answer a line back, {"ok": ...} or {"error": "..."}. One that ends
is started again at the next call; one that never starts says what it
wrote on stderr.
apply
It checks the folders before the sandbox does, so a refusal says why in
words: a path is relative, without .., and never through a link, and
a write is in a folder --write names, or, with --write ., anywhere
but chats/ and the folder itself.
A write
A write goes to a file beside the target, then is moved in place.
write and replace answer with the change from
link_plugin::diff::unified as diff, from the text the file held, or
/dev/null for a file made; past 16 KB, or where the file held wasn't
text, diff_cut and the new text's sha256 instead.
find
It builds one Query from its arguments, for a walk on disk and
through the bridge alike: name a glob made a regular expression, any
case, matched against a file's name, or its path below the folder
searched when the glob holds /; text a regular expression of a
line. Each is compiled to at most 1 MiB, so a hostile pattern is
refused.
tests/calls.rs
It runs the tool as apply alone, a session's calls in one, and on
Linux under lnk-sandbox run from the core, told it may write a folder
the sandbox doesn't, to show the sandbox holds.
Through the files bridge
bridge --socket <path> --as <agent id> --owner <owner> is the tool where
Home is kept in a bucket: each call goes to the files bridge
(link_store_contract::files), proved with the agent's key from the
keyring (--keyring, else this machine's). tests/bridged.rs runs it
against a stand-in bridge over an encrypted folder.
The version a write names
A write names the version of the file it last read or wrote
(Expect::Version, kept by path in a Seen for the session), so a
file changed since is refused; of a file it never saw, the version it
reads first, or Absent for a new file. A replace names the version
it reads.
Paging a read
A read from line 1 keeps the file's text in the Seen (one file, the
last so read); a read from a later line pages that text, so a 4 MiB
file's pages read it once, not once a page, and a write or replace
of it forgets it.
find and teams
find lists the folder through the bridge and reads each file of at
most 1 MiB. A path under teams/<team>/, or teams/<account>/<team>/
(an account's ID is never a team's name), goes to that team's folder
(TeamRef, with read_in, write_in and list_in): the bridge says
at each call what of it the agent reaches.