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-tool

How 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.