Decisions

Why agents work the way they do.

Harnesses

Link Harness's own are on its Decisions page.

Which harness is the default?

Link Harness: you install only what you need, and OpenClaw is a large install someone else wrote, most of the minutes before a first answer. Link Harness installs nothing from a third party. OpenClaw, Hermes and DeepSeek Harness stay a lnk agent use away, and a box installs Link Harness too. An agent whose settings name no harness but that has a model keeps OpenClaw, the default it started with, and start saves the harness, so a new default never switches an agent. Changing it is one line in lnk-agent.

So the smallest engine does the whole job: it answers you on your phone and remembers what you said. It is one more harness beside OpenClaw and Hermes, never part of the core, in a repository of its own. Every harness, Link's and those from outside, is proved against the conformance kit in the core's tests, which the core's own small fixture harness passes too. It's a router in Rust: it runs no code, holds no keys, and keeps nothing between turns, so an idle agent costs nothing and a restart loses nothing. Its tools run outside it.

It grows through tools

It grows mostly through tools: almost every new capability is a tool, not code in Link Harness. Its runtime adds only what a tool can't be: an event that starts a turn, an answer no request waits for, a turn that waits on an event, and what a message does mid-turn. Everything else is a tool in Link's tools.

Declared, not discovered

What an agent can do is declared, not discovered. Its spec and its sandbox file decide it when the agent is configured, and the prompt and the tools then stay the same from turn to turn, so the provider's cache holds.

What's in it

Schedules, subagents, notes the agent writes, skills, web and compaction are in Link Harness because without them it can't stand in for OpenClaw or Hermes, and those are what people use agents for.

Why are OpenClaw and Hermes adapters in repositories of their own?

Because Link works on Link Harness, and supports any other harness the way it supports any model: through a contract. On one machine OpenClaw takes about 90 seconds to answer after a start, needs the network as it is (--network-mode host) for its Slack and Discord sockets and, with OpenAI, runs a program no sandbox can run; Hermes answers fast but takes about ten minutes to install. Link Harness answers faster than either with nothing beyond Link's defaults.

An adapter like anyone's

OpenClaw, Hermes and DeepSeek Harness are each an adapter in its own repository, riding the same adapter contract as Link Harness, the way anyone's adapter would, and its issues go there. Link's release builds and signs them (below).

So that one install gives you all four harnesses: lnk agent use --harness hermes works with nothing to add or trust first. Link's release builds the three adapters from their repositories, at the core's version, and signs them with Link's key, as it does its own plugins; lnk upgrade updates them with Link.

Where they're named

They're named only in the core's plugin lists (PLUGINS, needs) and the harness plugin's KNOWN, besides what it keeps for agents, so what's specific to each is still its adapter's answer through the contract. A harness Link doesn't ship comes from its own repository.

How do the core and an outside adapter keep working together?

Through a versioned contract. They release apart, so a newer lnk-agent must run an older adapter and the other way round. The adapter says which version it speaks (lnk-<name> contract), and each change is additive, as in the relay's protocol: a new command with what lnk-agent assumes when an adapter doesn't answer it, or a new field with a default. What one harness needs, such as how long it takes to answer after a start or whether it takes the proxy's address, is the adapter's answer, never a name in the core.

How does a change to the contract land?

In link_harness_contract::harness first, as an addition an older side can follow. The mini harness answers it, the conformance kit checks it, and then the harnesses take it up, each passing the kit. Whatever crosses repositories is in the contract, never in one harness's code.

Which release of a harness runs?

The one its adapter pins. It's installed by the harness's own installer, fetched from its repository at that release's commit and run only if its SHA-256 matches, the way the bucket plugin pins rclone. The installer is the harness's to trust, but the one that runs is always the one Link checked. Moving to a new release is a Link release.

Where is a harness installed?

In a home of its own, ~/Link/Agents/<agent>/Harnesses/<name>, which is its HOME. Its installer, its configuration and the harness itself run there, inside the sandbox, so nothing spreads across the machine. Left alone on a Mac, a harness writes ~/.openclaw, ~/.local/bin and a LaunchAgent of its own. lnk agent harness show lists the home and any traces outside it, and lnk agent harness delete --harness <harness> deletes both. Shared files live in the agent's Home.

Does every start configure the harness again?

Only when something changed. Each harness CLI run is a Node or Python start, and a full configure is ten or more of them.

.lnk/configured

OpenClaw's and Hermes's adapters keep a hash in the home, .lnk/configured. It covers the settings they were given (OpenClaw's, those its configure reads: not the spec, what Link knows of the model, or the environment, so a teammate added doesn't configure it again), the adapter's and the harness's releases, the adapter program itself (a rebuilt one keeps its version), the agent's ports, and the config and .env they wrote.

When it's configured again

When it all matches, configure does nothing. A harness that edits its own config, or a home that moved without its .env, is configured again, so Link's settings still win at the next start. OpenClaw's doctor --fix runs once per install of a release, when it succeeds.

How does a harness find its tools?

In its own MCP settings: each tool the agent may call, and its memory, is handed to the adapter by name and address on the harness's proxy, and the adapter adds it the way that harness takes an MCP server over HTTP. OpenClaw's mcp.servers and Hermes's mcp_servers then list it with the servers you added, and a harness's own tools call it as any other. An adapter removes only the servers it added, recorded in its home, so one you added under another name stays. Link Harness reads the same addresses from its environment.

Agents

How an agent is named, owned, given its ports and its folder, and which model it starts with.

How are agents and machines named?

Each has a name you type and a random ID, kept in machine.toml and the agent's settings. An agent's ID is 12 random characters that never change. Names are what people say; IDs keep two machines' or two users' main apart, with no registry to hand out numbers. The first lnk agent start makes main: most people have one agent and never name it. No agent or box is named here, home or all, Link's own words where a command takes a name, so lnk agent move --here always means this machine; a computer named one of them before keeps its name.

What is named after the agent?

What people see by its name: its folder, its settings file, its log and its rules files.

Keyed by its ID

What runs is keyed by its ID, which no other owner's agent shares, so two owners' main never meet even on one machine: its service (link.agent.<id>, lnk-agent-<id>.service), the service carrying its connections (link.bridge.<id>, lnk-bridge-<id>.service, unless one bridge carries every agent's), its memory's and its scheduler's tools (memory-<id>, scheduler-<id>) and its proxy's sockets (~/.lnk/agents/.net/<id>). Nothing of one agent is shared with another. A harness can't claim any of Link's services as its own: every name starting link. or lnk- is refused.

Where the ID is read

The ID for these names is read from the agent's settings file alone, as it is: a service's name is asked for at any moment, and loading an agent's settings moves old sections and writes the file. An agent from before IDs is keyed by its name.

Where does the truth about an agent live?

On the machine running it, and nowhere else. Every Link is equal, so a list of agents is asked for and never kept, and losing one machine loses only what ran there. A computer that moved an agent to a box keeps only a note, so the machine holding the state is the only one that makes its ID, and migrating both ends would never make two agents with one bot.

Who owns an agent?

Two kinds of owner, each whole, so everything works without signing in. Someone who never signed in owns their agents as the user running Link on the machine, <user>@<machine>, from the machine's own properties, with nothing to set up. The machine's part is its name in machine.toml, made once from its host name and kept, so renaming the host changes no agent's owner.

Signed in

Once signed in (lnk auth login), it's the account's owner ID, acct-<id>: at an agent's next start the account replaces the stand-in, and the agent keeps all it has. It's the start, not lnk auth login, since signing in is the accounts plugin's, which writes no agent's settings.

One owner per agent

Most harnesses assume one person; Link keys each agent by its ID and its owner's, so two people's agents on one machine, or one process, never meet. The owner is in the agent's settings, never taken from a request, and moves with it.

Where it may move

An agent nobody signed in for moves to any machine, signed in or not, and on one signed in it becomes that account's at its next start there; an account's moves only to a machine signed in as that account, so an account's agents never land where anyone else, or nobody in particular, is signed in. One owner per agent; people who share agents are an owner of their own.

How does each agent get its ports?

Through LNK_PORT on every adapter command. Every agent but main gets a block of ports from 20000, as many as its harness says it takes (its contract's ports) and one more, for its proxy on a Mac: two for Link Harness. An adapter that doesn't say gets 200, as every agent did before. Switching harness grows or shrinks the block at its next start, at the same first port when no other agent's block is in the way.

main's ports

main keeps the adapter's defaults, OpenClaw's 18789 and Hermes's 8642. This is the same for every adapter, leaves nothing to remember between commands, and lets a lone agent keep its harness's usual ports. An adapter that doesn't take LNK_PORT runs only main: its ports must be in the agent's block.

A free block

A block is free of the other agents of the user running Link, and of every port a program on the machine holds when it's given; a start makes it free of everything else on the machine: each user's agents allocate from 20000, so where another program already listens on one of its ports, another user's agent say, the start moves the agent, main too, before it starts anything, and says so.

Where it moves

It moves to a free block picked at random below 32768, where the system gives programs ports for their outgoing connections at any moment, so two users' agents moving at once rarely land on the same block.

A program on its ports

A program on the ports would otherwise be taken for part of the agent, or be handed its messages; one that takes a port in the moment before the harness listens leaves the agent unanswered, never answered by that program, since a bridge sends nothing to an endpoint that can't prove it holds the agent's key.

How are agents kept apart?

The sandbox hides ~/Link/Agents from every harness except its own agent's folders, and ~/Link/History, what agents that left kept, even with --read '~', since agents run as one user. Their ports stay reachable wherever localhost is; each harness's own token guards them.

Its folder

Why an agent has two folders and how they're named.

Why is an agent's folder two, Home and Agent?

What's yours and what's the agent's are kept apart: Home/ holds your documents and its work, Agent/ its brain, each collection a folder (instructions/, skills/, memory/, chats/, schedule/). A Home you pick (--files, a repository) no longer gets the agent's conversations and instructions written into it, Agent/ stays put wherever Home is, and each collection kept at file is one folder, so nothing has two ways in. Agent/ is open to you: you edit its instructions/ and skills/ as plain files.

Seen as one

The agent sees both as one, as before: its tools and its sandbox file's [files] name instructions/, skills/ and chats/ beside Home's own folders, and a move carries them so (files/...). One view kept every grant written before meaning what it meant, every move between Links reading the same stream, and every prompt saying the same paths. . is all of Home, no longer the agent's instructions: a repository it works in is Home, and its own folder is named beside it where it may write it.

How does a restore know a backup carries the agent's own folder?

Its buckets say so: each backup labels them lnk-layout=2 through your cloud accounts (lnk bucket label), and a restore reads the label before what the backup folder holds.

Why not the backup folder

The backup folder sits in Home, which the agent writes, so nothing in it can say how it's laid out: a version written into the harness's archive could be removed as easily as Agent.tar itself, and so would say no more than whether Agent.tar is there.

Why the label

The label is set with your accounts, which the agent never holds, and the key a box gets for an agent's bucket reaches its objects alone, never its labels.

Why are the agent's folders' names lowercase?

What the agent reads and writes (instructions/, skills/, chats/, work/) is named for it, in lowercase, and side by side, never one inside another: a sandbox has no read-only folder inside a writable one, so instructions/ can be locked ([files] read) while work/ stays writable. The folders themselves keep their names, Home and Agent, since you browse them. chats/ is never writable to a tool: its harness alone writes it, so nothing rewrites what was said.

Why does [files] hold only for some harnesses?

A harness's sandbox can make Home read only and write only some of its folders only if the harness writes nowhere else there. Link harness writes only chats/ of the agent's own folder, and says so in its contract (folders); a harness from outside Link works in the whole of Home, so its sandbox writes all of it and never reaches the agent's own folder, and a start says your [files] holds only for Link's tools there.

How does a skill reach an agent?

lnk agent skill add copies its folder into the agent's skills/, so a skill is a file of the agent's like any other: it moves with the agent, a backup keeps it, and you can edit or delete it there. It's named as its SKILL.md says, as the Agent Skills standard does, and checked before anything is copied: a folder that isn't a skill is refused, saying why.

From a URL

A URL is a git repository, cloned once with your own git into a private folder of the system's, never the agent's, since skills are shared as repositories, GitHub's page of a folder naming one in it.

How it's copied

A copy leaves out links and anything that isn't a file or folder, takes at most 1,000 files, 64 MB and 1,000 folders at most 32 deep, and is put in place by a rename, so a harness never lists a skill half copied.

The harness writes skills/ while skill add runs outside its sandbox, so every file and folder is read and written through its folder's handle without following a link: one swapped in, in the skill or where it's copied to, is refused, and a repository's folder that's a committed link can't lead out of the clone. A harness that doesn't take Link's instructions doesn't read skills/, and skill add says so.

In the prompt

Skills are listed in the prompt whether or not memory is on, so the prompt stays the same across conversations, a narrowed subagent's included. Memory is opt-in, so skill add says when it's off: the one place it's seen.

Which model does an agent use with no --model?

Link's default for the provider, named, in every harness: claude-haiku-5-5 on Anthropic, gpt-6-luna on OpenAI and anthropic/claude-haiku-5.5 on OpenRouter. Each harness's own default differs (OpenClaw's for OpenAI costs many times Link Harness's), and would leave status saying only "the harness's default". A named model says what it costs, and stays the same when you switch harness.

One price

The three are one price, $0.10 a million tokens in and $0.50 out, so switching provider never multiplies the bill without your saying so. A mid-size model such as Claude Sonnet costs twenty times that, and you're paying, so the default is the cheap one and --model names a larger one. OpenRouter's own openrouter/auto isn't the default: it sends each prompt to a model of its choosing and bills that model's price, which is often a large one.

A hosted key

A start doesn't check a hosted key: that takes a call to the provider, and start and status look only at this machine. They say the agent is started and running, which is its harness answering; a key the provider refuses shows at the first message, as "Your agent couldn't answer" on your phone and the provider's error in lnk agent logs.

Commands

Why the agent is a flag, and a harness's commands a word of their own.

Why is the agent --agent, even for move?

A positional argument names what the verb acts on (the grammar). move acts on a destination, as in lnk agent move --here --agent work. The agent is selected with --agent, which can be omitted when there is only one. create, remove and restore take an agent as their object.

Why are a harness's commands lnk agent harness?

A slot holds Link's words or names you chose, never either (the grammar). lnk agent <harness> allow put a harness's name where Link's commands go, so no harness could be named start or use, and a mistyped command read as a harness.

The agent's harness

lnk agent harness allow --network-mode host acts on the agent's harness, the one you most often mean, and --harness hermes names another, as --agent names another agent. remove takes the harness as its object, since it ends it.

Messages

Which agent a message is for, how many agents share a machine, what a message mid-answer does, and how others' messages, subagents and teams reach an agent.

Which agent is a request for?

The one its Link-Agent: <agent> header names, sent by every bridge on each message and its proof. A header, like the thread, since chat completions has no field for it.

Answered as that agent

An adapter whose contract says agent answers a request as the agent it names, with that agent's key and settings, and one naming none as its own agent's. It refuses one naming an agent it doesn't run before the model sees it, so one process could answer for many agents. Link Harness and the mini harness read it. An adapter that doesn't answers every request as its own agent's, so the contract stays additive. Each agent runs a process of its own, unless the machine shares one (below).

Beside it, Link-Agent-Id: <id>: every owner has a main, so a process answering for more than one owner's agents needs the ID. It's a header of its own, not the ID in Link-Agent, which keeps the name every adapter already reads and which an ID could be taken for. Link Harness keys everything of an agent's (its folders, its settings as last read, its tools' listings and sessions) by its ID and owner, never its name, and a test runs one process for two owners' mains.

Many agents on one machine

The trades a person makes to pack a machine with agents: one harness, one bridge, and an agent that sleeps.

Why is one harness for an owner's agents off by default?

Because it trades a sandbox for code. An idle agent's harness is six processes, its sandbox and the harness in it, of the eight it holds; one harness for all of an owner's agents takes that down to the agent's own proxy.

What it trades

One harness for an owner's agents is one process in one sandbox holding every one of those agents' folders, and which agent a request touches is kept apart by Link Harness and its tests, where a sandbox each kept them apart by the kernel. Each agent's proxy, key and grants stay its own either way.

Whose trade

Giving up a sandbox each is a trade a person makes for their machine, a box packed with agents, say, so it's machine.toml's shared_harness, which no agent's sandbox reaches, and never across two owners. An agent that reaches anything but through its proxy keeps a sandbox of its own, since one network for all couldn't keep its endpoints its own.

Why does no process serve two owners' agents?

Because one owner's agents together are one person's, and two owners' aren't. Memory's service and the harness an owner's agents share each hold every one of those agents' folders, so each is one owner's: nothing asks for one across owners, and offering it would be a second trade on top of the first.

Why is one bridge for every agent off by default?

Because it trades a fault kept to one agent for fewer processes. Each agent's bridge is a service of its own, with a scheduler runner beside it: two processes an agent, idle or not. One bridge for every agent on the machine, machine.toml's shared_bridge, carries them all from one process, with one runner a time zone, since a runner's times are its process's zone.

What it doesn't change

Nothing is less isolated than before: every agent's bridge already runs as you, outside every sandbox, and reads every agent's settings, and each agent still has its own way in, key and connections' bridges.

Whose switch

With one bridge for every agent, a fault in it stops every agent's connections until it's back, where a bridge each stopped one. That's a trade a person makes for their machine, so it's a switch they set, which no agent's sandbox reaches. An idle agent then holds no process of the bridge's, which is what lets one with no turn running hold none at all.

Why one scheduler runner a time zone?

Because a runner's times are its process's time zone, which is set once as it starts, so one process can't hold two. The bridge every agent shares runs one for each zone its agents are in, and hands it the agents as a file it reads again when it changes. An agent taken off it stops only once its firings have ended, so a change of agents never restarts the runner, and no job is fired twice.

Why does the bridge listen on an idle agent's port?

Because something must hear a message to wake an agent that holds nothing. Connections and schedules reach an agent at its endpoint's port, so where one bridge carries every agent, the bridge listens there. On Linux behind its proxy it does so always, in the proxy's place, passing each connection into the sandbox through the proxy's folder, and on every byte it knows the agent is busy, whatever its harness.

Where the harness listens itself

Where the harness listens on the loopback itself, as on a Mac, the bridge can't hold the port while the agent runs, so it holds it only while the agent sleeps and hands it over at the first connection, Link's connections trying a refused connection again for the moment between. Awake, the agent is busy while anything is connected to its port. Only bytes pass either way, and the endpoint proves itself with the agent's key as before.

When it sleeps

It stops once nothing has come for [run] idle_after, no answer is still coming through its door, no turn runs and no background command holds its lock.

What counts as an answer

The door reads how each HTTP message on a connection is framed (its head, then a body by length, in chunks or until the connection ends), never what it says, so an answer that pauses longer than idle_after (a long tool call, a harness closing its tools) is never cut; what it can't frame (a tunnel, an upgrade to another protocol) counts as an answer coming until the connection ends. A connection its client keeps open, idle, between answers isn't. No ceiling cuts one: an answer that never ends, like a turn that never ends, keeps the agent awake.

How long it waits

How long an idle agent waits before it sleeps is an opinion, memory against a start from cold, so a key of the spec, and Link's is five minutes: a person's back-and-forth never waits on a start, and a start after that is too fast to notice.

How fast does an idle agent answer?

Fast enough that a person doesn't notice: its first byte reaches the model within about 200 ms of its message reaching the machine. That's the start of its sandbox and harness, or, where its owner's agents share a harness, of its proxy alone, which is why a sleeping agent stays one the shared harness serves. With Link Memory, an agent woken when every one of its owner's agents has been asleep starts memory's service too, which ends 5 seconds after its last session, and starts again in about a tenth of a second, never waiting on a model server the machine doesn't run.

What ends a process no agent owns?

Its last agent leaving. Memory's service ends 5 seconds after its last session, and the harness an owner's agents share and the bridge every agent shares 20 seconds after their last agent, so an agent started again meanwhile isn't left with none. Each is a service of its own where Link's services run, so stopping the agent that started it doesn't stop it, and in a session of its own elsewhere, which an agent in a terminal starts again when it's gone.

What does a message sent mid-answer do?

What the agent's spec says ([turns] busy), since whether a second message should change the answer, wait, or stop it is an opinion; Link's is to steer, as OpenClaw does. Whatever it does, the message is kept at once, so nothing said is lost to a turn that crashes.

One turn a conversation

One turn runs per conversation, whichever process runs it: a lease in the conversation's folder (turn.json), renewed as often as the spec says ([turns] renew; Link's: every 10 seconds), says who. A lease not renewed for as long as the spec says ([turns] lapse; Link's: a minute), judged to the millisecond, belongs to a process that died: how soon a crash is noticed against how long a slow machine may go without renewing is a trade, so both are keys.

Where the lapse is read

The store reads the lapse from the agent's settings, not from the lease, because a crashed process can't say how long its lease lasts.

A crashed turn

The next to take the conversation finishes its turn first: its tool calls that hadn't answered are answered as not made again, and the model decides whether to call again, so nothing runs twice unseen. A turn that had answered every message before it crashed only has its lease let go: asking again would send a second answer.

Its answer

An answer finished that way at the harness's start was waited for by nobody, so the agent's bridge, outside the sandbox, asks the harness for it (/_link/recover) as it starts and when the harness answers again after it couldn't be reached (it stopped, so it may have crashed), not on a timer that reads every conversation.

Where that answer goes

It sends it where that turn's answer was to go: the bridge keeps, from each message from others until its answer went, where the answer goes (nowhere, the chat, or a subagent's parent, up the chain); an owner's goes to the chat. Asked in two steps, the harness keeps it until the bridge says it was sent, so one lost on the way is handed over again rather than dropped.

One taker

Taking a lapsed lease is one taker's at a time (turn.take), so two processes never both finish a turn. Every event is written once, conditionally on its number, so a turn and a message arriving race safely: the one that loses reads again.

A steered message

A message that steered a running turn is answered with a header and no body (Link-Steered, status 202): its answer comes on the request that started the turn. The bridge marks the message instead, as the spec says ([connection.<name>] steered; Link's: no mark). Link's spec marks no steered message: the answer on the first message already shows it was taken in, and a reaction is your choice.

Steered as the turn ends

A message steered in as the turn ends is answered by the turn, which takes the lease again after letting it go when the message came after its last look, else by the message's own request. So none is left unanswered.

How does a message from someone else reach an agent?

A subagent, a schedule, and a message from another agent look like three features, but they're one: a message for the agent from someone who isn't its owner, now or later. The hard part isn't writing down when; it's something holding the message until it's due and waking the agent then.

One tool and one runner

Since all three are a message from someone else, one tool and one runner do all of it: Link Scheduler, a program of its own in Link's tools, and the core only gains a header (Link-From, with Link-Hops) and the bridge's way in. The bridge already holds the agent's endpoint and key, restarts with the machine and delivers answers to connections, so the runner runs from it, through a driver that tests swap for one in process.

Kept as theirs

A message from someone else is kept as theirs, the model is told who's speaking, and it never counts as the owner's. That every message carries a hop count and every agent a cap is decided; the numbers are the agent's spec's.

An answer nobody waits for

An answer nobody waits for goes only to the owner's own chat (an allowed user's on Telegram and Signal, a direct message on Slack and Discord), or to a group an allowed user talked to the agent in, never another chat the harness names, since a harness could otherwise make the bot message anyone.

Its groups

The groups are kept by the bridge, in the connection's folder, which the harness never reaches, and an agent whose spec never answers in groups sends to none. Signal holds one connection, the bridge's, so a message sent there goes through the running bridge.

Why doesn't the scheduler ask before it starts a subagent?

Because every subagent would otherwise wait on you, and the caps already bound them. Whether task, schedule and cancel ask first is an opinion, so a key of the agent's spec ([tool.scheduler] ask), and Link's is not to ask. With it on, the three aren't allowed when the tool is defined, so each asks as any tool's call you haven't allowed does.

Can an agent turn its scheduler off?

Yes, in its spec ([tool.scheduler] enabled = false): at the next start the scheduler isn't defined, nothing runs its jobs, and a harness that reads who a message is from has none. Without the key, every such harness has one, since a subagent and a schedule are what people ask an agent for. There's no command for it: the key is the one way, as for every other opinion in the spec.

What wakes the scheduler?

The agent's bridge, by Link's spec, since it already runs for the connections and restarts with the machine: the runner sleeps beside it until a job is due. A machine where a resident process costs too much can say [tool.scheduler] driver = "cron" instead: the machine's calendar starts the runner once a minute, and nothing of it runs between.

A key, not a guess

It's a key, as every opinion is, rather than something Link guesses from the machine. A machine that runs an agent only while it answers says address: the scheduler tells it when the jobs are next due, and it wakes the agent then, so even a calendar's minute isn't spent on an agent with nothing due.

The bridge still runs

Whichever wakes the scheduler, the bridge still runs, since a firing reaches the agent through its way in. Where one bridge carries every agent, one runner fires every agent's jobs in a time zone, each through that agent's way in, and an agent's started again only stops once its firings have ended, so none is fired twice.

How does a subagent get fewer tools?

A subagent is a new conversation of the same agent, so by itself it has every tool its parent has. Narrowing it is a list per conversation, in the harness contract (tools): the subagent's task comes with Link-Tools, the names of the tools it may use, and the harness keeps that list with the conversation, offering it only those from then on.

Never widened

A later list is intersected with it, so nothing widens a conversation again. The harness tells its tools which a conversation has (_meta link.local/tools), so the scheduler gives a subagent its parent's tools or fewer, a narrowed parent's subagent included, and never more.

Additive

It's additive: a harness that doesn't say tools gives a subagent all of them, as before, and a list is refused for it rather than ignored, since a subagent with tools it was meant not to have is worse than one not started. The mini harness answers it and the conformance kit checks it.

Link Harness keeps the list beside the conversation's events, not in them: it's a grant, like the agent's settings, and finding it in the events would read every event of every conversation at each turn.

Teams

What a team is, how its agents message each other, and who answers you in one.

What is a team?

One owner's agents working together, each with a purpose: a file of its own (~/.lnk/teams/<team>.toml) listing its members by ID, each with a name in the team and a purpose, a folder they share, and the opinions they share, a spec beneath each member's own.

Where it's kept

It's kept on the machine like an agent's settings, outside every sandbox, and only lnk agent team writes it, so no member can put itself on a team or another on its. The commands are lnk agent team, not a group of their own: a team is agents' settings, which the harness plugin keeps, and a plugin of its own would read another's files. A team isn't shared agents, where strangers talk to yours: every member is one owner's.

Why does a member have a purpose, not a role?

Because what a member is for is a line in words, for people and its teammates' prompts, and grants nothing. In access control a role is a policy, what a holder may do, so the word is kept for that. A team's file with a member's role is refused, naming purpose, the key it takes.

How do a team's agents message each other?

Through Link Scheduler, as subagents and schedules do: send, to a teammate by name or to the whole team. Its runner hands the message to the teammate's bridge, in that agent's conversation with the sender (or the team's), marked as from agent:<id>, never from you, and the teammate's answer comes back to the conversation it was sent from, as a message from it.

Its answer

The answer comes back as a job of its own, so failing to deliver it never sends the message twice.

Who may send to whom

Who may send to whom is the teams' files': the scheduler is told an agent's teammates at its start, and each bridge reads the files again as a message comes, taking one from another agent only when the two share a team. No message goes through a harness or the relay, and no process waits on another.

What stops two teammates answering each other for ever?

The scheduler's caps, as for subagents: each message carries its hop count, a message and its answer each one more, and send refuses past [tool.scheduler] hops (Link's 8), saying so to the model; the cap a day (per_day, Link's 100) bounds how many messages an agent sends in all. A team has no cap of its own: its file can set the scheduler's keys for its members, as any opinion.

When does a change to a team take effect?

At each member's next start, which lnk agent team makes for the members that run. What a team changes (an agent's spec, its tools, the folders its sandbox reaches, whom its scheduler may send to) is fixed for a start, so the prompt and the tools never change inside a turn, and the provider's cache holds between them. The one thing read again each time is who may message whom, by the bridge, so a member taken off a team is refused at once.

Who answers you in a team?

Each member, on its own connections: a team has no connection of its own. You give a task to the member you text, it passes parts to the others with send, and their answers come back to it, so the one you asked answers you.

What does a mention in a group get?

An answer, there, on every harness and connection: the owner mentioning their agent in a group, connection or server gets it answering, as in a direct message, since silence looks broken. Only the owner's mention is answered; anyone else's, and a message that doesn't mention it, get nothing. Link sets it in each harness's settings (OpenClaw's and Hermes's group policies), and Link Harness's bridges carry the mention, each group a conversation of its own (<connection>:<group id>), so switching harness doesn't change it.

Connections

How a connection is set up, shared, carried for a harness that speaks none, and how an agent's webhooks take their posts.

Connecting a connection

Where a connection's setup lives, which a harness speaks, and who a connection answers.

Where does a connection's setup live?

In a plugin per connection (lnk-telegram, lnk-slack, lnk-discord, lnk-signal), installed the first time you connect it, rather than in the harness plugin. You install only the connections you use, with what each needs, such as Discord's Gateway client, and adding one doesn't grow the harness plugin. The harness plugin keeps what's the same for every connection: its settings, stopping the agent while it pairs, disconnect, switching and moving.

Which connections does a harness speak?

Its adapter says, with lnk-<name> connections, so connect refuses one it lacks and use says which stop answering. A connection stays in the settings when the harness changes, so switching back brings it back. An adapter that doesn't answer it speaks Telegram. An adapter with an endpoint also speaks the connections Link carries.

Why does the harness plugin know no connection by name?

So a connection is a plugin like a harness: lnk-matrix on the PATH plugs in, and adding one of Link's own touches only its plugin and the core's list.

One map

An agent keeps one map of connections, each as its plugin's connect made it and describe said of it (its title, which fields are secret, whether it's linked as a device, its hosts), and hands each section to the adapter under the connection's name. The typed sections of Link's own connections stay in link_harness_contract::harness, the contract the adapters read, and a connection from outside Link sits beside them.

One map for every connection

Settings and moves hold connections in one map, bots, and lnk agent list --json prints it as bots. A Telegram token keeps its Keychain name, bot-token, so no Mac asks for it again.

Who does a connection answer?

Whoever sends the one-time code printed in your terminal, in a private chat or direct message (Telegram, Slack, Discord), or the number you type (Signal), as its owner, and no one else as its owner: a chat you share it with is answered as its members. Knowing the bot, or being in the workspace, isn't enough. Slack's allowlist is by member id: the harness's own for one that speaks Slack, the bridge's for one Link carries.

Shared chats

Why the owner shares a chat, and who in it may do what.

Why share a chat rather than a workspace?

The owner shares individual Slack connections, Telegram groups and Discord connections. A member's direct message stays private to the owner, and bots are refused. Each agent is the cordon: everyone in a shared chat can use all its tools, files and memory. Separate files belong to separate agents. The owner pays for every turn, with no cap per chat.

Why keep the audience in access?

[access] message lists chat principals. link-principal owns their one grammar, also used by Link-From, without depending on another Link crate. A connection names a person as person:<connection>:<id>, including the workspace when its user IDs are workspace-scoped. The harness keeps the person's identity and name beside their message, never treating them as the owner. Sharing requires a harness that supports these senders.

Who may approve a shared agent's actions?

The owner alone shares, unshares and approves sandbox requests. An admitted member can use the agent's tools. A stranger receives only the tools the spec gives strangers, on a harness that can narrow a conversation's tools. The agent's own schedules, subagents and background commands act for its owner.

Why does sharing stop the agent?

The connection plugin takes a paired user's mention with a one-time code, within ten minutes, while the bridge is stopped. It never writes the spec. The command atomically edits only [access] message, preserving comments, then restarts the agent. Sharing is done on its machine before a move to a cloud. The three connections use the same contract and ship together.

Why Link has a connection of its own, and how its answers and Ctrl-C work.

So you can talk to your agent, and a script can check what it says, with no bot, phone or third party: every other connection is someone else's app, made for each agent, and some can't be scripted at all. link has no account and no pairing; its owner is whoever reaches the agent's endpoint the way its bridges do, which is its user on its machine. Its threads are link:<name>, link:main unless named.

Why does lnk agent chat send send as a bridge does?

So a harness speaks it with nothing new: a message is POSTed to its endpoint with its thread, key and proof, exactly as a connection's bridge sends one (link_connection_contract::ask_for), and the conformance kit's endpoint checks cover it. A harness that speaks its own connections has no endpoint to send to, so lnk agent chat send refuses it, saying so, rather than reach it another way. An agent on a box is reached through lnk box run, its own lnk agent chat send there: nothing new on the box.

Where does an answer nobody waited for go?

To the next chat on its thread. One to a message from others in a link thread (a reminder) is held by the agent's bridge, in the connection's folder, as a connection's plugin would send it to its chat. One the harness finished after its caller went (a crash, or Ctrl-C) stays with the harness, which keeps it in two phases: the bridge leaves a link thread's there, and the chat on that thread asks for it, so one ask hands it over and nothing is held twice.

What does Ctrl-C stop?

The chat's wait. A harness runs a turn to its end whether its caller waits or not, and keeps the answer, so Ctrl-C loses nothing: that answer comes at the next message. A message sent while that turn still runs does what the agent's spec's [turns] busy says, as on any connection: by default it joins the turn, and the chat waits for that turn's answer, which answers both. Which kept answer that is, the harness doesn't say, so the chat notes what's kept in the thread before it sends and takes the first answer kept after: one turn runs in a conversation at a time, so that's the turn it joined.

Carrying a connection

How Link carries the connections of a harness that speaks none, and where that runs.

The connection plugins already make the bot and pair its owner; for a harness that speaks ACP at its endpoint instead of speaking connections (lnk-<name> endpoint), the plugin carries the messages too: lnk-<connection> bridge sends each message from the owner there and the answer back, streamed while it's written.

ACP

Any harness that answers one local endpoint gets every connection Link carries, and a new connection reaches every such harness at once. ACP, the Agent Client Protocol, is the shape, as every harness is driven the same way so they differ as little as possible, a harness on stdio through the hub; only the new message is sent, never the thread's history, since remembering is the harness's.

Where does the thread go in the request?

In the session's ID, <connection>:<chat id>: each thread is an ACP session of its own.

Where does the carrying run?

In each connection plugin, outside the harness's sandbox, beside the agent while it runs: a background service of its own (lnk agent bridge, which runs lnk-<connection> bridge per connection), stopped with the agent, or a thread of lnk agent start --foreground; or, where the machine says so, one bridge for every agent (lnk agent bridges).

What the bridge holds

The harness never holds the connection's token, and the bridge holds no state but what the connection needs, so a harness answering elsewhere sees no difference. The harness's endpoint takes a key Link makes at each start, since other users of the machine can reach its loopback.

How does a bridge know the harness answers on its port?

The endpoint proves it: each message goes on a connection of its own, which first answers a random challenge with an HMAC of it made with the endpoint's key, and only then gets the key and the message.

Why a proof

A start checks the agent's ports are free, but another program can take one in the moment after, or while the harness's service restarts, and a bridge would hand it the key. The other way, the endpoint on a unix socket in a 700 folder, keeps everyone else off it, but a harness's endpoint is an address on the loopback in the contract, which an adapter released earlier can't change, and an adapter can't always make its harness listen elsewhere.

Additive

A challenge needs only a route and a function in the adapter, so it's in the contract as proof, a field with no new version, which an adapter says it answers: older ones keep working, unchecked. The proof is on the same connection as the message, so the port can't change hands between them, and it's a signature of the challenge, never the key, so asking for one gives nothing away.

Telegram, Slack, Discord and Signal, where people already chat: all four for any harness, so any harness can answer anywhere Link connects. How a long answer is split, and showing the agent typing, are each connection plugin's, as their limits differ:

  • Telegram's answer is edited in place about once a second and split past 4,096 characters.
  • Slack's is the same, split past the 4,000 characters Slack recommends; Slack shows no bot typing, so 👀 goes on the owner's message while it's answered.
  • Discord's is the same, split past 2,000 characters.
  • Signal caps how often a message can be edited, so it shows typing and sends the whole answer, split past 2,000 characters.

Where does a linked connection's device live?

In a folder of the agent's for that connection, ~/.lnk/agents/<agent>/connections/<connection>, which the connection's plugin links into (lnk-<connection> link) and carries from. A linked device's keys read and send as the account, like a token, so they stay out of the harness's home, as tokens stay out of its settings.

When it moves

A move carries what the plugin names as the link (lnk-<connection> state), never a program it installed, which differs between a Mac and Linux: the machine the agent left deletes its copy, so one device holds the account. It's decided when the agent is sent, from its harness: one that speaks the connection itself keeps the link in its own state, which moves as it always has.

Webhooks

Where an agent's webhook listens, whose its post is, and how long a post's id and a rolled secret are kept.

Where does an agent's webhook listen?

On 127.0.0.1 alone, in the agent's bridge, which already runs outside its sandbox beside it and holds its endpoint's key. Anything public in front of it is a tunnel you open (lnk tunnel open --public), so the address a stranger can reach is always one you chose, and add never opens one.

Its port

The port is chosen once and kept with the agent's settings, so a tunnel to it keeps working after every restart. The listener names no connection: each hook says which plugin checks its posts (sift), as on a machine that packs agents, where the same plugin answers the same sift.

Who hands it over

The bridge, not the plugin, hands a post to the agent, through the way in every message from others takes: who it's from is the webhook's own, so a plugin can't send a post as the owner's, and its tools are a stranger's.

Why is a webhook's post a stranger's message?

Anyone who holds the secret can post, and a service that signs (GitHub) passes on what other people wrote. So the post reaches the agent from webhook:<name>, quoted, never as its owner, with only the tools its spec gives strangers, none unless it says, and only a harness that can say who a message is from, and keep a conversation's tools, gets a webhook. No answer goes back to the poster: 202 says it was taken, and nothing the agent says leaks to whoever sent it.

How long is a post's id kept, and a rolled secret?

A post's id is kept 30 days, the longest any sender Link knows keeps its own (GitHub's, whose posts sign no time), so one keep covers every scheme. It's kept only once the agent's harness has taken the post, when it answers the way in, before its turn ends: a sender gives up long before a turn might, and waiting would hold the post's place for the whole turn. One the harness refused, or that couldn't reach it (its harness down), is answered 503 and let go, so the sender's redelivery is taken rather than refused as a repeat.

A rolled secret

A rolled secret's previous one is taken for 24 hours, its expiry kept beside it, or until roll --done: Link can't see when the sender switched without carrying which secret matched through sift, and a day is enough to paste the new one in.

Sandbox

How a harness is sandboxed and kept to its proxy, how its bridges know who calls, and whose its grants are.

The sandbox

How the sandbox runs on each OS, where a harness's temporary files go, and how to run something as your agent would.

How is a harness sandboxed?

On by default, and managed by Link: Seatbelt on macOS, bubblewrap on Linux. The harness reaches its home, its agent's Home, its own localhost ports and a local model's, and system libraries.

Its grants

Everything else is a grant, a section of a sandbox's spec: the internet through its proxy and, on a Mac, the developer tools are on by default; your home, your browser, this machine's other ports, your network and the network as it is are off. lnk agent allow and deny change them for the agent, a harness's needs add to them while it runs, and lnk agent harness check tests them. The sandbox is the sandbox plugin's, so every harness runs in the same one, with its own folders and grants.

With a profile for bubblewrap alone, added with your consent, rather than turning the rule off with sysctl, which would open user namespaces to every program. Never a silent fallback to no sandbox.

Where do a harness's temporary files go on a Mac?

In its home's tmp, its TMPDIR, with a mktemp of Link's first on its PATH. macOS's mktemp without a template uses the per-user temp folder whatever TMPDIR says, and the sandbox refuses that folder, which every app shares: installers calling mktemp -d (uv's, in Hermes's) fail. Link's makes the file in TMPDIR and hands a call with a template to /usr/bin/mktemp. Opening the shared folder, even only its tmp.* entries, would show the harness other programs' temporary files. A program calling the C library directly, as xcrun does, still meets the refusal.

Is there a package cache for installs?

No. A cache the sandbox can write to is one harness's install poisoning another's.

How do you run something as your agent would?

lnk agent harness run -- <command>, under the harness's own policy, beside the running agent. Without it, debugging an agent is guessing from its log what the sandbox or its proxy did to a call. It builds the same policy the harness gets rather than entering the running one, so it works whether or not the agent runs, and it gets its own rules file and proxy so it can't disturb the agent's.

The proxy

How a harness is kept to its proxy, what gives it this machine's network back, and how its install reaches the internet.

How is the harness kept to its proxy?

In a network namespace of its own (bubblewrap's --unshare-net), crossed only by Link's relays over unix sockets, rather than firewall rules: no root, no rules that go stale when the service restarts, and anything ignoring the proxy has no network at all. On macOS, with no namespaces, Seatbelt allows connecting only to the proxy's loopback port. The proxy is the sandbox plugin's.

Is an agent behind its proxy on your own computer too?

Yes, whenever it has [internet] and the sandbox is on, not only on a box or with an exit. The network as it is, with no proxy, opens every localhost port and X11's sockets on Linux, and localhost through ::ffff:127.0.0.1 and the Mac's own address on macOS, which the proxy closes.

What gives an agent this machine's network back?

[network] mode = "host" (lnk agent allow --network-mode host), off by default: the network as it is, with the gaps it brings, which lnk agent harness check shows. The proxy refuses this machine and its networks by design, so a NAS or a model on another computer that [lan] through the proxy doesn't reach needs it. An exit ignores it: its proxy is its only way out.

Does run go out through the agent's route?

Yes: what lnk agent show --permissions lists is what every path of the agent runs with, so a program run starts leaves as the harness does. With a route (an exit), each call's proxy goes out through it, and [network] mode = "host" and [lan] reach nothing past it, as for the harness.

Which ports does a harness reach through its proxy?

HTTPS's and HTTP's, and endpoints you name (lnk agent allow --tcp github.com:22). A raw protocol reaches only what's named, and a name reaches only its port, so nothing on the internet is reachable on a port no one chose. With a list of hosts, both apply.

Where are an agent's hosts kept?

In its sandbox file, as [internet] hosts, and passed to its proxy as its list, whichever harness runs it: the hosts are what you trust the agent to reach, its model's and its connections', which a switch of harness doesn't change. Link's file has "*", any host, so a new agent works from its first start.

How does a harness's install reach the internet?

Through a proxy of its own, direct from this machine, for each setup step (the installer, configure, link): no step gets the network as it is, so none reaches this machine or its network. The health check gets no proxy and no network at all: only the harness's own ports on this machine's loopback.

Which hosts

The installer and configure, which fetch code, reach only the hosts the adapter lists (lnk-<harness> internet-hosts): what a harness installs is the adapter's to know, where the running harness's hosts are yours. link reaches any host, since a connection's servers are its own. The exit isn't used: an install needs nothing from home.

Why does the health check reach only the harness's ports?

All it asks is whether the harness answers on them, and lnk agent status asks it of every running agent. Behind a proxy it started a proxy too, holding the agent's keys where the agent has them, for a request that never leaves the machine. On the loopback alone it needs none of that, and reaches less: no internet, no other port of this machine, never a local model's.

When status runs it

status runs it only when nothing on the harness's ports answers a plain request with success (a 2xx): a port its proxy holds closes without an answer, so one that answers with success is the harness, up, known without a sandbox, and status costs a running agent about what status --json does. Any other answer (a 503 from a harness without its key) is the health check's to judge.

Why do the scheduler and run take the conversation from the harness?

Because nothing else knows it. Which conversation a call is made in, and its hops, come in the call's _meta (link.local/thread, link.local/hops), which the harness writes. The sandbox's tool proxy forwards a call as it comes, and its secret is one per sandbox, not per conversation, so it has nothing to check them against.

What that trusts

Link Harness writes _meta itself and runs nothing the model writes, and run's commands reach no tool. A harness that runs what the model writes (a shell) could name another conversation, or reset its hops, bounded then by the caps a day alone. A source the proxy could trust, a token per message the bridge makes and the proxy checks, would change the bridge, every adapter and the harness contract; the trust is stated instead (Security).

Where is a hosted model's key?

Added by the proxy, outside the sandbox (lnk agent secret wire). The harness speaks plain HTTP to the proxy's local address and the proxy speaks TLS to the provider.

The alternative, a certificate authority of Link's trusted inside the sandbox to read its TLS, would be a CA to protect, and this way only providers the proxy knows get a key.

Whose harness holds it

It's on by default for a harness a test shows reaching its model through the proxy, since a harness that ignores the address would fail with the placeholder: Link Harness, OpenClaw, Hermes and DeepSeek Harness. A harness from outside Link holds its key, since no test of Link's runs it. lnk agent secret sets it either way for an agent.

Bridges

How a bridge knows which agent calls, which key proves it, and why every bridge and store driver runs as it does.

How does a bridge know which agent is calling?

By a proof made with the agent's own key, never the operating system's word. Every way out of a sandbox but the proxy is a bridge (a connection's, the way in, the store bridge), and each call to one is the same three things: the agent's identity (its ID and owner, as Link already defines them), the action, and an HMAC of a challenge the bridge sends on that connection and that identity, keyed with the agent's key.

One type

One type in link-bridge for every bridge, so none tells who's calling its own way.

Why not the system's word

A Unix socket's permissions or its peer's process say which user or program connected, not which agent, and differ from one system to the next; a harness serving several agents, or one bridge serving many, is one process to the system. A key proves only its own agent, wherever it runs, so a harness, however it's steered, acts on a bridge only as itself, and a signature gives the key away to no one. It's the endpoint's proof turned the other way, under a label of its own, so an endpoint, which signs any challenge, is never asked to make one.

Which key proves an agent's calls?

The one made at each start, which the harness already holds as its endpoint's key: bound in the machine's keyring while the agent runs, and unbound at its stop. A key that leaks then dies with the start.

Where an agent runs only while it answers

Where a machine runs an agent only while it answers, the key is made each time something there is bound to run the agent instead: that receives the agent's identity and its key as it's bound, from the machine's own store of secrets, never from the agent's state, and discards them when it stops, blank again for the next agent, which gets a key of its own. What runs an agent there is then only ever one agent, and only while bound.

Why does every bridge run on one server?

Because a bridge is a door out of the sandbox, and what keeps one standing (a deadline before the proof, caps, a busy answer, an error shape) is the same whatever it carries.

Written once

Each bridge once wrote its own socket loop, and each got some of it wrong in its own way: a deadline on each read but not on the whole proof, a cap counted before the proof, one call at a time, five shapes of error. One server, in link-bridge, does it once, and one test checks every bridge against it, running; a bridge says only what it does with a call and how many it holds. It's a library, not a plugin: no one uses it on their own.

Why five seconds to prove, and unproven connections apart by user?

A caller with the key proves at once: the challenge is a line, the proof a line. Five seconds over the whole exchange is far more than that takes, and short enough that a caller dripping a byte at a time holds nothing for long.

Places by user

Connections not yet proved hold places of their own, so they never take one a proved call needs. Before its proof, the one thing a bridge knows of a caller is the user at the other end, as the system names it: a machine that packs many owners' agents runs each as a user of its own. So each user gets a few places, and its newest takes the place of its own oldest: a user flooding a bridge closes only its own connections, never another user's proof, and room is kept for every user such a machine runs.

Why does send.sock check the keyring too?

A connection's bridge is handed its agent's key as it starts, and could check only that. But the keyring is where a key stops proving anything: unbound at the agent's stop, replaced at its next start. Checking both means a bridge that outlives its agent's stop takes nothing more proved with the old key, as every other bridge already did.

Why does a store driver run outside the sandbox?

Because whatever a driver needs to reach its store, a credential or a way to a database, would otherwise be in the harness's sandbox, where an agent with a shell could read it and do anything the credential lets it. Behind the store bridge, the harness asks for the store's operations alone (append if the number is free, read, the lease), as itself, and only for its own conversations; the bridge runs the driver outside, and the harness neither knows nor cares which it is.

Another driver's conversation

A conversation kept by another driver than the spec's now is read as before, the driver run where its caller runs, until it's moved.

Why does the store bridge keep a driver running?

Because a process and a fresh connection to the store for every call are most of a short turn's store calls: a turn makes a dozen or so, and each paid a process's start and a connection's handshake on top of its one request, about twice the time at 30 ms to a database.

serve

A driver whose contract says serve answers call after call on one process and the connection it holds, so a call costs its one request. It stays one call at a time, in order, stopped past 30 seconds as before, and a driver that doesn't say serve is run once a call as it always was, so no driver had to change.

Why not a library

The alternative, a library each driver links into the bridge, would put every driver's code and credentials in one process and tie drivers to Rust.

Grants

Whose each grant is, why the agent's file is a sandbox spec, and who may turn the sandbox off.

Whose are the grants?

Two layers, each in its own file. What you grant an agent is the agent's (~/Link/Agents/<agent>/Config/sandbox.toml, lnk agent allow): it follows the agent to any harness, so a switch never asks for it again, and two agents on one harness can differ.

The harness's

What a harness needs beyond that is the harness's (~/.lnk/harnesses/<harness>.toml): what it is decides it (OpenClaw's sockets ignore the proxy whichever agent runs it), so it's asked when an agent switches to it, held only while it runs, never given to another harness, and the same for every agent here that runs it.

Only what it asked for

A harness's layer holds only what it asked for: lnk agent harness allow and deny change a need it declares, and refuse anything else, which is lnk agent allow's to grant the agent; reset forgets the answers, so its next start asks again. Allow and deny mirror each other, so taking a need back is never a one-way door. A harness that needs nothing runs with the agent's alone.

Why is the agent's file a sandbox spec?

So there is one way to say what a sandbox may do. The agent's file, a harness's grants and its needs are a sandbox spec's sections, and lnk agent allow and deny take the flags lnk sandbox run takes, so what you learn for one you know for the others.

In the agent's terms

The file is in the agent's own terms: [files] names the folders it sees (work, instructions, . for all of Home), and a section a harness's sandbox can't enforce is refused, naming the line, never a grant that silently doesn't hold. Its [files] and [commands] hold where Link enforces them: [files] in a harness that keeps to the layout and in Link's tools, [commands] in each of run's sandboxes.

What does a member reach of its team's folder?

Only what its own sandbox file grants it, [teams.<team>]: the folders of the shared folder it writes, and those it only reads, or all of it. Two members' sets may overlap or not, so a checker can read the sources no one else does.

Hidden but for what's granted

Every sandbox hides ~/Link/Teams, and each team's folder kept elsewhere, but for what's granted inside, as it hides other agents' folders, whichever harness runs the member and whatever --read '~' allows.

The agent's grant

The grant is the agent's, not the team's, so it follows the agent like its other grants; being on a team is still what lets it reach that team's folder. lnk agent team add grants the whole folder unless told otherwise. lnk agent harness check reads each team folder the member is granted (it must) and every other team's (it must not).

Why is no sandbox the agent's setting, not a harness's grant?

The agent owns one sandbox setting. lnk agent start --no-sandbox sets it to false, --sandbox turns it back on, and the setting persists across starts and appears in status. A harness reports whether it can run confined through its needs answer. A start that requires an explicit choice refuses until the person supplies --no-sandbox. The sandbox is enabled by default.

Who can turn the sandbox off?

You, or your script, from outside any sandbox, by name; never an agent. Running without one is reasonable on a machine you know, or in a test, so a script may, with --no-sandbox, the only way. --yes answers what a harness asks and never that, so a script that starts or restores an agent doesn't turn its sandbox off by running a harness that asks: no sandbox reads and writes all you can.

Never an agent

An agent can't turn its own sandbox off, or another's: what decides it (its settings, where --no-sandbox is kept, its spec, its sandbox file, its harness's grants, the machine's settings and the services' units) is all in ~/.config, which no sandbox reaches, and a test tries every way at each from inside one.

unsandboxed = false

A deployment that must never run an agent unsandboxed (a cloud starting harnesses for others) sets unsandboxed = false in ~/.lnk/machine.toml: then every way there is refused, and a harness that can't run in a sandbox doesn't run.

A service already running

A service already running a harness unsandboxed reads it too: it runs the harness through lnk agent unsandboxed, which checks the file at every restart and boot, and removes the service rather than run it, since systemd and launchd restart a service without asking lnk.

The file and a box

The file may hold the switch alone, written before lnk first runs: its ID and name are added. It's read only when you (or root) alone could have written it. A box needs no switch: root's /etc/lnk/kind.toml says it's a box, and a box never runs an agent with no sandbox, since what a person's machine.toml says loosens nothing root decides.

Why does removing a harness keep its grants for another agent?

lnk agent harness delete --harness <harness> removes one agent's home of it. Its grants go with it, so adding it again asks again, unless another agent here runs it: deleting them would change what that agent runs with at its next start.

What happens to a sandbox file that names permissions?

It's read once, through one table, and written back as a spec's sections, the same table for an agent's file, a harness's grants and a move's manifest.

Never widened

What a section says as broadly is kept, and what it can't say as narrowly is left out and said once, never widened: a localhost without lan reached every port behind its proxy, so it's left out, and each port it needs is named with --port; lan without network opened nothing, and is left out too. One thing is gained, the one exception: ports named without network, which only macOS reached, reach on Linux from then on, relayed into its sandbox as on macOS, and that's said as it's read.

What a harness asks for

Whether a harness says what it needs and risks, when Link asks, and what the ask covers.

Does [commands] bind the harness's own programs?

Yes, as it binds run: what lnk agent show --permissions lists is what every path of the agent runs with, so a harness names the programs it runs, past the program Link starts and its #! interpreter. One that runs what it installed in its home asks for any program in its needs.

Does a harness say what it needs?

Yes, lnk-<harness> needs, with why, in the harness's words; Link doesn't change a harness to fit its sandbox. It reads the settings, since what it needs can depend on them (a model provider, a connection), without keys or tokens, since it runs outside the sandbox. An unknown name is refused, so a typo never runs a harness with less than it needs, or more than it said.

A spec

A need is a sandbox's spec, as lnk sandbox run --spec takes it, with no name, version or relative path, so it's shown and taken back as the flags lnk agent allow takes.

Version 2

The contract's version 2 made needs specs: an adapter of version 1, whose needs names permissions, is refused, saying it's older than this Link and to update it, rather than read as something it didn't say. An adapter that doesn't answer it (an error exit, or no answer) needs nothing more, since an outside adapter releases on its own schedule.

Before the harness runs: use, its first start, a start after its needs grew, and a move, in the terminal running it, before the agent starts where it goes: the machine it goes to answers what it needs there, and a refusal removes the copy put there, so the agent starts where it was and never runs twice.

Kept with its answers

The answer is kept with the needs it answered, so it asks only for what's new, and never again for what you allowed or took back: a deny is an answer too, until lnk agent harness reset forgets them. What the agent has of its own is never asked. Without a terminal, it refuses and names --yes, rather than run a harness without what it needs, or with what nobody allowed. The question names the agents here that run the harness, since the answer holds for each.

Why does every switch that widens what an agent may do ask?

Because a harness's needs are its own, so switching an agent to another harness can give it more than it had, and the sandbox is only worth what the user knows of it. lnk agent use lists each need the new harness holds beyond the old one's and the agent's own, with what it opens, beside what the new harness asks for now, and asks once, in the warning color; no keeps the agent where it was.

At every switch

It asks at every such switch, not once per harness: the agent and the harness it leaves are what change, and a grant already answered for a harness is still new to an agent coming from one without it. Less is said in a line and asks nothing.

How does a harness say what running it risks?

By a risk of its own in needs: a name, and why. Some of what a harness does opens a door no spec names, such as a port of its own that asks for no credential, which every user of a Mac reaches on its loopback.

Named by the adapter

Link can't know each harness's ports, and the fix is the harness's to make, so the adapter names the risk and Link asks for it as it asks for a spec, then hands the adapter those allowed (allowed) as it's configured.

One it can run without

A risk the harness can run without (OpenClaw's managed browser) says what it does without it, and only a yes in a terminal turns it on: off is safe, so --yes and no terminal leave it off rather than fail. One it can't run without is asked as a spec is, and taken back, the adapter refuses to start. Its name is refused where allow or deny takes the name for something else, and the field is new without a new version: an older Link refuses an answer with it rather than running without asking.

How does an adapter know where its ports are?

Link tells it whether its harness runs behind its proxy (proxied), not which grants it has. On Linux behind its proxy the harness has a network of its own, and its ports other than those the proxy serves stay there; without it they're on the machine's loopback.

One fact

No sandbox, [network] mode = "host", and anything later that takes a harness off its proxy all come to that one fact, so an adapter that names a risk by it needs no change when a new way off the proxy comes. The adapter adds what it knows: that macOS has no network of the sandbox's own, and a --network-mode host it asks for itself, which needs hears before it's granted.

Can a harness ask for less than the defaults?

No: a harness says only what it needs beyond them. A harness with no use for the internet runs with it unless you deny it.

Does the ask cover hosts?

No: it covers what a harness needs beyond Link's defaults, the grants that widen the sandbox past its proxy, and risks of its own. The agent's hosts stay as they are, any until you list them, and the hosts its installer reaches stay its adapter's internet-hosts.

Why are only this machine's permissions listed?

--developer-tools opens Xcode's tools, which only a Mac has, and the network and a port open different things under Seatbelt and bubblewrap. A list that names another OS's grant, or describes one as it works there, makes a person reason about a machine they aren't on. So lnk agent show --permissions, help, and what a grant says show this OS's own, each as it works here. Another OS's table ([commands.macos]) is still read and kept, never refused: an agent's grants move with it between a Mac and a box, and a script works on both.

Actions

Why an agent's commands, tools, routes and CLI come from one registry of namespaced actions, and how the registry is shaped.

Why is there one kind of action?

An action is a typed request from a caller, answered outside the caller's walls. A part that relays a protocol it does not type, such as MCP or inference, is the same action with carries in place of answer, and one of the two is required. A scheduled run, a webhook and a connection's message are callers, not actions: the caller is Trigger, which acts under the grant it was set up with, since it acts later with no owner there.

What it costs

One registry, one way of proving a caller and one table for the proxy replace a separate kind with its own plumbing for each. The server branches once, between a typed answer and relayed bytes. Every place typing cannot hold is one search for carries.

Why are the names two parts?

A name is <namespace>:<action> as a command, <namespace>__<action> as a tool and /_link/<namespace>/<action> as a route, so one action is found by the same words everywhere. Another harness's familiar spellings are aliases that resolve to an action and add no handler or permission.

Why these namespaces?

Each holds one subject and is named in the singular. The ways a message goes into a conversation, such as goal, are chat's rather than namespaces of their own. memory stays apart from chat because it outlives conversations and is a store of its own, and browser and desktop are separate from web so that two-part names can hold each.

Moving an agent

Where an agent runs, the stream it moves in, what happens where it lands, and how it's backed up.

Where does an agent run?

In one place at a time, and a move carries its state, not its install.

Installed fresh

A harness's home on a Mac holds macOS programs that don't run on Linux, so the machine it moves to installs the harness fresh; each adapter's state says which paths are state. The harness's own Telegram bot has one poller, so it follows the agent.

The stream

What carries an agent between machines, how its two ends stay compatible, and what happens to a message or a move cut short.

What carries an agent between machines?

One machine's lnk agent state-send piped into the other's receive, through your computer, rather than pushed to buckets and pulled on the other side. A box then needs no cloud keys nor the agent bucket's password to receive, nothing sits in a bucket after, and a move is as fast as the two SSH connections. The cost is that your computer is in the middle, which it is anyway, since you run the move there. Backups stay on buckets.

Why does a move use each place's storage contract?

A conversation kept in SQLite and one kept as files are the same collection. Every end reads and writes them through the same collection transfer, so a move keeps events, narrowed tools, notes and scheduled jobs across places. Home stays a stream and an engine carries its index in its own format.

How do settings reach a box?

In the move's stream, over SSH from your computer, never through the relay, as the rest of the agent goes.

Why is the complete switch sealed, when SSH carries it?

SSH protects the stream on each leg, but your computer is in the middle and passes it whole: a plaintext recording would expose linked-device credentials and harness state as well as manifest secrets.

Its key pair

The receiving end makes a key pair for the switch alone. HPKE seals a fresh streaming AEAD key to it; bounded records authenticate order and a required final record proves completion. A machine drops its receiver after the move.

A hosting service

A hosting service keeps an independent receiver for restart, deletes it after commit when possible, and refuses it after 24 hours. The preliminary copy retains SSH or TLS protection. It stops passive capture; whoever holds your computer holds its SSH keys to both machines too, which a seal never could stop.

How do the two ends of a move stay compatible?

The stream has a format number, and a manifest named for it (lnk-move-4.json), so a receive of another format finds none of its own and says which it found, instead of putting the agent in place of its own. The formats must match, not the versions, since a dev build has no release. move upgrades a box older than itself.

Why does a move go in two phases?

So the agent answers throughout, wherever it goes. Its files and its memory's index, the bulk of it, go first while it runs, and only what changed since goes once it's paused, which takes seconds however long its history. One protocol for every destination is less code than a stop-and-copy for boxes beside it, and a move to a box stops being minutes of silence. Each file carries its SHA-256, so what the other end holds already isn't sent again: a copy tried again, or a move back to a machine the agent left, sends only what's new.

What happens to a message sent during the switch?

It waits, never lost, on a connection Link carries for the agent. Those connections stop reading while it's paused, so their platforms keep what comes, and it's answered where the agent went. A harness that reads a connection itself isn't paused by Link: what comes there is its own, as always.

On a cloud that hosts it

A cloud that hosts agents holds what comes while the agent is paused there and hands it over once it answers where it went, where it's kept with the agent's settings until it's given: by lnk agent deliver, or by its bridge as it reaches its harness, so an agent moved with --no-start gets them when it starts.

Until it's given each

Until where it went has them, the computer running the move keeps them too (~/.lnk/held/). One it's never given there, from a sender its connection isn't paired with or on a connection Link doesn't carry for it, is shown whole to whoever moved it rather than kept forever. The switch takes seconds, and an answer from the agent beats a notice that it's moving.

What if a move is stopped midway?

From the pause until the agent answers where it went, Ctrl-C, a closed terminal or a stop doesn't end lnk midway: what it runs then ends, the question it asks is answered no, and the move fails as any failure does. A move that ends with the agent not answering where it went fails too, and its copy there is removed.

Where it answers

A move stopped midway, or one whose agent doesn't answer where it went, leaves the agent answering where it was: resumed on a machine, the move ended (agent-abort) on a cloud that hosts it, so a move tried again isn't blocked. What its harness needs where it's going is asked while it's paused, since it's the copy there that's asked, which exists only after the switch.

Finished later

A move that can't be ended at once is kept in its journal on the computer running it, one entry a move, finished by the next lnk agent move or lnk agent start of the agent there. A copy stopped on a box that doesn't answer stays an entry when the agent moves again, so it's still removed once the box answers. An end on a cloud that hosts agents counts as over only when its agent-abort says so: one that fails keeps the entry, rather than lose track of a copy there.

How does a move reach a cloud with no box?

Through the cloud's adapter, which may host agents by answering the agent-* commands, and owns its transport: its service's API, signed in as the account. lnk only pipes the move's streams to and from it, as it pipes a box's over SSH. The core then speaks no cloud's API, as for boxes, and the public code names no service.

Where it lands

When a move is done, what it replaces, and whose word it takes for what the agent may do.

When is a move done?

When the agent answers where it went, running, with its ID (its status --json there). Link can't see a Telegram reply, so that's the proof it has; the files left behind stay 30 days in case it's wrong. How long is an opinion, disk against safety: keep_moved in the machine's machine.toml ("90d", or "never" to delete them yourself) changes it for that machine.

What happens to the copy an agent leaves?

Its settings, state and keys are deleted once it answers elsewhere, and its files are kept a while. A second copy would be a second truth, and a second poller of its bot. A note says where it went, and start refuses its ID.

What does a move replace?

The state wholly, the files folder additively. The harness's state is its own, so the stale copy where it arrives is replaced; the files folder is shared with you, so a move overwrites files but never deletes them.

Which model does an agent use after a move?

Each machine keeps a local model it can reach. A box without a GPU can't run the Mac's, so it needs a key once (the provider's variable, or asked for). Back on your computer, your own model is used again if it answers.

Whose word does a move take for what the agent may do?

The machine running lnk agent move's, never the stream's. The stream is written by the machine the agent leaves, and when that is a box, the box would choose what the agent may do on your computer: its whole home, its loopback, its network past the proxy, a program its connection's plugin runs outside any sandbox.

The agent's grants

The computer running the move sends the agent's grants in the first line when the agent leaves it; an agent coming back keeps the grants it has there; and one arriving any other way gets the stream's, but for --read '~', --network-mode host, --lan-host, --port, --browser-host, its endpoints (--tcp), its tools that run outside (--tool) and a program of its [commands] named by a path or * (--command), which the receive names, as the lnk agent allow flags that give them here. A program named alone is found on this machine's own PATH, so it stays.

The harness's grants

A harness's grants never come from a stream: what the machine running the move asked about, and was allowed, is granted where it lands (lnk agent needs --allow), for what the harness asks for there; of the harness's grants it sends, only what the harness asks for there (its needs on that machine) is kept, since a grant there holds for every agent that runs the harness, and the file here may hold more than this agent's harness needs anywhere else.

The rest of the stream

What of a connection's folder comes is what the receiving machine's own plugin names, and a model on the loopback comes only from the computer running the move. The rest of the stream (the harness's state, the files) goes where only the sandbox reaches.

Yes. Memory, moves and backups keep to their folders by path: they never follow a symbolic link, but a hard link is a regular file, read and carried like any other. Package stores such as pnpm's and uv's make hard links in ordinary folders, so refusing files with more than one link would break harnesses that use them. A harness can link only a file its sandbox shows it, and on Linux the sandbox's bind mounts stop a link across them.

Boxes

Why a move starts a box, and how an agent's traffic leaves one.

Why does moving to a cloud start a box without asking?

lnk agent move --cloud aws says where the agent should run, and a box is how it runs there: asking again would only stop the move halfway. The move says it's starting one, billed by the cloud, before it does. With one box there that this computer made, that's the box; with several, move can't know which, so it lists them and asks for a name.

Why does a move with --image always start a new box?

An image is where a new box's disk starts, so lnk agent move --cloud aws --image web-ready asks for a box made from it, even where you have a box already: one of those wasn't made from it, or was and has changed since. To move onto a box you have, name the box. The image is the setup a box needs before an agent comes, done once and saved, so the agent lands on a box ready for its work.

Why does a cloud's name take only a box this computer made?

A box found in a cloud is yours by its lnk-owner tag, which anyone who can tag machines in a shared account can set. The agent carries its keys, tokens and a new bucket key, so lnk agent move --cloud aws takes its one box unasked only when this computer made it, which lnk box list --json tells by its created time. A box found by its tags is asked about on a terminal, and without one the move stops and names it: typing a box's name is the choice the tag alone can't make. Having reached a box once doesn't count, since lnk agent list reaches every box it lists.

Who makes the ID of an agent that runs on a box?

The machine holding its state, never the computer that moved it. A computer keeps only a note that its agent is on a box, and learns its ID from the box, so one agent never gets two.

Why does a box's lnk agent list show only its own agents?

Giving boxes keys to find each other would let a box broken into reach your account and your other boxes. Your computer shows them all.

How does an agent on a box exit from here?

Through the exit plugin's exit: ssh -R, a SOCKS proxy on the box (exit). With your computer off the proxy isn't there, so it fails closed: no home, no internet, never the box's own address.

Who keeps the exit?

lnk exit, not the agent. The exit is useful with no agent and on any machine you reach, so lnk agent exit --here holds it for the agent, by its ID, and gives it back when the agent leaves or its exit is off. Agents on one box share its exit, and a move gives the old box's back.

Backups

Where a backup keeps the harness's state, what a backup and a restore replace, and what removing an agent keeps.

Where does a backup keep the harness's state?

In one archive in the files folder, .lnk-backup/<harness>.tar, pushed like any file, rather than in a third bucket per cloud. lnk bucket knows two folders and nothing about harnesses, and the state is the harness's own files, which it can read anyway.

What does a backup replace in the clouds?

The archive and every file that changed since the last backup, since the machine running the agent holds its latest copy. A conversation grows every turn, so keeping the old copy would leave it out of every backup after the first. Nothing is deleted from the clouds: a file gone here stays there.

What does going back to a backup replace?

The harness's state wholly, and nothing else. What it learned since is what went wrong, so it's gone. The files folder is yours and keeps its files, and the keys and settings are Link's and never in a backup. The state is unpacked aside first, so a broken archive leaves it as it was.

What does restoring a lost agent bring back?

The agent's ID, files and its harness's state, not its keys. The model key and bot token are asked again, so no key sits in a bucket, even encrypted.

What does removing an agent keep?

lnk agent delete takes everything Link keeps for the agent on this machine, as a move away does, but leaves no note of where it went. Its buckets stay: removing an agent is often a mistake found a day later, and lnk agent restore brings it back from them, as for a machine that's gone; lnk bucket remove deletes them.

Its files

Its files are the person's, so they go only when the person says so: kept, they're put aside where a moved agent's are, for as long, and a files folder they chose stays where it is. Without a terminal, --yes removes the agent and keeps its files unless --files says otherwise.

Decisions of other parts

Each part keeps the decisions about its own code on its own page: