Decisions
Why Link's specs are what they are: first what holds for every spec, then each one's own.
Every spec
What holds for every spec: one set of rules, one type for a command's arguments, and tests that are files.
Does each spec keep its own rules?
Every spec follows the same rules for names, paths and combining a file with command-line arguments. Each spec's page describes its own fields, so learning another spec does not require learning another configuration format.
Does the command line keep its own parsing beside the specs?
No: every command's arguments are one type, deriving clap's Args
and serde's Deserialize, and its code takes that type whether the
values came from flags, a file or a test. The flags, the file, the help
and the tests all come from it, so the command line does nothing a
file can't say, and a change to the type is a change to all four.
Does every command get --spec?
No: every command gets the type, and a published spec only where the command line is complex enough to write down, boxes and sandboxes now. Since every command's type is already a spec, publishing another needs no new wiring.
What does a field left out of a spec mean?
The least that works: the narrowest access and the lowest cost,
not whatever the command did before. Wide open is written down, as
["*"], so reading a spec shows everything it opens.
Is where a thing runs, or whose it is, in its spec?
No: accounts, secrets and what Link picks per run (which box, the upstream) are the command's arguments, never a spec's fields, so a spec handed to someone else holds nothing of yours. What it grants on their machine (their tools, ports and network) is shown to them before it runs.
How are specs tested?
By TOML files, each a spec and what must come of it, read by one runner per group, so a new case is a file and not new test code.
The agent spec
Why an agent's opinions are a spec, how specs meet, and who picks where an agent's things are kept.
Where are an agent's opinions?
In its spec, never in a harness or a bridge.
A decision, or an opinion
A decision is what anyone with the whole evidence would choose (a platform's documented limit on a bot's messages, a guard against a broken tool server), and lives in the code; a model's own figures are facts too, kept as data Link ships and you correct (Where does a model's window come from?).
What an opinion is
An opinion is a trade reasonable people make differently, most often cost against what the agent can do, latency against memory, or autonomy against control: how much of the window to pay for, how many rounds a turn may take, a reaction while it answers. Each is a key of the agent spec, and the test for which is which: would a user with other constraints, looking at the same facts, choose differently?
Link's opinions, as a spec
Link's own opinions are a spec Link ships, printed whole by lnk agent show --opinions, so someone with the opposite ones keeps every decision and
changes only a file. Specs meet as sets: Link's, then a team's (what
its agents share), then the agent's own, a later key winning, and the
union must be whole. A missing key means Link's, not nothing, so a
spec of yours holds only what you'd change; extends = "none" is for
someone who wants none of Link's. Two teams that disagree refuse to
guess, and the agent's own spec settles it.
Thinking and the window
Thinking is one: on, a little, for a model that reasons, since the
better answer is usually worth a few hundred tokens, and off for one
that doesn't. The window is another: Link's spec sends at most 128,000
tokens of a conversation, so a long one never runs up a surprise bill
on a model that takes a million; someone who'd rather pay for more
memory sets [window] max higher.
Compaction and the rest
When a long conversation is compacted, and whether the model saves to its notes first, are opinions too: that it summarizes rather than forgets is a decision, but at three quarters of the window or at half, and with an extra call to save notes or without, are trades of cost against memory.
Each a key
How often a lease
is renewed and when it lapses, how often a streamed answer is shown
again, whether the scheduler asks first and in which time zone, and how
much of a model's window to keep for the answer or for counting roughly
are opinions too, each a key. Link's spec has no [model] allow, so any model may run: "the default
only" would refuse lnk agent start --model.
Who picks where an agent's things are kept?
The agent prefers; the machine decides.
A preference, and a fact
Where a thing is kept is an
opinion of the agent's (a conversation in SQLite to search it, or in a
file to read it) and a fact of the machine's (a machine that runs
agents only while they answer keeps nothing on its own disk). So the
agent's [storage] names a place for each collection, and the
machine's lists what it supports, its default first: the preference
holds where it can, and the machine's default where it can't.
It moves unchanged
The agent never names where it runs, so a spec moves with it unchanged, and what a machine overrode is printed, never written back into it.
Why are places collections, and not formats?
How a place keeps a thing is its driver's decision. Asking for a
conversation as a log or compressed named a format, which held only on
a disk: one place a collection says what's wanted, and each driver
keeps it its own way (on file, a conversation is a file only added
to). Six collections, each one folder on file, give the agent's
tools one way into each.
Why is a new agent a personal agent?
Most agents are messaged, not set to work in a repository, and an
agent's first start is where its defaults are chosen. So a new agent's
configuration saves concrete personal defaults in [behavior], beside
setup in Config/agent.toml. Owners can read and edit their choices
without resolving a profile pointer. Sandbox permissions stay separate
in Config/sandbox.toml. Built-in tools start off, and adding one asks
before widening permissions. Deleting either file recreates defaults,
with missing permissions independently remaining closed.
The box spec
Why a box is a file, what's in it, and how a cloud's table and the matching work.
Why is a box a file?
So the same box can be made again, and in another cloud: a box was
whatever lnk box create had in its flags, and t3.small means nothing
to Google Cloud. A box spec says what machine you rent, as you'd
describe one at a store, and any box prints the spec that makes it
again.
YAML, JSON or TOML?
TOML. Every file Link keeps is TOML, the toml crate is in already,
YAML's main Rust crate is unmaintained, and JSON holds no comments.
What is in a box spec?
The machine you rent and the lnk on it, nothing that runs there:
agents, models, tunnels, ports, exits and setup scripts belong to the
plugins that run them.
Which is the source of truth, the file or the flags?
The file. lnk box create's flags are the spec's fields, one type read
from TOML and from the command line, and a test keeps them equal, so
the command line does nothing a file can't say.
How do the default and a cloud's own table combine?
[machine.<cloud>] replaces [machine]'s fields, each whole, so a
cloud's memory range never mixes with the default's. A cloud's size
wins over CPUs, memory, GPUs and arch, which a machine type has built
in, and giving both in one table is an error, since one would be
ignored.
Any cloud's kind
A cloud's table is named by the cloud's kind, any kind an adapter can
have, never from a list in the box plugin: a cloud whose adapter comes
from outside Link gets its own table, and its --size and --place,
with no change to lnk box. A table for another cloud has
no say; a field the table doesn't have is refused as in [machine].
Is the account or the place in the file?
Not the account, which is whose box, not what box: a spec handed to
someone else means the same for them. The region is, in each cloud's
table, since a region names one cloud's place. --name stays the one
box's exact name for the same reason, and the spec's name is what its
boxes' names start with.
min, prefer and max, or min and max?
min and max: the box gets the smallest that fits, so a preferred
size is a min, and max is the most you'll pay for when a cloud has
nothing at the minimum.
Where does matching live?
In the box plugin, which knows no cloud: each adapter lists what it has
there (sizes), in its own order of preference, and lnk box picks the
same way for every cloud. A spec with no CPUs, memory or GPUs gets the
adapter's default size, so an empty file makes the box Link made before
specs, and an adapter without sizes still makes a pinned or default
size.
Where does lnk box spec read a box from?
Never from the box, where whatever runs could write the spec you make the next boxes from. What it reads instead: Security.
Why can't a spec install an older lnk?
A spec may be someone else's, and an older release may have a known
hole. So only a version you type yourself can be older than this
computer's lnk ([lnk]).
The sandbox spec
Why a sandbox is a spec, where its type lives, and what a spec may never open.
Why is a sandbox a spec?
A sandbox is written down once, as a TOML file, then run, checked and
handed to someone else as it is. Each section is a grant and a section
left out is closed, so what a sandbox may do is exactly what the file
says. Every flag is a key of the same spec, so the command line can't
say what a file can't. A box's spec and a sandbox's follow the same
rules: TOML, strict, a missing key the least, "*" writing wide open
down.
Where does a spec's type live?
In link_sandbox_client, beside Confinement, since more than the
sandbox plugin keeps one, and plugins never link each other's code. What
a spec opens on a machine is one function there, Confinement::of; the
refusals that need the machine itself (a write that leads out, a program
it could replace) stay the sandbox plugin's, checked as it wraps:
another plugin has a spec checked by running lnk sandbox, never by its
own copy of the rules.
Is a harness's sandbox a spec?
Yes: what a harness runs with is two specs joined, the agent's
(~/Link/Agents/<agent>/Config/sandbox.toml, lnk agent allow and
deny) and what the harness needs (~/.lnk/harnesses/<harness>.toml),
and that is all there is. A harness ships no spec of its own to add
yours to, and none with [commands] allow = ["*"]: it says each need
in lnk-<harness> needs as a spec's sections, with why, and Link asks
you for it. A harness whose sockets ignore the proxy needs
[network] mode = "host", the network as it is, since [lan] goes
through the proxy too.
Why is a missing [commands] closed in a file, but open with flags?
A spec says everything a sandbox may do, programs too, so a file
without [commands] runs nothing. Flags alone are how a person, or
Link itself for an agent's memory, runs one program as before, and
listing every program a shell starts would make lnk sandbox run --write . -- bash useless; --command closes it when wanted.
Why are a spec's refusals for files, not flags?
A file may be anyone's: one handed over could write ~/.ssh or the
folder it's in, then widen its own next run. Flags are typed by you, or
by Link for an agent's own folder, which an agent's sandbox must reach.
So a file is checked whole, with the flags beside it, and shown before
its first run, while Link's settings stay closed to both.
Why is a spec not run before shown first, and known by what it opens?
Specs are written to be handed on, so one can come from anyone, and
what a file opens depends on where it is (relative folders), whose home
~ is, and which PATH finds its programs. Its text, or a hash of it,
says nothing of that. So Link remembers the grants you allowed, every
folder resolved through links and every program as found, and asks
again when a spec opens anything else, a link moved under a folder it
names included.
Why are folders of other programs' sockets refused in a spec?
/tmp, /run, /var/run, /var/folders, $XDG_RUNTIME_DIR and the
temp folder hold the sockets of a session's bus, Docker, an SSH agent
and a display, and on Linux a socket connects with a read grant alone.
What answers there runs what it's sent outside the sandbox, so a spec
can neither read nor write them, nor a folder holding one, / among
them.
Why does a spec never run anything, nor turn the sandbox off?
One spec is one sandbox. Setup that needs other grants is a second spec, run as its own sandbox before the first, so neither holds the other's grants. And no key runs without a sandbox: to run without one, don't run the sandbox.
Why are a spec case's expectations check's own flags?
A case in tests/specs/sandbox is a spec and what must come of it.
Each expectation (a host reached or refused, a program run or refused,
a folder's dot files written or not) is a hidden flag of lnk sandbox check, a probe beside its own, so a case runs through the same sandbox
and probes a person does, and a new case is a file.
What counts as reached
A reach is the proxy taking the call, its answer or still dialing after three seconds, so a case needs no internet. A refused host is reached when the proxy answers anything but its 403, but a name with no address here only when it connects: the proxy's 502 for a name it can't resolve dialed nothing, so a machine without DNS doesn't see a leak.
The tunnel spec
Why a tunnel is a grant written down, and who it lets in.
Why does a tunnel get a spec?
A tunnel is a grant: who may reach what runs on your machine. A grant written in a file is read before it's opened, kept in git and handed on, as a sandbox's is. Its spec holds only what a tunnel opens and to whom; what it serves, the relay and a domain of yours stay the command's.
Why is "anyone" ["*"], and not public = true?
Every spec writes wide open the same way, so reading a spec shows everything it opens. A second key for the same thing would be two ways to say one thing, and a file that set both would need a rule for which wins.
Why does a tunnel with no list let in only you?
Left out means the least that works, and the least a tunnel can do is
serve its owner. Nothing goes public by accident, which is what
requiring --public, --account or --auth was for, without an error
to read first.
Why are visitors Link accounts and emails?
A tunnel is let into by people, and a person's identity in Link is their account. A username is resolved to the account's id once, so a name changing hands lets no one in; an email is never looked up, so the list leaks nothing about who has an account. The relay then needs no sign-in of its own.
The relay spec
Why a relay's limits are opinions with Link's values, and what stays the command's.
Why does the relay get a spec?
How long a token may last and how much each user may ask are opinions: an operator with other users, other machines or another threat would choose differently, looking at the same facts. So each is a key with Link's value, not a constant, and the rest of the relay's limits stay decisions in its code.
Why does a key left out mean Link's, not least?
A relay's spec holds opinions, as an agent's does, not grants. The least of a limit is a relay that serves nothing, which no one means by leaving a key out. So a missing key is Link's value, and an empty file is the relay Local Link runs.
Why is a token's longest life the relay's, and not the tunnel's?
The relay is what checks a token, so it's the one that says how long a token it takes may last. The account service says how long it makes them, which is shorter; a tunnel only asks for one, and has no say.
Why aren't the relay's domain and account service in it?
They say where the relay is and whom it trusts, as an account names who a box is for: the command's, never a spec's, so a spec handed to another operator holds nothing of the first's.