Secrets Contract
link-secret-contract, the library every program that keeps or gives a
secret reads it through: the store, its grants, the keeper's protocol
and the secret route's types. lnk-secret is its command line
(Secrets).
The store
A user's secrets are kept as files, secrets.toml (mode 600) and its
lock beside it: yours in Link's settings (~/.lnk/secrets.toml,
path), or one the keeper keeps for a user (Store). Each value is in
the Keychain where there is one, its account under secret/
(KEYCHAIN).
Reading it
list gives each secret kept for you, by name: how it's given and
where, never its value, and those no longer kept but with copies left
elsewhere, by their copies alone. for_agent gives what the agent of
an ID is granted, its own or what lnk secret add --agent gave it;
a value the Keychain should hold and doesn't is an error, naming it.
Writing it
add keeps a secret, sealed when it's sealed, given where its grant
says, replacing whatever was kept by that name. keep_own keeps an
agent's own keys, dropping one no longer its own (a connection
disconnected), and is written only when something changed. clear
removes every secret kept for you, and their store, for lnk uninstall.
An agent's own names
own_name names an agent's own secret AGENT_<NAME>_MODEL_KEY,
AGENT_<NAME>_TELEGRAM_BOT_TOKEN or AGENT_<NAME>_WEBHOOK_<HOOK>_SECRET
(and …_PREVIOUS while it rolls). Every agent's own secret's name
starts with AGENT_ (AGENT_PREFIX), and no other's does.
Grants
Grant is where a secret is given (--to), kept with its value, and
Use how an agent is given one granted to it.
An agent's
lnk secret add --agent <name> asks the plugins that keep agents
where the agent of that name is (AGENT, answered with an AgentAt).
agent_here gives its ID, or an error naming the box it's on, where its
secrets are given it.
Another plugin's
For any other kind, lnk secret add --job <name> or --env <name> asks the plugins
built on this one who it is (PRINCIPAL, its Asked the kind:name
and the real folder it was given in). vouched gives the answer, or an
error when no plugin keeps the kind or the one that does refuses it,
and for_principal gives what's granted to it, plain or sealed.
Copies a move gives
copied notes a place (Place, a box or an account by its name) for
each of an agent's own keys before a move's switch sends them there,
never a secret lnk secret add gave it. gone_from takes the note
off once the agent has left that place, and forget_agent takes back
every grant to an agent that leaves or is removed.
Models supplied by a host
retain_own parks selected own uses in the keeper while the agent uses
a hosting account's models. forget_agent removes its live grants but
keeps those values; keep_own restores them for the same stable ID.
remove deletes retained values too. copied_without omits uses the
host never receives, so they are never listed as copied there.
Following a remote move
gone_from_without preserves those uses at the source after the
agent leaves it. Copy records keep each own use so a controller can
follow connection tokens while leaving model keys at their source box.
Older records without a use refuse an excluded-use move before any
copy changes; moving on the source machine, or first moving here,
replaces the ambiguous record.
Removing a secret
remove notes every copy as left before any is tried, and takes each
reached off after, so nothing is forgotten if it's cut short; with an
agent, only that agent's own key goes. It reaches copies by asking the
plugins whose moves gave them (COPIES, answered per copy with a
Reached); with none answering, each is unreached.
Listed for lnk secret list
held gives the secrets kept for you as lnk secret list shows them:
where each is kept, where it's given and where a move left a copy, never
its value. One no longer kept here is listed by the copies left
elsewhere. A store that can't be read, or a keeper that doesn't answer,
is one entry saying so.
The keeper
The keeper runs as lnk-secrets (KEEPER_USER), which holds secrets
and runs nothing else, and answers on /run/lnk-secrets/keeper.sock
(SOCKET) and, for sealed routes, /run/lnk-secrets/secret.sock
(ROUTE_SOCKET). here says whether there's a keeper on this machine,
and running_as_keeper whether this process is it.
Its protocol
A call to whoever holds your secrets is a Request, answered with a
Reply; Holder is who holds them, the keeper or your own file.
serve and serve_with_routes are the keeper's side. A debug build's
tests point it at a keeper of their own with LNK_TEST_KEEPER
(TEST_KEEPER).
At rest
at_rest::has_tpm says whether there's a TPM. at_rest::write seals
first, then atomically replaces and syncs the file and its directories;
without a TPM the file is the keeper's private fallback, never the
working user's. at_rest::read opens a private file, and migrates
plaintext when asked.
The secret route
The route adds a sealed secret to its service's calls on their way out
of a sandbox, served at /_link/secret/ on the proxy (PREFIX) by lnk secret bridge (bridge). route builds one from the secrets handed
with their values and those granted by name, none named twice.
How a secret is given
Sealing is how a sealed secret is given, set with its value: the one
host it's sent to, over HTTPS, the header it goes in, and that header's
value. SecretRule is where a secret may go and how, which a plugin
that owns the service gives (a connection plugin's rule --json) and the
proxy enforces, knowing nothing of the service: its paths
(SecretPath, SecretRest) and header (SecretHeader).
Names on the proxy
proxy_name gives a sealed secret's name on the proxy (stripe-key
for STRIPE_KEY), which check_secret_name holds to 1 to 40 lowercase
letters, digits and -, not starting with -. sealed_url_var gives
the variable what runs calls its service at, LNK_SEALED_<NAME>_URL,
and url that address on a proxy's port.
Placeholders
placeholder gives what a program holds in place of a secret: a tag,
none of the secret, after the part a rule says isn't secret, which a
client may read (secret_prefix).
Bindings
binding::Unit is one agent at a time, and only while bound: a key kept
from a binding that slept proves nothing, and the next agent bound gets
a new one. binding::Keyring wraps the bridge's keyring so a binding's
keeper grants are revoked before its keys are removed.