Skip to content
Galley

Docs / Install

The master key

How to generate it, what it protects, and what to do if you lose it.

GALLEY_MASTER_KEY is the root of the secret-encryption chain. Every encrypted column in the database — webhook secrets, OAuth tokens, project env vars marked secret: true, the bypass-token plaintext — is wrapped under it.

Generating

openssl rand -hex 32

That’s 32 random bytes encoded as 64 hex characters. The server accepts hex or base64. Anything shorter than 32 bytes is rejected at boot.

Storing

Treat it like a TLS private key. The host filesystem is fine if it’s locked down to root + 0600. A secrets manager (Vault, AWS/GCP/Azure secret manager, 1Password Connect, Doppler) is better — pull at boot via env var.

For Compose, the simplest place is /opt/galley/.env with chmod 600 .env and chown root:root .env. Don’t commit it to git, don’t bake it into images.

What it does

The server treats the master key as a key-encryption key (KEK). For every secret stored:

  1. A fresh data-encryption key is generated.
  2. The plaintext is encrypted with AES-256-GCM under the data key.
  3. The data key is wrapped under the master key.
  4. Both ciphertexts go into the database column.

Decryption reverses the process: unwrap the data key with the master key, decrypt the payload with the data key. The master key is in process memory only; nothing writes it to disk or sends it over the wire.

Backing it up

Back up the master key separately from the database. If both leak together, the secrets are decryptable. If only the DB leaks, the secrets stay opaque.

A reasonable shape:

  • Production master key in a password manager + a sealed envelope in the company safe.
  • Staging key separate from production.
  • Per-developer dev keys in their own machines (don’t share dev keys).

What happens if you lose it

There is no recovery. The secrets are unrecoverable ciphertext.

What’s salvageable:

  • Project + environment + deployment metadata: still readable (only secrets are encrypted, not the rows themselves).
  • GitHub installation IDs: still in the DB. You’ll re-authorize the App when prompted.
  • Webhook secrets: lost. You’ll regenerate them on each git connection (Settings → Git connection → Rotate webhook secret).
  • Project env vars marked secret: lost. Re-enter them.
  • Bypass tokens: lost. Re-generate per project.
  • User passwords: stored as argon2id hashes, not encrypted under the master key. Logins still work.

Recovery plan: bring up the server with a new master key, expect a wave of decrypt errors in the audit log for old secret reads, walk each project through a re-credential pass. Painful, not catastrophic.

Rotating

Rotation isn’t a single command today. The path:

  1. Generate a new master key.
  2. For each encrypted secret you want to rotate, decrypt with the old key (galley keys decrypt) and re-encrypt under the new (galley keys encrypt). Or rotate at the source (re-paste each secret in the dashboard) which forces a re-encrypt.
  3. Swap GALLEY_MASTER_KEY and restart.

Properly online dual-key rotation lands later. For now, rotation is a maintenance window.

Dev / CI keys

Don’t reuse production keys outside production. The server will boot fine with any 32-byte value — generate one per environment. CI runs that need to decrypt should mount their own key, not production’s.