Skip to content

0028 — Nothing writes ConfigMaps or Secrets; written secrets are sealed

Status: Accepted; partly superseded (2026-10-04): sealing is retired, the rule that nothing writes ConfigMaps or Secrets stands; extended by 0034; amended by 0036 Date: 2026-10-02

Superseded (2026-10-04). Sealing is retired entirely: the Sealing port, the KMS sealer, the sluis:binding encryption context and ports.sealer are gone. A dynamic secret is written to the Secrets port (SSM in production) under private/<key>/<ref>, and State holds only the record that names it (design/ports.md). The rule that nothing writes ConfigMaps or Secrets stands. The text below is the decision as it was taken.

Context

Today the service writes Kubernetes objects: workspace credentials, GitHub App private keys, links and the catalogue Apps' tokens are Secrets it updates, and its reports are ConfigMaps. That needs RBAC to write them, makes a GitOps sync the enemy of a ConfigMap whose data a controller rewrites, and has no counterpart on Lambda. It also means a compromise of the process is a write into its namespace.

Decision

ConfigMaps and Secrets are inputs only: the mounted policy, the configuration file, and secrets an operator or an operator-managed system puts in place. The service holds no permission to create or update either.

A secret the console writes (a connected workspace's credential, a GitHub App's key, a link's tokens, a catalogue App's bot token) is sealed: encrypted with AES-GCM under a data key, and stored in the State store (0027) with its record. The data key is wrapped by a key-encryption key the Sealing port holds:

  • KMS on AWS, which an EKS workload can also use through Pod Identity;
  • OpenBao Transit, or a mounted key-encryption key, where KMS is not available.

The store holds only ciphertext and the wrapped data key, so a copy of the store is not a copy of the secrets, and a rotation of the key-encryption key rewraps without touching the plaintext. The signing-key schedule is sealed the same way.

No parameter store (SSM) and no OpenBao key-value mount holds a secret this service writes.

Consequences

The backup of a deployment is the State store and the key-encryption key, both named, instead of five named Secrets. Losing the key-encryption key loses every sealed secret, and an operator is told so in the runbook; the recovery for a lost workspace credential stays Reconnect.

A secret an installation declares as data (for example in a catalogue) is still an input and is read, not written. Pushing a catalogue App's token to a secret store for a consumer is a separate output and is unchanged.

Alternatives considered

SSM Parameter Store for written secrets. Rejected: AWS-only, a second store with its own limits and throttling, and a second place a backup must cover.

OpenBao key-value for written secrets. Rejected: it makes the service's availability depend on the secret store's on the sign-in path.

Store plaintext in the State store and rely on its encryption at rest. Rejected: it puts every secret in the clear for anyone allowed to read the table or the stream.