Skip to content

Encryption Envelope Spec (MVP Baseline)

Purpose: - Define one canonical encrypted-field shape for _enc columns and sensitive payloads. - Prevent ad-hoc encryption formats across services.

Scope: - Applies to allocations.ssh_private_key_enc, nodes.access_secret_enc, and any future credential-bearing JSON fields.

1. Envelope Shape

All encrypted JSON payloads must use this object structure:

{
  "v": 1,
  "alg": "AES-256-GCM",
  "key_id": "local-dev-kek-1",
  "nonce": "base64url",
  "ct": "base64url"
}

Field rules: - v: integer schema version (1 for MVP). - alg: encryption algorithm identifier (AES-256-GCM). - key_id: logical KEK identifier used to encrypt/decrypt. - nonce: base64url-encoded GCM nonce. - ct: base64url-encoded ciphertext+tag bytes.

2. Cryptographic Baseline

  • Primitive: AES-256-GCM.
  • Key size: 32 bytes.
  • Nonce size: 12 bytes (GCM standard).
  • AAD: none in MVP.
  • Encoding: base64.RawURLEncoding for nonce and ct.

3. Key Source by Environment

  • Local/dev: static 32-byte key from environment variable (documented in local-dev env examples).
  • Production: KEK material from managed KMS/HSM; key_id must map to KMS versioned key metadata.

4. Rotation Model

  • New writes use current active key_id.
  • Reads must support decrypting any still-supported key_id.
  • Rotation strategy:
  • enable new key for writes
  • background re-encrypt existing rows
  • retire old key only after migration verification

5. Operational Rules

  • Never log plaintext secret values.
  • Never log full envelope payloads at info/error level.
  • Include only key_id and v in diagnostic logs when needed.
  • Invalid envelope payloads must fail closed (treat as decryption error).

6. Go Helper

Canonical implementation lives in: - packages/shared/crypto/envelope.go - packages/shared/crypto/envelope_test.go

All services must use this helper (or strict equivalent) for envelope serialization and AES-GCM operations.