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

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.

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.