Accounts & Credentials#
This page contains no secret values. It says where each credential is
stored (a secrets.yaml path under sops, or a GitHub Actions repository
secret), what depends on it, and how to rotate and verify it. Cells only Ben
can fill are marked TODO(owner).
Two stores — do not confuse them:
- sops (
secrets.yaml) — decrypted on hosts at activation. Read a value withsops --decrypt --extract '["group"]["key"]' secrets.yaml; write one withsops set secrets.yaml '["group"]["key"]' '"value"'(JSON-encoded). A declared secret must exist before the host builds (eval-time check). - GitHub Actions secrets on
tsunaminoai/nix-flake-final— used only by.github/workflows/*.yml. Set withgh secret set NAME -R tsunaminoai/nix-flake-final.
Summary#
| Dependency | Console | Credential lives in | Breaks when it lapses |
|---|---|---|---|
Cloudflare (account sc2in, zone inaba.network) |
https://dash.cloudflare.com · Zero Trust team sc2in.cloudflareaccess.com |
sops cloudflare/terraform-token, cloudflare/inaba; Actions CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID |
*.inaba.network DNS/Access changes, ACME DNS-01 for inaba.network, docs publishing |
| Microsoft Entra (IdP behind Cloudflare Access) | https://entra.microsoft.com | Ben’s admin account — TODO(owner) |
Nobody can sign in to any *.inaba.network app or to Grafana |
Tailscale (tailnet armadillo-banfish.ts.net) |
https://login.tailscale.com/admin | Admin login TODO(owner); sops tailscale/auth-key; Tailnet Lock signing key on mokou |
New nodes cannot join or get no peer traffic; deploy-rs and SSH paths die |
| FlakeHub (Determinate Systems) | https://flakehub.com | sops nix/flakehub-token (rendered to a netrc by modules/flake/nix/flakehub-netrc.nix) |
nix flake update 401s; cache.flakehub.com silently stops substituting |
GitHub — tsunaminoai (owner) |
https://github.com/tsunaminoai/nix-flake-final | sops github/automation-token; Actions ACTIONS_PAT |
Auto-update PRs stop; docs-deploy status posts fail |
GitHub — sc2ben (machine account) |
https://github.com/settings/keys as sc2ben | Actions SC2_CI_SSH_KEY |
All three GitHub workflows fail to fetch the private corp input |
| BuyVM (myon) | https://manage.buyvm.net (Stallion) | Account login TODO(owner); root console password TODO(owner) |
A billing lapse deletes myon; acquisition egress stalls (kill switch, no leak) |
| Synology DSM (voile) | https://voile:5001 · https://192.168.0.25:5001 | Admin login TODO(owner) |
Backups, the Forgejo mirror (fleet stops converging), Kiwix, RADIUS Wi-Fi, media shares |
| BorgWarehouse (on voile) | https://voile.armadillo-banfish.ts.net:7777 | UI login TODO(owner); sops borgwarehouse/cronjob-key, borgwarehouse/api-key |
Status pushes fail (SystemdUnitFailed pages); no new repos |
| Forgejo mirror (on voile) | http://voile.armadillo-banfish.ts.net:8418 | Mirror-sync credential configured inside Forgejo — TODO(owner) |
release never advances on the mirror; every host’s guard fails closed |
| Bitwarden (Ben’s personal vault) | TODO(owner) (bitwarden.com or self-hosted?) |
sops bitwarden/client-id, client-secret, topsecret, recovery-key |
Wi-Fi PSKs, the XCA passphrase, LUKS recovery passphrases become unreachable |
| Discord (notifications server) | Channel settings → Integrations → Webhooks | sops discord/notify-webhook (also merged into the Alertmanager env template) |
Auto-update failure notices and the critical-alert mirror go silent |
| healthchecks.io | https://healthchecks.io | HEALTHCHECKS_PING_URL inside sops telemetry/healthchecks-env; account TODO(owner) |
Whole-host death of ereshkigal is no longer noticed (dead-man’s switch) |
| Internal service tokens | — | sops paperless/api-token, jellyfin/api-token, openwebui/api-key, telemetry/*, step-ca/password-file, esphome/*, borgmatic/<host>/* |
Documented on each service page |
Cloudflare#
- Three different tokens.
cloudflare/terraform-token(Zone:DNS:Edit oninaba.networkplus Account:Zero Trust:Edit onsc2in) drivesnix run .#terraform-cloudflare.cloudflare/inabais the DNS-only ACME token.CLOUDFLARE_API_TOKEN(GitHub Actions) is the Pages deploy token for projectmaster-documentation(docs.falseblue.com) indocs.yml. None is interchangeable. - Account, zone, Entra group, and IdP IDs are not secret; they are constants in
modules/flake/terraform/default.nix. - Rotate: create a token with the same scope in the dashboard →
sops set(orgh secret set) → for the terraform token runnix run .#terraform-cloudflare -- plan(must succeed with no auth error;applyis the owner’s call) → for the Pages token push a docs change and watchdocs.yml. - Acceptance:
curl -s -H "Authorization: Bearer $(sops --decrypt --extract '["cloudflare"]["terraform-token"]' secrets.yaml)" https://api.cloudflare.com/client/v4/user/tokens/verifycontains"status":"active". - Access app tags must exist before
apply(API error 12130); see Authentication and Family media.
Microsoft Entra#
- Tenant
sc2in; groupssg-voile-personal(family tier) andsg-voile-personal-admins(observability/admin tier). Their Object IDs are inmodules/flake/terraform/default.nix. - Cloudflare Access emits group names as plain strings in the
groupsclaim, and only when thegroupsscope is requested — see Grafana admin via Cloudflare Access. TODO(owner): which Entra account is Global Administrator, and is there a break-glass account?
Tailscale#
- Tailnet Lock is enabled. Sign a new node with
sudo tailscale lock sign <nodekey> <tlpub>on mokou; the locked-out node prints the exact command intailscale lock status. Symptom of an unsigned node: Online in the admin console,tailscale pingto every peer times out,journalctl -u tailscaled | grep tailnet-locksays “locked out”. tailscale/auth-keyis a reusable, tagged auth key consumed bytailscale-autoconnecton first boot (modules/nixos/tailscale/default.nix). Auth keys expire. A stale key makes a freshly flashed or installed node sit at “Logged out” withinvalid key: API key does not exist. Mint a new one at Admin → Settings → Keys,sops set, rebuild the image or host. One-time fallback on the box:sudo tailscale up --ssh.- Acceptance: on the new node
tailscale statuslists peers andtailscale ping mokouanswers. - Removing a host: Admin → Machines → remove (step 2 of the decommissioning runbook).
FlakeHub#
- A ~90-day user token. It gates both flake-input fetches and the
cache.flakehub.comsubstituter, so expiry shows up twice:nix flake updatefails withHTTP error 401 — The provided token has expired, and host builds quietly build far more from source.TODO(owner): expiry date of the token currently in sops (a 2026-07 note said ~mid-September 2026). - Bypass to update the lock without a token: add
--netrc-file /dev/null(anonymous access works for public flakes; thedeterminateinput stays pinned). - Rotate: new token at flakehub.com →
sops set secrets.yaml '["nix"]["flakehub-token"]' '"<token>"'→ rebuild any host (the netrc is a sops template). The value must exist insecrets.yamlbefore rebuilding. - Acceptance:
curl -s -o /dev/null -w '%{http_code}' -H "Authorization: Bearer $(sops --decrypt --extract '["nix"]["flakehub-token"]' secrets.yaml)" https://cache.flakehub.com/nix-cache-infoprints200. - GitHub Actions runners install Nix with
DeterminateSystems/nix-installer-actionand talk to FlakeHub too; a red “Nix Flake Checks” whose log saysFailed to auth to FlakeHubis this token, not the code.
GitHub — tsunaminoai#
github/automation-token: fine-grained PAT (Contents RW, Pull requests RW, Metadata R) used only by the auto-update scheduler on ereshkigal (stage 1). Rotation and the acceptancecurl(must print200) are in the fleet-convergence gotchas; the 2026-08 rotation shipped a dead token because that check was skipped.ACTIONS_PAT(Actions secret): thegitHubTokenforcloudflare/pages-actionindocs.yml, used to post deployment statuses on this repo. Not the automation token.TODO(owner): which account issued it and its scope.github/ssh-key/github/ssh-pub: Ben’s personal fallback SSH key (modules/users/tsunami/ssh.nix), not a CI credential.- Branch protection on
mastermust keep requiring “Nix Flake Checks”, and “Allow auto-merge” must stay on, or automation PRs never merge. ghgotcha: if the activeghaccount issc2ben, the personal repo 404s (“Could not resolve to a Repository”). Rungh auth switch --user tsunaminoai.
GitHub — sc2ben (machine account) and SC2_CI_SSH_KEY#
- Why it exists:
flake.nixinputcorpisgit+ssh://git@github.com/sc2in/corp(transitivelypraxisandPolicyPress). Every flake input is placed in every host’snix.registry(modules/flake/nix/nixos.nix), so evaluating any host fetches it.eval.yml,checks.yml, anddocs.ymleach write the key to~/.ssh/id_ed25519before running Nix. - The key is an account SSH key on
sc2bentitlednff-ci (private flake inputs: corp/praxis/PolicyPress). It inherits sc2ben’s write access to those repos; the accepted mitigation is rotation on leak.TODO(owner): is the private half escrowed anywhere besides the Actions secret? - Rotate:
1.
ssh-keygen -t ed25519 -f nff-ci -N ''2. as sc2ben:gh ssh-key add nff-ci.pub --type authentication --title 'nff-ci (private flake inputs: corp/praxis/PolicyPress)'3.gh secret set SC2_CI_SSH_KEY -R tsunaminoai/nix-flake-final < nff-ci4. delete the old key from sc2ben; shred the local file. - Acceptance:
GIT_SSH_COMMAND='ssh -i nff-ci -o IdentitiesOnly=yes' git ls-remote git@github.com:sc2in/corp HEADprints a ref; then a branch push turnseval.ymlgreen. - ereshkigal holds no copy of this key. Its
om cistubs corp with--override-input flake/corp path:./ci/stub-corp, so the real SC2 compliance module is validated only by GitHub CI. See SDLC §6.5.
BuyVM (myon)#
- Stallion panel: https://manage.buyvm.net — console, rescue ISO library,
reinstall. A root console password was set on 2026-08-07.
TODO(owner): account email, and where the console password is stored. - Public IP
45.61.186.219; its reverse DNS still saysmail.rdp.webportal.cyou(previous tenant). Set it tomyon.falseblue.comin the panel when convenient. - Never install or build on the box: 1 GB OOMs. myon has the rescue-ISO lustrate procedure.
Synology DSM (voile) and BorgWarehouse#
- DSM: https://voile:5001.
TODO(owner): admin account, and where its password and 2FA recovery codes live. - BorgWarehouse has two keys:
borgwarehouse/cronjob-keyis the server’sCRONJOB_KEY(from the.envin/volume1/BorgWarehouse), used by theborgwarehouse-cron-*units;borgwarehouse/api-keyis a read-only account API key (UI → Account → Integrations). Details: Backups. - Acceptance after rotating the cron key, on ereshkigal:
systemctl start borgwarehouse-cron-status && systemctl status borgwarehouse-cron-statusexits 0. - Forgejo mirror health:
curl -fsS http://voile:8418/api/v1/version. If the mirror stops syncing from GitHub, hosts stop converging andAutoUpgradeCompareFailedfires (runbook).TODO(owner): which GitHub credential the Forgejo mirror uses to pull, and when it expires.
Bitwarden#
- Holds: the
FalseBlue-BasicandFalseBlue-IoTWi-Fi PSKs,FalseBlue Root CA Key(XCA database passphrase), the/poolLUKS recovery passphrase on mokou.TODO(owner): also the DSM / BuyVM / FlakeHub / healthchecks logins? - The
bwCLI on Ben’s hosts unlocks from sops-provisioned files (modules/home-manager/tools/bitwarden.nix):bitwarden/client-idandbitwarden/client-secret(API key) plusbitwarden/topsecret(master password file).bitwarden/recovery-keyis the account recovery key. TODO(owner): is Bitwarden Emergency Access configured for a successor, and for whom?
Discord and healthchecks.io#
discord/notify-webhookis read at runtime by the auto-update unit and is also interpolated into ereshkigal’salertmanager-envsops template asDISCORD_WEBHOOK_URL(Alertmanager takes exactly oneEnvironmentFile). Rotate in the channel’s webhook settings →sops set→ redeploy ereshkigal. Acceptance:curl -s -o /dev/null -w '%{http_code}' -H 'Content-Type: application/json' -d '{"content":"webhook test"}' "$(sops --decrypt --extract '["discord"]["notify-webhook"]' secrets.yaml)"prints204.telemetry/healthchecks-envcarriesHEALTHCHECKS_PING_URL; the vmalertWatchdogrule pings it continuously and healthchecks.io alerts when the pings stop. Rotate by creating a new check and updating the env value.TODO(owner): the healthchecks.io account and who receives its alerts.Delivery details: Alerts.
Review cadence#
Review this page quarterly and on any change to the secrets model (SDLC §10). Add a row when a new external dependency lands; delete the sops key and the row when one is retired.