Credentials, who mints, holds and checks each

Every credential Link mints, holds or checks: what it proves, who mints it and who holds it, where it lives, how it travels, how long it lasts, how it's revoked and what it may do. A secret is any value kept private, and a credential is a secret used to authorize, so every credential is a secret.

Whose it is

A credential is named by whose it is: a user credential, a machine credential, an agent credential, or a service credential, a service's own and shared by no user.

Each service's own

Each service lists its own secrets: the relay's are in its security, a release's in Releases.

Not built yet

These pages are the design Link's credentials are built to. Where the code doesn't do it yet, the entry says so under Not built yet, with what runs today.

The rules

Each credential is written down here: one Link handles that this page doesn't name is a bug. Below is where a credential may go, and how it gets there; how each is held is in How each is held.

Never printed

Output that describes something (an account, a machine, an agent, a tunnel) never carries a credential, in text or --json, since a person copies it blindly. Logs never carry one, and neither do errors. The only commands that print one are those made to manage credentials, and only when that is the point: lnk auth login prints a code to type in the browser.

Handed on only for its purpose

A credential goes to another program only when this page says so, and then plainly: an access token to the plugin that asked for it, through a pipe, never a terminal. lnk auth account, lnk auth relay and lnk auth token print an access token so; lnk iam machine add prints no credential, since the new machine's own key never leaves it.

Never on a command line

Where every user of the machine sees it. A secret comes from standard input, a file, a variable or the Keychain, and no flag takes one: flags.rs refuses a command whose flag named like a secret takes a value, a file's path aside.

What travels is short and narrow

A credential that lasts never leaves where it's kept; what crosses a network is made for one audience, one purpose and minutes.

Sealed when it crosses

A secret that must reach another machine crosses only sealed (Sealed, HPKE: X25519, HKDF-SHA256, ChaCha20-Poly1305) to a key the receiving end makes for that one transfer and says first. A machine drops its receiver when that receiving process ends.

A hosting account's receiver

A hosting account keeps an independent per-move receiver for restart, deletes it after commit where possible, and refuses reads after 24 hours even before its sweep removes it.

A move's switch

This covers a move's switch (lnk agent receive), which refuses one unsealed. A computer in the middle or a log reads no switch secrets; a captured switch requires its receiver, independently of Home's lasting encryption key.

Its records

The complete switch is encrypted in authenticated 64 KiB records, including its manifest, harness home and linked connection state. An authenticated final record is required before the switch succeeds; missing, altered or reordered records fail. The preliminary copy still carries files over SSH or the hosting account's TLS, without the switch's harness state and connection links.

For a machine not there yet

What must wait for a machine that isn't there yet, a connection's token you connect in the browser, is sealed instead to a key the machine keeps for it, its sealing key.

Short bearer tokens

Short bearer tokens made to be shown, to one audience over TLS, are the exception, by purpose.

Carried only by what's made to carry it

A secret travels over SSH (a move's stream) or TLS (a service, a grant read once), and to a program on a pipe or standard input: never on a command line, through the relay, in a bucket or in a log.

No child carries one it wasn't given

A program Link runs gets Link's environment but every variable named like a secret's (a word of it TOKEN, KEY, SECRET, PASSWORD, PASS, AUTH or CREDENTIALS, or …DATABASE_URL; not one ending _FILE, _PATH, _DIR or _SOCK) and every one Link read a secret from; a variable read as a program starts, on one thread, leaves its environment.

Link's own programs (lnk, a plugin, a tool) are handed every secret the program that started them holds, on a pipe (Secrets' Interface).

A program that reads its environment

A program that reads one from its environment and nowhere else gets it there, each place listed by link-secret-value's audit with why: a cloud's command line (aws, gcloud, rclone) its sign-in, a tool's server (lnk sandbox tool) its environment as you gave it, and the sandbox's own stages the environment their caller gave the program.

How each is held

What every one of Link's programs does with a secret it holds, and where a secret is kept.

Held by a process safe to hold them

Every one of Link's programs makes itself so first, in main (process::guard): it writes no core dump and isn't dumpable, so no other process of its user reads its memory, its environment or its descriptors (the kernel makes its /proc/<pid> root's). A program it runs is dumpable again once it starts.

Its memory out of swap

It locks its memory out of swap where it may lock all of it: its limit on locked memory unlimited (the relay's unit sets it, and the relay runs no program of anyone else's) or CAP_IPC_LOCK held (a service run as root), never by raising a limit a program it runs would inherit.

LNK_SECRET_DUMPABLE

A debug build with LNK_SECRET_DUMPABLE set is left as it is, for a debugger; a release build never reads it.

Held in its type, by where it may go

In Rust, every secret is in one of three types of the link-secret-value library (which link_plugin re-exports), and a wrong way doesn't compile.

OpaqueSecret

One Link uses and nobody reads (an agent's proof secret, a binding's, a service's signing secret, what a holder's placeholders are made with): it signs and checks, and there's no way to its bytes.

Secret

A value another program or service must be given (a model's key, a connection's token, a password): read only by asking for it by name (expose), at a place link-secret-value's audit lists for its folder; copied only by saying so (duplicate).

Sealed

Either of them encrypted to one recipient's key (Sealed when it crosses): the only form that crosses to another machine.

What none of them does

None prints what it holds: Debug writes <redacted>, and none has Display, so what takes one (format!("{}"), a header) doesn't compile; nor Clone, nor Serialize. Its memory is zeroed when it's dropped, and it's compared in constant time.

Where it's written as its value

A Secret is written as its value only where it's kept, a file of mode 600 or the keyring, by bare, each place in the audit's list.

The audit

Every folder's tests run the audit over its own code: a new place a secret is read, written as its value, or given to a child fails until it's listed, which a review sees. None of the old ways passes at all (a copy of an exposed value, one on a command line or put in a child's environment by hand, a redacting Debug written by hand, zeroize outside the library).

A field named like a secret

A field named like a secret holds one of the three types, or is in the audit's list with what it holds instead (a public key, a name).

Accounts are their ids

An account's id, acct- and 32 hex digits, never changes; its username can, at will. Whatever remembers who someone is keeps the id, or an email the account service has verified, never a username.

Kept where only its keeper reads it

At rest a secret is in the Keychain on a Mac, and on Linux in files of a user of its own, lnk-secrets, which the work never runs as; a box's are sealed to its TPM where it has one (At rest).

Always findable

lnk secret list lists every credential this machine holds: what it is, where it's kept and what it may do, never its value. So nobody digs through hidden folders for their own.

Where each is listed from

lnk secret list asks lnk uninstall --parts --json, where each of Link's plugins keeping something says so by part (Plugin Contracts): each part names the credentials it holds, as {"what", "of", "kept", "may"}, never a value. So the list is what the plugins installed here hold, each saying its own, and none reads another's files.

Accounts, harness and cloud

The accounts plugin lists this machine's key, or a relay operator's token; the harness plugin each agent's key while it's bound; the cloud plugin each cloud account's keys.

Box, bucket and measure

The box plugin lists Link's SSH key for boxes and the box SSH secret that signs its certificates; the bucket plugin each bucket's keys and encryption password; the measure plugin an export's headers.

Secret

The secret plugin lists each secret lnk secret keeps, where (the Keychain, the keeper or your file), and where it's granted: agents' model and fallback keys and their connections' tokens among them.

OpenClaw's, Hermes's and DeepSeek Harness's adapters list what their harness keeps in each agent's home: OpenClaw's gateway token, Hermes's API server key and its Signal relay's secret, DeepSeek Harness's copy of the endpoint's key. An adapter finds the agents' homes for its harness with link_harness_contract::harness::homes, and lists them as the agents part's credentials; the folders are the harness plugin's.

Who proves who they are

What each kind of caller proves itself with, and to whom. Apart from these are held credentials: yours, for other services (model keys, connection tokens, cloud accounts, bucket keys), which Link keeps and uses but never mints.

A person

Their sign-in to the account service, in a browser, which is the service's own, proves them to the account service.

A machine

Its machine key, signing for access tokens, proves it to the account service, then to the relay and services with access tokens.

A visitor to a tunnel

An access token from their sign-in, then the relay's visitor pass, proves them to the relay.

A relay's user, an agent and a service

A relay's own user proves themselves to that relay with an operator token, an agent to its bridges with its agent key, and a service to another service with a signing key of its own, as its own docs say.

Credential kinds

The credential catalogue gives every kind a stable identifier and a link to its owning explanation. Credential views use these types while keeping their existing display labels; the inventory is generated from the same declaration as those runtime values.

Compatibility with outside plugins

The what field remains a JSON string. Known labels resolve to their catalogue kinds; older or outside labels remain readable and round-trip unchanged. Link's own code constructs a credential view from an explicit kind rather than inventing another label.

What each may do

What each credential opens at the account service, the relay, another service and an agent's bridges; a dash is nothing there. Why it works this way: Accounts' decisions. How the relay checks what it's given: Tunnels: accounts and tokens.

CredentialAccount serviceRelayA serviceA bridge
Machine keyAccess tokens, by assertion———
Access token, tunnel—Open and serve tunnels——
Access token, share—Share links——
Access token, visit—Visit tunnels that let its account in——
Access token, apiIts API, as the machine———
Access token, identify——Say who it is, to its audience alone—
Visitor's pass—Visit one tunnel——
Operator token—Tunnels and share links, as its user——
Agent key———Its agent's calls

Docs

  • A Machine: The credentials that prove a machine is one signed in to its account: its machine key and the assertion it signs with it, the sealing key what's sent to it is sealed to, and the device code of its login
  • The Access Token: The one short token, for every audience: a JWT the account service signs, naming whose it is, for whom, for what, and until when
  • On the Relay: The credentials a relay takes beside access tokens: a visitor's pass, an operator token, the passwords of tunnels and share links, and a share link's address
  • An Agent: What proves a call comes from an agent, and what keeps another program from acting as it: its key, its memory's embedding proof and its sandbox proxy's secret
  • Held Credentials: Yours, for other services
  • At Rest: Where a secret is kept on a machine, and who there can read it
  • A Webhook's Secret: What proves a post from outside comes from the sender that holds the secret, and the one way every receiver of Link's checks it