# Credentials The swarm's credential store is bao. This page is the overview of how the swarm treats credentials; [`credentials-reference.md`](../../4-implementation/swarm/credentials-reference.md) lists each one: who mints it, who reads it, and how it renews, with the notes for operating them, and [`credentials.md`](../../4-implementation/swarm/credentials.md) holds the store's mechanics. **Store secrets stay off disk.** A process pulls a value from the store at runtime and holds it in memory only. **The mTLS client certificate is the only credential on disk.** Each identity (an agent, a hive, a swarm-level service) has one. It's what logs a principal in to the store, so you place a hive's or service's certificate by hand. The nix option for it takes a path to the certificate, never its bytes, because a literal in a nix expression lands in the world-readable nix store. A hive hands each agent its certificate; the agent pulls everything else itself. **Each credential has a minter, a reader and a renewal story.** Who mints it, who reads it, and whether it renews without an operator are what the [reference table](../../4-implementation/swarm/credentials-reference.md) gives for every credential, with the operating notes that go with them. ## Where a secret lives A swarm generates its credentials in three different places and reads them in a fourth, so "where does this file go" has a different answer per deployment. This page is that answer, one row per secret. Two rules run through all of it. **Private key material and access tokens are paths, never values.** Every option carrying one takes a file path (`*File`), because a literal written into a nix expression ends up in the nix store — world-readable and permanent. No option in this tree accepts one inline, and adding one would be a leak rather than a convenience. The rule is about what must stay secret, not about credentials generally. **Public material is a value**: a certificate, or a public nkey like the queue callout's user key, reaches every client that connects, so the store is a perfectly good place for it. **The generator and the reader typically live in different containers.** They share the host's network namespace, which makes them feel co-located, but their filesystem roots are separate. That's why delivery is a **host-side copy rather than a bind mount**: `nixos-container` refuses to start when a bind source is missing, and a secret minted on another container's first boot doesn't exist yet. Binding it would make one container wait on a file that waits on a container that starts after it. ### Topologies, by who places secrets Read every row of the [reference tables](../../4-implementation/swarm/credentials-reference.md#where-each-secret-lives-on-disk) against one of these. This is a different cut from the [deployment shapes](services.md#deployment-shapes) --- those say _where services run_, these say _who is responsible for a secret file being there_ --- the two lists don't line up one-to-one, and neither is a renaming of the other. | topology | what it means | who places secrets | | ------------------ | ---------------------------------------------------------- | -------------------------------------------------------------------------- | | **all-local** | one host runs the swarm's shared services and its own hive | nobody — the reader itself generates each secret, or a host unit copies it | | **swarm-managed** | the swarm's services run on a host with `swarmctl` | `swarmctl` writes what it owns; the rest is still generated in place | | **hive elsewhere** | a hive that federates with a swarm it doesn't host | the operator provides the file and names it in config | ## Swarm-level and hive-level secrets Swarm-level secrets belong to a swarm service (authelia's keys and client secrets, the collectors' and Grafana's OIDC secrets, the forge and queue credentials); hive-level ones belong to a hive and its agents. Each has a unit that mints or copies it and a reader that holds it only in memory. Which unit, where the value lands and what a missing identity costs, secret by secret: [how each credential reaches its reader](../../4-implementation/swarm/credential-delivery.md). ## The browser UI OpenBao's built-in web UI is at `https://bao-ui./` (`swarm.bao.ui.domain`), for members of authelia's `admins` group only. Log in with the **OIDC** tab, or with a bao token under **Other**. ⚠️ The UI's listener asks for **no client certificate**, because a browser has none to present, so on this one door a bao token is the whole credential: anything on the store's host that can dial loopback, and any `admins` session through the vhost, needs only a token to use the API. Unseal from the host's `bao` CLI; the UI's unseal form gets a 403. The proxy: [the secret store browser UI](../../4-implementation/swarm/credential-delivery.md#the-secret-store-browser-ui). ## Adding a secret State three things, in the row you add to the [reference](../../4-implementation/swarm/credentials-reference.md#where-each-secret-lives-on-disk): **who mints it**, **which container reads it**, and **what happens when they differ**. If they differ, it needs a delivery unit, and the unit copies — it doesn't bind.