apiVersion: v1 kind: ConfigMap metadata: # Before anything reads it: the migration Job (wave 0) and both Deployments (wave 1) mount # these as envFrom, and a pod that starts without them starts with the wrong configuration # rather than with none — envFrom of a missing ConfigMap is a pod that never becomes ready. annotations: argocd.argoproj.io/sync-wave: "-2" name: jarvis-api-config namespace: jarvis data: # Read by the api under this name: the refresh cookie is `Secure` only in production. NODE_ENV: "production" API_PORT: "4000" # One logical keyspace, shared by every pod. Sentinel or a managed single-primary rather than # Cluster: the Socket.IO adapter and the inter-pod bus both publish across what would otherwise # be different slots. # # A SINGLE REDIS IS A SINGLE POINT OF FAILURE FOR EVERY POD AT ONCE. This line is correct for a # managed Redis that fails over behind one address, and correct-but-fragile for one Deployment of # one Redis. It is IGNORED while the two Sentinel lines below are set — and they are, because # `kustomization.yaml` deploys `08-redis-sentinel.yaml`. With a managed Redis instead: remove that # file from `kustomization.yaml`, delete the two lines below, and point this one at it. # # NEVER THIS LINE ALONE WITH 08 DEPLOYED. Its `redis` Service is headless: the name answers with # all three Redis pods, and two of them are read-only replicas. Each api connection lands on one # of the three, so a pod that drew a replica fails every write it makes — `READONLY You can't # write against a read only replica` — while its readiness probe, which only reads, stays green. # That was this file's default while the two lines below were commented out. REDIS_URL: "redis://redis.jarvis.svc.cluster.local:6379" # Set BOTH or neither. Sentinel needs the sentinels and the name of the primary they watch; a # name is refused rather than defaulted, because a client's usual default (`mymaster`) against a # primary called anything else is not an error — it is a pod that never becomes ready, silently. # When these are set, REDIS_URL above is ignored. They name what `08-redis-sentinel.yaml` deploys: # its three sentinels, and `jarvis`, the primary they monitor. REDIS_SENTINELS: "redis-sentinel-0.redis-sentinel.jarvis.svc.cluster.local:26379,redis-sentinel-1.redis-sentinel.jarvis.svc.cluster.local:26379,redis-sentinel-2.redis-sentinel.jarvis.svc.cluster.local:26379" REDIS_SENTINEL_NAME: "jarvis" # ONE, not the two the compose files use, and getting this wrong is a security fault rather than # a cosmetic one. # # It counts the proxies that REWRITE X-Forwarded-For. On compose a request passes the operator's # TLS terminator and then the web container's nginx: two. Here the Ingress routes /api/ straight # at the API Service, so there is one. Set HIGHER than the truth, the API believes a hop that does # not exist — and since X-Forwarded-For is a request header, anyone can then prepend an address of # their choosing and have it written into session records, audit rows, and the key the anonymous # WebAuthn budget counts on. # # It is a STORED setting: this seeds the first boot, and the install screen — which shows the # chain the API actually received — is where it is confirmed. TRUST_PROXY_HOPS: "1" # The seed for the address enrolled agents dial back to. Whatever the install screen stores wins # from then on, so this is a starting value rather than the answer — and it is COMMENTED OUT # because a placeholder is a worse start than none. Seeded as it stood, `jarvis.example.com` was # stored as this instance's address before anybody had opened the install screen, which asks for # the address anyway, prefilled with the one your browser used. Uncomment it only with your real # address. # WEB_ORIGIN: "https://jarvis.example.com" # Where the agent binaries are, filled per pod by an initContainer. Leaving it unset is # supported: everything works except the agent installer, which answers 503 saying no build is # published. AGENT_RELEASE_DIR: "/srv/agent-releases" AGENT_HEARTBEAT_INTERVAL_SEC: "30" # Must stay comfortably below the pod's terminationGracePeriodSeconds (90). Whichever expires # first decides whether a restart leaves a closed transcript or a half-written one. RUN_SHUTDOWN_GRACE_SEC: "25" # The script library ships with api and web 2.2.0: scripts, runs and their results need nothing here. # Its next parts (approval, schedules, assistant tools) are built release by release and stay hidden, # their routes answering 404, until they ship. "true" shows them; it is for the team's own dev stack, # never a customer platform. SCRIPTS_PREVIEW: "false" # How many script jobs may run on machines at once, platform-wide and per organization. "0" stops # dispatching new ones; it cancels nothing. SCRIPT_MAX_RUNNING_JOBS: "200" SCRIPT_MAX_RUNNING_JOBS_PER_ORG: "50" # The browser service — a separate pod on purpose, see 09-browser.yaml. COMMENTED OUT to match # that file: with no browser deployed, setting this only turns "unavailable" into a timeout. # BROWSER_URL: "http://jarvis-browser:3000" # No demo organization and no seeded administrator: the install screen is the way in. The seed # refuses an instance that already has accounts anyway, but a Deployment has no business # furnishing anything. SEED_ON_START: "false" OPENAI_BASE_URL: "https://api.openai.com/v1" OPENAI_MODEL: "gpt-4o" OPENAI_THINKING_LEVEL: "medium" --- apiVersion: v1 kind: ConfigMap metadata: # With the ConfigMap above, for the same reason. annotations: argocd.argoproj.io/sync-wave: "-2" name: jarvis-web-config namespace: jarvis data: # Where the web container's nginx sends /api/, the socket.io handshake and the agent websocket. # # FULLY QUALIFIED, and that is not decoration: nginx's `resolver` does not apply the search list # from /etc/resolv.conf, so a bare `jarvis-api` would not resolve however correct it looks. JARVIS_API_UPSTREAM: "jarvis-api.jarvis.svc.cluster.local:4000" # Who resolves it. The compose default is Docker's embedded DNS at 127.0.0.11, which nothing # listens on inside a pod — every /api/ request answered 502 until this became substitutable. # # Replace with your cluster's DNS ClusterIP: # kubectl -n kube-system get svc kube-dns -o jsonpath='{.spec.clusterIP}' JARVIS_DNS_RESOLVER: "REPLACE_WITH_YOUR_CLUSTER_DNS"