Skip to content

Secrets & Contracts

Source of truth

hns-platform/inventory/group_vars/all/secrets.yml (the vault_* catalog) and each service's .platform.yml (which env var each side consumes). Update this page when those change.

Names only — never values

This page lists secret names and the wires between services. It contains no secret values, and neither should any documentation. Values live in the Nebion UI (or ansible-vault).

How secrets are stored

Every credential is an env-driven vault_* variable in secrets.yml, following the same lookup-with-default pattern as the rest of configuration. The Nebion UI env var name is the uppercase of the variable name without the vault_ prefix — e.g. vault_stripe_secret_keySTRIPE_SECRET_KEY. Dev defaults are placeholders; production must override them in the Nebion UI.

Cross-service shared-secret contracts

Some secrets are held identically by two services, sometimes under different env-var names on each side of the wire. Rotation must update all holders in one coordinated change — an out-of-order rollout opens a 401/403 window.

Contract Sender side Receiver side Wire Platform var
Internal webhook bearer eventbus NATS_WEBHOOK_SECRET backend INTERNAL_WEBHOOK_SECRET Authorization: Bearer on /api/v1/internal/webhooks/* vault_nats_webhook_secret
User-sync webhook eventbus BACKEND_USER_SYNC_WEBHOOK_SECRET backend USER_SYNC_WEBHOOK_SECRET (and KC KC_USER_SYNC_WEBHOOK_SECRET) X-Webhook-Secret on /api/v1/internal/users/sync vault_user_sync_webhook_secret
Mailer API key eventbus MAILER_API_KEY mailer MAILER_API_KEY X-API-Key vault_mailer_api_key
KC backend client secret admin/quota portals + backend (token-exchange) Keycloak hns-backend client OIDC client secret vault_kc_backend_client_secret

Two contracts deliberately do NOT live here

  • Stripe webhook signature (STRIPE_WEBHOOK_SECRET) is verified once, upstream, in hns-stripe-microservice (the router) and never propagated — destinations trust the router and guard the internal hop with the internal-webhook bearer above. It is therefore not in this platform's secrets.yml.
  • Drupal webhook (DRUPAL_WEBHOOK_SECRET, eventbus → Drupal) belongs to the externally-hosted Drupal webshop, not this cluster.

Backend holds the KC client secret (code vs. orientation)

Per backend's .platform.yml, the backend is a confidential KC client: it sets KEYCLOAK_CLIENT_SECRET (= vault_kc_backend_client_secret) to perform RFC 8693 token-exchange for admin masquerade. This refines the general "backend validates JWTs via JWKS only" description — the code is authoritative.

Platform secret catalog

All defined in secrets.yml (Nebion env var = uppercase, vault_-stripped). Values omitted by design.

Group Variables (env-var names)
Database DB_USER, DB_PASS, KC_DB_USER, KC_DB_PASS
App secrets APP_SECRET, ADMIN_APP_SECRET, QUOTA_APP_SECRET
Keycloak admin KC_ADMIN_USER, KC_ADMIN_PASS, KC_BACKEND_CLIENT_SECRET
Mail MAILER_API_KEY, MAILGUN_API_KEY, SMTP_USERNAME, SMTP_PASSWORD, MAILPIT_UI_AUTH
Push NTFY_BASIC_AUTH
Stripe STRIPE_PUBLISHABLE_KEY, STRIPE_SECRET_KEY
Inter-service webhooks NATS_WEBHOOK_SECRET, USER_SYNC_WEBHOOK_SECRET
Social IdP GOOGLE_*, FACEBOOK_*, APPLE_* (client IDs/secrets + Apple signer)
Waiting-room WAITING_ROOM_DB_PASS, WAITING_ROOM_JWT_SECRET, WAITING_ROOM_BOOTSTRAP_TOKEN, WAITING_ROOM_SEED_EMAIL, WAITING_ROOM_SEED_PASSWORD, WAITING_ROOM_TENANT_API_KEY

S3 keys are referenced but not yet defined

Backend's .platform.yml references vault_s3_access_key / vault_s3_secret_key (with empty defaults), but they are not defined in secrets.yml — S3-backed PDF ticket storage is blocked on credentials. Until they exist, S3_ACCESS_KEY / S3_SECRET_KEY resolve to empty in pods.

Rotation

To rotate a shared secret: set the new value for every holder in the Nebion UI (both env-var names where they differ), then redeploy the affected services together. Rotating one side first guarantees an auth-failure window until the other catches up.


Last Updated: June 2026