infra_wiki

Ownership and security

Which system owns each part of kthx, the identities and federation each part uses, and how built images are signed and admitted.

OpenTofu, Flux and the kthx engine each own part of kthx. Built apps defines the nouns.

Who owns what#

PartOwnerSource of truth
Vessels, tunnels and signing keysOpenTofu, applied by Atlantisterraform/
The engine, quick sites, Target RBAC and the image policyFluxclusters/
App namespaces, releases, DNS records and DatastoresThe engine, through Target APIsIts database
Engine settings, such as Targets and build routesThe engineIts database, exported from the console's Settings page

The engine deletes only namespaces labelled app.kubernetes.io/managed-by: spindrift. Target RBAC cannot touch Secrets, so Datastore credentials reach Apps through External Secrets.

Identities#

CallerCredentialCan
The ownerA console passkey, enrolled once with KTHX_ENGINE_ENROLMENT_TOKENEvery console and MCP action
An agent on /mcpAn agent token, valid 90 daysEvery action except minting tokens, replacing the engine settings, and connecting or probing a Target
kthx on GitHubThe private GitHub App kthx-bot (ID 4576122), installed on jonpulsifer onlyPush branches, open pull requests and run workflows in connected repositories
A quick-site ownerWrite access to the sites bucket, and a Google ID token or tailnet login that the site's marker namesDeploy and delete that site, and call its owner routes
The engine, at quick sitesA projected ServiceAccount token with audience kthx, sent to /api/engine/* on the control hostList and read every quick site with its owner login, and reserve and release names under the quick-site zone. It carries no login, so it opens no site and cannot delete every site.
RowbuttAn agent token from Secret mate-kthx-agent, which mate's pod holds, and a sandbox's federated token for kthx-deployer@bluenose.iam.gserviceaccount.comWhat an agent on /mcp can, without approval, and every quick site it deploys

Secrets#

clusters/offsite/apps/kthx-engine/secret.sops.yaml holds the engine's secrets under their KTHX_ENGINE_ names.

SecretAlso inIf missing
Enrolment tokenNobody can enrol a passkey
Vercel and Cloudflare tokensTheir Targets cannot connect
GitHub App key1Password and nix/secrets/bosun.sops.yaml. Update all three before you delete an old key.kthx has no GitHub App until the console creates one
KTHX_ENGINE_GITHUB_WEBHOOK_SECRETThe engine refuses every GitHub webhook

Quick-site trust#

Quick sites trust Tailscale-User-Login only on kthx.<tailnet>, where KTHX_ADMIN_LOGINS can delete every site and free its name. Names the engine reserves for built apps stay taken. Network policies kthx and kthx-gateway admit only the Tailscale proxy, Gateway, kubelet and namespace pods. Rate limits key reliably only on traffic that arrives through Cloudflare.

The console shares an origin with kthx's /api and /cli on kthx.lolwtf.ca, so kthx's same-origin write guard trusts console script.

The engine's read of quick sites includes each owner login. That login is no secret inside the cluster: any cluster-admin, Rowbutt's sandbox included, can mint the engine's token or read kthx-db. Keeping it from agents is hygiene, and it is no security boundary.

Federation#

The engine runs as ServiceAccount kthx-engine/kthx-engine on offsite, with no GCP or cluster key.

  • GCP: The offsite provider of the fml-pool workload identity pool lets it impersonate kthx-engine-controller@bluenose (terraform/gcp/projects/bluenose/iam.tf).
  • folly: The API server accepts its token as user federated:system:serviceaccount:kthx-engine:kthx-engine (clusters/folly/talos/patches.tf).
  • kthx: The quick-site server accepts a token from offsite's issuer with audience kthx and subject system:serviceaccount:kthx-engine:kthx-engine (clusters/offsite/apps/kthx/helm-release.yaml). It reads the issuer's keys from https://oidc.lolwtf.ca/offsite, and neither API server accepts that audience. The engine sends kthx only that projected token, set by serviceAccount.token.kthxAudience in clusters/offsite/apps/kthx-engine/helm-release.yaml. Its other tokens never reach kthx.
  • Vercel and Cloudflare: none. The engine uses stored tokens.

Quick sites run as kthx-server@bluenose, which reaches only bucket bluenose-kthx.

Supply chain#

  • The engine verifies each Build's provenance, then signs the image digest with a KMS key in the trusted-builds GCP project.
  • Kyverno policy kthx-engine-verify-images rejects an App pod without that signature. It matches pods labelled kthx.dev/deploy on both clusters (clusters/base/platform/kthx-engine-policy/verify-images.yaml).
  • Kyverno reads the image signature from GHCR. A private package without registry access prevents pod admission.
  • On Cloud Run, the Vessel's Binary Authorization policy admits only attested images.
  • github-actions builds reach GCP through the homelab workload identity pool as the workflow kthx-engine-build.yml on main.

Edit on GitHub

↑↓ move ↵ open esc close