Decisions
Why Link's specs are what they are: first what holds for every spec, then each one's own.
Every spec
Does each spec keep its own rules?
No: one set of rules for every spec (specs.md), and each spec's page says only what its fields are. The box's and the sandbox's specs were first written apart, and differed in where the name went, how a flag and the file combine, and whether the file could name the account; one set takes the better of each, so a new spec is never a new format to learn.
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
Where are an agent's opinions?
In its spec, never in a harness or a bridge. 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?). 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 own opinions are a spec Link ships, printed whole by lnk agent spec, 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 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. 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.
The box spec
Why is a box a file?
So the same box can be made again, and in another cloud: a box was
whatever lnk box start 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 start'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.
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 --region,
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 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_plugin::sandbox, beside Policy, since more than the sandbox
plugin keeps one, and plugins never link each other's code. What a spec
opens on a machine, its refusals and the policy it makes stay the
sandbox plugin's: 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?
No: what a harness runs with is two files of permissions, the agent's
(~/.config/lnk/agents/<agent>/sandbox.toml, lnk agent allow and
deny) and what the harness needs (~/.config/lnk/harnesses/<harness>.toml),
and that is all there is. A harness ships no spec to add yours to, and
none with [commands] allow = ["*"]. lan gives a
harness the network as it is, which no spec section says, since nothing
in a spec reaches the network unfiltered; a harness that needs lan
because its sockets ignore the proxy would stop working behind
[lan]'s proxy.
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 src/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. A reach is the
proxy taking the call, its answer or still dialing after three seconds,
so a case needs no internet.
