Choose What It Can Reach
The programs a sandboxed program runs, the hosts, machines and models it
reaches, the tools it calls and the pages it opens, each a flag of lnk sandbox run and a key of a spec (Sandbox).
Choose the programs it may run
With a spec, nothing runs that [commands] allow doesn't list, and the
program given to run must be on it, or the run is refused before it
starts. Without a spec, any program runs unless --command names them.
# every program it runs, to add
lnk sandbox learn --spec app.toml -- npm test
lnk sandbox run --write . --command sh --command git -- sh -c 'git status'
# only what the spec allows in its commands
lnk sandbox run --spec app.toml -- npm testA program refused
A program started from inside that isn't listed gets "Permission denied", and the sandbox says which:
lnk sandbox: refused /usr/lib/git-core/git-remote-https: add it to [commands] allowlearn
learn runs the command once under the spec's other grants with every
program open, and prints the entries to add to [commands] allow. It
writes nothing. A helper a program starts only now and then is found
only if this run starts it.
An entry
An entry is a name found on PATH, a path, a folder ending in / for
every program in it, or * for any. A script needs itself and what it
starts with, so #!/usr/bin/env node needs env and node. An entry
in a folder it may write, or in /tmp, is refused.
An interpreter
Listing an interpreter such as sh, node or python3 lets it run any
code it's given. The list limits which programs run, not what they do.
What it needs
On Linux this needs Landlock, which is in Linux 5.13 and newer. On
macOS, naming a refused program and learn need an admin user, because
they read what Seatbelt reports in the system log.
Give it the internet through Link's proxy
The program gets the internet without the rest of your network. Its
only way out is Link's filtering proxy, set as its HTTPS_PROXY, which
refuses this machine, your network and a cloud's metadata service. Every
agent with [internet] runs this way, unless it has [network] mode = "host".
lnk sandbox run --internet-host '*' -- python3 fetch.py
lnk sandbox run --internet-host api.anthropic.com --internet-host '*.telegram.org' -- ./bot
lnk sandbox run --internet-host github.com --tcp github.com:22 --write . -- git clone git@github.com:me/repo.git
# mongodb+srv://, Linux
lnk sandbox run --internet-host '*.abc.mongodb.net' --tcp '*.abc.mongodb.net:27017' -- node app.js
lnk sandbox run --internet-host '*' --upstream socks5://127.0.0.1:1080 -- curl -sI https://example.com--internet-host and --tcp
--internet-host names a host it may reach, or * for any, and
*.telegram.org covers the names under it but not telegram.org
itself. --tcp <host:port> opens an endpoint beyond HTTPS on 443 and
HTTP on 80, such as github.com:22 for git over SSH, and its host must
be one it may reach.
--upstream and --per-minute
They change where its traffic leaves from and how many calls it may
make. The upstream is direct, a SOCKS5 proxy
(socks5://127.0.0.1:<port>, or socks5://<a socket's absolute path>), or an HTTP proxy on this machine's loopback
(http://127.0.0.1:<port>).
An HTTP proxy upstream
It's for a machine whose traffic leaves only through a filtering proxy of its own network: that one is handed each host's name, which it resolves, so this machine looks up no name the sandbox asks for.
A program that ignores HTTPS_PROXY
It has no network. Python follows it, and Node gets
NODE_USE_ENV_PROXY=1. Git over SSH works once its endpoint is named
with --tcp, using the sandbox's own key in its ~/.ssh.
Names on Linux
On Linux the sandbox also gets DNS from its proxy, so a --tcp host
with a port from 1024 is reached by its name, and psql "host=db.example.com" and mongodb+srv:// work as they are (its
DNS).
Other endpoints, and a Mac
Other endpoints, and every one on a Mac, are reached only by a client
that takes a ProxyCommand, like ssh. On a Mac, psql and database
drivers can't reach a --tcp endpoint.
Reach your network
--lan-host lets it reach machines of your network on any port, through
its proxy. It takes a name, an address, a range, or * for the private
ranges, and your network is reached from here whatever --upstream
says. Security says which
addresses a host reaches, and what it never reaches whatever is listed.
lnk sandbox run --lan-host nas.local -- curl http://nas.local:5000/
lnk sandbox run --lan-host 192.168.1.0/24 --internet-host '*' -- ./syncLet it use a model
--model <port> makes a local model runtime such as Ollama, LM Studio
or llama.cpp reachable for inference only. That means chat, generation,
embeddings and the models' list, and pulling, creating or deleting a
model gets a 403. On Linux the program keeps the runtime's address. On
macOS it reaches the runtime at $LNK_LOCAL_MODEL_<port>, on its proxy.
# Ollama, inference only
lnk sandbox run --model 11434 --write . -- python3 chat.pyA hosted model's key, or a secret
The proxy can also add a hosted model's key, or a service's secret such
as a Telegram bot's token, so the program never holds it: each is a
route of the proxy's, served outside the sandbox by the plugin it's for
(lnk-model, lnk-secret), as Security
explains.
For an agent
lnk agent secret wire sets this up for an agent, and every agent on Link
Harness, or on a harness whose adapter says it takes the proxy's
address, has it by default.
Calling it
The program calls http://127.0.0.1:<proxy port>/_link/model with a
placeholder key, and adds /v1 for OpenAI and OpenRouter. The proxy
port is 40781 on Linux. It works with Anthropic, OpenAI and
OpenRouter. A service's secret is reached the same way, as Security
explains.
Call a tool that runs outside
A tool is an MCP server that runs outside the sandbox, with your logins,
desktop and SSH agent, or a server at a URL reached from outside with
its headers, which no sandbox sees. Use one for what the sandbox keeps
out, such as gh or aws with your credentials. A sandbox reaches only
the tools it names, at $LNK_TOOL_<NAME>, so $LNK_TOOL_GH for gh.
lnk sandbox tool add gh --allow list_issues --allow get_issue -- github-mcp-server stdio
lnk sandbox tool add docs --url https://example.com/mcp --header-file docs.headers # a file of its Authorization header
# one server for every sandbox naming it
lnk sandbox tool add notes --shared -- notes-mcp serve
lnk sandbox tool calls gh # its calls
lnk sandbox tool list # every tool, and which calls ask
lnk sandbox run --tool gh -- my-agent
lnk sandbox tool remove ghAsked first
Every call asks you first, except those you --allow by name, and
--ask none asks for none. Removing a tool ends its sessions.
--shared
A tool with --shared runs one server for every session, where any
other starts one each, and stops once none has been open for the
sandbox's shared_idle: a team's memory is one.
Open a page in your browser
With --browser-host, a program can open an https:// page in your
browser, such as a login, on the hosts given (* for any). Every page
asks you first and shows its whole URL, because it opens with your
cookies and logins. $BROWSER in the sandbox does it, so Python's
webbrowser, gh and xdg-open work as they are.
# asks you, then opens it
lnk sandbox run --browser-host github.com -- python3 -m webbrowser https://github.com/loginA login's answer
A login that answers on localhost (its redirect_uri) gets that port
until the answer arrives, or for five minutes at most. With no desktop,
such as on a box, nothing opens, and the program's own fallback, a URL
to copy or a code to type, still works.