Sync the self-hosting stack

This commit is contained in:
2026-08-27 17:52:23 +02:00
parent af6710d849
commit 02ab18aff1
12 changed files with 1407 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
# The secrets an operator writes. Minted ONCE, mounted by every pod.
#
# ─── READ THIS BEFORE YOU GENERATE ANYTHING ───────────────────────────────────────────────────
#
# There are two Secrets, and the split is not filing: `jarvis-generated` holds the values that are
# random bytes and `jarvis-secrets` — this file — holds the ones that are facts about your estate.
# One of those can be regenerated safely and the other cannot, so they do not share an object.
#
# The three keys below are here as the MANUAL path. If you would rather have them minted for you,
# apply `01-secret-job.yaml` instead and leave the `VAULT_*`/`JWT_*` lines out of this file
# entirely. Do not do both: `04-api.yaml` reads this Secret last on purpose, so a value typed here
# wins over a generated one, and the two quietly disagreeing is the shape of the accident.
#
# `apps/api/init-secrets.cjs` is the Compose generator, and it must NEVER become an initContainer
# here. Its guarantee that a key is minted exactly once is every writer sharing one filesystem.
# Per-pod, on an `emptyDir`, each replica generates its own — and the consequence is not a crash.
# Pod A seals a vault credential under a key pod B does not have; pod B reports that credential as
# corrupt; both pods log nothing at boot, because from each one's point of view everything is fine.
# By the time anybody notices, there are several keys in circulation and no way to tell which
# entries belong to which. `apps/api/mint-k8s-secret.cjs` is the same one-shot done correctly, with
# the API server's 409 standing in for the filesystem's exclusion.
#
# Either way, keep the output. The vault key in particular is the thing every stored credential is
# encrypted under — a database backup that travelled with its own key would be a backup that
# decrypts itself, which is why this lives apart from the database.
#
# VAULT_MASTER_KEY openssl rand -base64 32
# JWT_ACCESS_SECRET openssl rand -base64 48
# JWT_REFRESH_SECRET openssl rand -base64 48
#
# In anything beyond a trial, do not commit this file with values in it. Use SealedSecrets,
# ExternalSecrets, or `kubectl create secret generic` from a password manager.
# ──────────────────────────────────────────────────────────────────────────────────────────────
apiVersion: v1
kind: Secret
metadata:
name: jarvis-secrets
namespace: jarvis
type: Opaque
stringData:
# Everything in the vault is sealed under this. Lose it and the credentials are unreadable;
# leak it and a database dump is enough to read them all.
VAULT_MASTER_KEY: "REPLACE_ME"
JWT_ACCESS_SECRET: "REPLACE_ME"
JWT_REFRESH_SECRET: "REPLACE_ME"
# WHEN THE VAULT KEY WAS CREATED, as an ISO 8601 timestamp or epoch seconds.
#
# Set it when you mint the key, and never move it afterwards. On Compose this is inferred from the
# key file's modification time, which is a good answer on a volume nobody rewrites. Here it is a
# wrong one: the kubelet writes a projected Secret into every pod at start, so the mtime is the
# POD's age and every pod reports a key created seconds ago however old it is.
#
# What that number decides is narrow and serious. An instance with no accounts can have its first
# super-admin claimed by whoever reaches the install screen — unless the vault key is old, which
# means the database has gone missing from an instance that already existed (a bad restore, a PVC
# pointed at the wrong volume) and the claim needs proof of possession instead. Without this line,
# that check reads "brand new instance" on exactly the estate it exists to protect.
VAULT_KEY_CREATED_AT: "REPLACE_ME"
# `connection_limit` is not optional here, and it is the reason this URL is in the Secret rather
# than assembled from parts. Prisma's default pool is per-process: N pods times that default will
# exhaust `max_connections` long before the application is under any real load. Size it as
# (max_connections reserve) ÷ (replicas + maxSurge + jobs).
DATABASE_URL: "postgresql://jarvis:REPLACE_ME@postgres.jarvis.svc.cluster.local:5432/jarvis?schema=public&connection_limit=10&pool_timeout=20"
# Required to create new organizations, users or agents. Without it an instance keeps running
# everything it already has and creates nothing new. See the project README.
JARVIS_LICENSE_KEY: ""
OPENAI_API_KEY: "REPLACE_ME"