Secrets go only where you grant them
Your secrets, kept on this computer and given only where you grant them. A secret is any value kept private: an npm token, a database password, an API key. A file that names one is never enough to be given it.
lnk plugin add env # the plugin that owns the example environment
mkdir link-secret-example
cd link-secret-example
cat > env.toml <<'EOF'
name = "example"
version = 1
[secrets]
sealed = ["STRIPE_KEY"]
EOF
lnk secret create STRIPE_KEY --internet-host api.stripe.com # enter the value when asked
lnk secret add STRIPE_KEY --env example # give it to the example environment
lnk secret list # secrets and credentials, never values
lnk secret delete STRIPE_KEYWhat it needs
You need the secret plugin: lnk plugin add secret, or any lnk secret
command offers it. The quickstart adds the environment plugin and
declares a local environment to grant the secret to; use a value of
yours when prompted. Environments
explains how a run receives its sealed secrets.
Keep a secret
lnk secret create keeps a secret: its value, asked without echo or read
from its input's first line, and how it is given. It is given to no one
yet. Creating a secret again replaces it whole, its value, how it is
given and every grant.
Grant a secret
lnk secret add gives a kept secret to what a flag names, once for each:
--agent <name>, the agent of that name on this computer, --job <name>
and --env <name>, the job or environment of that name, which the plugin
keeping it vouches for (answering secret.principal). The grant keeps
the agent's ID, or what that plugin vouched for, as it said, run in the
folder lnk secret add was run in: what it's given is that plugin's to
say. A job or environment no installed plugin keeps is refused.
lnk secret remove stops giving it, and the secret stays kept.
An agent
Start an agent once with lnk agent start before granting it a secret,
so it has an ID. An agent is given a secret sealed only, for its host
(--internet-host), and is told a placeholder, never the value. An agent made later with the name
gets none of it. An agent on a box is granted its secrets there (lnk box ssh <box>).
What a plugin keeps
What a plugin keeps may be given a secret plain: with no --internet-host, it's
kept plain and given as that plugin gives it; with --internet-host, sealed, as
an agent's is.
Seal a secret for one host
A secret given to what runs as a variable could be read and sent
wherever its sandbox reaches. A sealed one is added
to requests for one host on their way out through the sandbox's proxy,
by this plugin's bridge outside it (lnk-secret secret bridge), and
what runs never has it.
lnk secret create STRIPE_KEY --internet-host api.stripe.com
# for git over HTTPS, as HTTP Basic credentials with the user x-access-token
lnk secret create GITHUB_TOKEN --internet-host github.com --basic-user x-access-token
# in a header named for an API key, the value as it is
lnk secret create OPENAI_KEY --internet-host api.openai.com --header X-Api-Key
lnk secret add OPENAI_KEY --agent mainHow a sealed secret is given (its host, header and format) is set with its value, never by the file that names it. The proxy adds it to any call to its host, on any path and with any method: whatever runs can use it there as fully as the secret allows, so seal it for the one host it's for, with the narrowest secret that host offers.
Your agents' own credentials
An agent's model credential, its fallback's and its connections' tokens are kept
here too, granted to it: lnk agent start and lnk agent connect add
them, as AGENT_<AGENT>_MODEL_KEY or AGENT_<AGENT>_TELEGRAM_BOT_TOKEN.
When the agent is removed they go with it. When it moves
(lnk agent move) they go with it too, sealed to the machine it goes
to, and are kept there as they were here. A key still where Link kept it
before (the agent's settings, or the Keychain's agent/<agent>/…) is
never read nor removed: the agent's start names it and how to give it
again.
An agent that leaves
A secret granted to an agent that leaves this computer stays here only
while something else is still granted it, or while it is a model key
retained for a host that supplies the agent's models. A retained key
has no live agent grant and lnk secret delete still deletes it.
What you added with
lnk secret add --agent doesn't go with the agent: grant it again
where the agent runs.
Look at one secret
lnk secret show STRIPE_KEY says how one secret is given and where it is
granted, never its value. lnk issue asks it for the state of a secret
named in a failure, so a bug report holds it scrubbed.
Take a secret back
lnk secret delete STRIPE_KEY takes it from this computer at once. A run already started keeps what
it was given: an agent's proxy adds it until the agent next starts
(lnk agent stop, then lnk agent start).
Copies elsewhere
When a move sends an agent's keys to a box or a cloud account, this
computer notes the place for each key first. It does this before
anything is sent, so a move cut short is noted too. lnk secret list
shows where each secret has copies. lnk secret delete asks the
plugin whose moves made them, the harness plugin, to reach each place
(its question secret.copies).
A host that supplies its models
When a host supplies its own models, the model keys stay at the source machine and remain noted there, including on a computer controlling a move from a box. Connection tokens still follow the agent. Older copy records that omit a credential's use cannot distinguish those keys; Link refuses that move and asks you to run it on the machine holding the agent, or move the agent here first.
On a box
It runs lnk secret delete <NAME> there, through lnk box run,
removing only that agent's own key. A box that's gone took its copy
with it, and so did one made again under its name since, which is never
touched. A box without the secret plugin (one readied before boxes got
it) is named, with how to add it: lnk box run <box> -- plugin add secret.
A box it can't reach
A box it can't reach (stopped, or offline) stays noted. lnk secret list shows it as left there, and remove exits non-zero. Run lnk secret delete <NAME> again once the box is up, and it reaches it.
On a cloud account that hosts agents
The key is removed when the agent there is removed or moves away.
remove says so, and the note stays until the agent moves away, or
until remove, signed in as the account the agent went there as, finds
that the account no longer lists it.
Once an agent leaves
Once an agent leaves a box or an account, the keys it took are gone from there, and so is the note. Each agent's notes are its own: an agent made later with a removed agent's name never changes them.
Where secrets are kept
On a Mac, each value is in the Keychain (link, secret/<NAME>).
On Linux, they're kept by the keeper, a user of their own (lnk-secrets)
that no program of yours runs as, once you've made it; until then, and
if you say no, in ~/.lnk/secrets.toml, mode 600, which any
program you run outside a sandbox can read. Either way the store holds how each is given
and where it's granted too.
Nowhere else on the disk
Nowhere else on the disk. An agent's proxy's routes,
~/.lnk/sandbox/<agent>-<harness>.routes.json (600), name its
sealed secrets, its connections' tokens and its model's key, and hold
none: at each call, its bridge reads the one it adds from the store,
only while it's granted the agent, so a secret or grant removed stops
at the next call.
The keeper
Your first lnk secret create on a Linux computer asks once whether to
make the keeper (--yes answers yes). It needs sudo once, and
systemd:
lnk secret keeper # makes lnk-secrets, puts lnk-secret in its place, starts its serviceWhat lnk secret keeper does
It makes the user lnk-secrets, which has no login and runs nothing
else; copies this lnk-secret to /usr/local/lib/lnk/lnk-secret,
root's, which nobody else changes; and starts the service
lnk-secrets, which keeps each user's secrets in
/var/lib/lnk-secrets/<uid>/, readable by it alone, and answers on
/run/lnk-secrets/keeper.sock. A caller there reaches its own user's
secrets alone, and no sandbox has the socket.
Your own file, and upgrades
What you kept in your own file moves to the keeper the first time it's
asked, and the file goes. Run lnk secret keeper again after an
upgrade, to put the new lnk-secret in its place.
On a box
A box made by lnk box create installs and starts the keeper during
setup, together with the worker launcher.
Ordinary-box work runs as separate users without sudo; its proxy and the
keeper stay outside that identity and its sandbox.
A TPM
With /dev/tpmrm0, setup installs tpm2-tools and gives the keeper access
to that device's group. Where the device is root's alone, as on Google
Cloud's Ubuntu image, setup first makes the group lnk-tpm and gives it
the device, now and at every boot (/etc/udev/rules.d/70-lnk-tpm.rules);
a device it can't give a non-root group is refused. Its store is sealed to that TPM; copying the disk
to another TPM cannot open it. Existing plaintext keeper files migrate on
read. A failing TPM never falls back to plaintext. Without a TPM the
keeper's private files remain the fallback.
A sealed route
A sealed route reaches the proxy as ciphertext. The keeper also serves
/run/lnk-secrets/secret.sock, where it checks the proxy's actual UID and
current bridge proof before opening the grant and sending the request.
Only root may bind a grant for a different proxy UID. A replaced or
unbound grant stops opening, and restarting the keeper requires proxies
to bind again.
A no
A no is remembered in machine.toml (keeper = false), so it isn't
asked again; lnk secret keeper makes it later. With no terminal,
nothing is asked: your secrets are kept in your own file, and the
command says so.
Deleted and listed
lnk uninstall deletes what the keeper keeps for you with the secret
plugin's part. lnk secret list lists each, never its value, and says so
when the file can't be read. It lists the credentials every plugin keeps
on this machine too, such as your Link login, and --agent work only the
secrets given to that agent. What can read them, and how they travel,
is in Credentials.
When it goes wrong
What to do when lnk secret refuses a grant, leaves a copy on a box or
a cloud account, or can't reach its keeper is on
Troubleshooting.
Docs
- Interface: How a program is handed the secrets it may use, never through its environment
- Decisions: Why
lnk secretworks the way it does - Troubleshooting: What to do when
lnk secretrefuses a grant, leaves a copy behind, or can't reach its keeper (Secrets)