-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path.env.example
More file actions
299 lines (283 loc) · 18.9 KB
/
Copy path.env.example
File metadata and controls
299 lines (283 loc) · 18.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# ---------------------------------------------------------------------------
# Local development environment contract. Copy to .env and fill in.
# .env is gitignored; every key the app or docker-compose reads is listed here
# with a SAFE PLACEHOLDER. Credentials have no defaults — missing values fail
# fast. NOTE: never put an inline "# comment" after a value; some parsers fold
# it into the value. Keep annotations on their own lines.
# ---------------------------------------------------------------------------
# --- Postgres (docker-compose) ---
# Host ports follow the 34xxx project range (34432 echoes conventional 5432).
POSTGRES_PORT=34432
POSTGRES_DB=spire
POSTGRES_USER=spire
POSTGRES_PASSWORD=CHANGE_ME
# Schema-scoped role for the gateway: it owns ONLY the `gateway` schema (its webhook
# registry) and is never granted the `orchestrator` schema, so a compromised edge
# cannot read the encrypted API-token registry. Provisioned by infra/postgres-init on
# a FRESH volume; for an existing volume run once (as the superuser):
# CREATE ROLE "gateway" LOGIN PASSWORD '<GATEWAY_POSTGRES_PASSWORD>';
# CREATE SCHEMA IF NOT EXISTS gateway AUTHORIZATION "gateway";
GATEWAY_POSTGRES_USER=gateway
GATEWAY_POSTGRES_PASSWORD=CHANGE_ME
# --- Redpanda / Kafka (docker-compose) ---
KAFKA_PORT=34092
# Services read the broker from here in dev (default localhost:34092):
# KAFKA_BOOTSTRAP_SERVERS=localhost:34092
# --- Service HTTP ports (quarkus dev; 34xxx range; distinct per service) ---
ORCHESTRATOR_HTTP_PORT=34080
GATEWAY_HTTP_PORT=34081
WORKER_HTTP_PORT=34082
# The run worker (factory M0). SPIRE_-prefixed because it is read by that service's own
# application.yml rather than by docker-compose port mapping, unlike the three above.
SPIRE_RUN_WORKER_HTTP_PORT=34083
# The factory (orchestrator side): the agent image per harness name, and the wall-clock cap a run
# is given. The image is environment-specific (a registry prefix, a digest); the cap is a tuning
# knob. Both default in application.yml to what the runbook builds locally (SMOKE-TEST Mode Q).
# SPIRE_FACTORY_AGENT_IMAGE_CODEX=spire-agent-codex:latest
# SPIRE_FACTORY_WALL_CLOCK_SECONDS=1800
# What a /fix run uses (FR-F27). NO DEFAULTS, and the emptiness is the opt-in: the REST endpoint
# takes the harness and the model from its request body, and /fix has no request -- letting a
# commenter choose the model would let them choose the price. A deployment that has not set both
# has not enabled /fix, and the command refuses naming the key that is missing rather than
# picking one. The harness must be a key of SPIRE_FACTORY_AGENT_IMAGE_*, and the model must have
# usable pricing, or the run is refused before a row is written or a token moves.
# SPIRE_FACTORY_FIX_HARNESS=codex
# SPIRE_FACTORY_FIX_MODEL=
# The run worker: the publisher image (the other half of the run unit) — required in prod, the
# locally built tag in dev and test — and the longest wall clock a command may carry, which sizes
# the channel's ack budget and must not be below the orchestrator's SPIRE_FACTORY_WALL_CLOCK_SECONDS.
# The worker refuses to start without the image, and the ack budget it checks at startup counts the
# publisher's 300 s drain window on top of the wall clock.
SPIRE_PUBLISHER_IMAGE=spire-publisher:latest
# SPIRE_RUN_MAX_WALL_CLOCK_SECONDS=1800
# How long a run's live transcript is kept before the hourly sweep trims it (default 7 days).
# The run stream is high-volume by design (ADR-034): it is the agent's own output, so it is bounded
# by a TTL and is NOT the durable log — a run's outcome lives in factory_run and llm_charge and
# outlives this. Clamped to a minimum of one day, because a zero or negative window would delete
# every transcript on the next tick.
# SPIRE_RUN_TRANSCRIPT_RETENTION_DAYS=7
# How often that sweep runs. Only worth changing if the retention window is very short.
# SPIRE_RUN_TRANSCRIPT_SWEEP_INTERVAL=1h
# The largest self-reported usage this deployment will PRICE for one run. Unset means unlimited,
# as every ADR-025 cap does, and any non-positive value means the same.
#
# The agent reports its own token usage, parsed from a container it runs shell in at full access.
# That was harmless while usage was telemetry; a run now writes to llm_charge, which the spend cap
# reads -- so a fabricated multi-billion-token line would price high enough to refuse every paid
# call until the rolling window drains, taking out the reviewer as well as the factory. Above this
# ceiling a run is recorded UNKNOWN rather than priced; it still leaves a row and still counts on
# the call axis, so the guard cannot itself become a way to spend unseen.
#
# No default, because a plausible one would be a number this project invented about your models.
# SPIRE_RUN_MAX_REPORTED_TOKENS=50000000
# How long a harness credential rests when its provider rate-limited it WITHOUT saying for how long
# (default 900s). Bounded rather than indefinite: an unstated limit is still a promise that capacity
# returns, and resting the member for ever would turn the recoverable exhaustion state into the
# permanent one -- which is the collapse the pool's two states exist to prevent. A provider that
# states a time overrides this. Note that no shipped harness reports a rate limit distinctly yet, so
# today this applies only when an operator rests a member by hand; see
# techdebt/spire-orchestrator/4-2-no-harness-reports-a-rate-limit-so-the-pool-only-heals-by-hand.md
# SPIRE_RUN_CREDENTIAL_RATE_LIMIT_DEFAULT_SECONDS=900
# How often the run worker advances the heartbeat on the leases it holds (default 30s).
# It must stay well below whatever staleness threshold the orphan watchdog uses: a live hour-long
# run whose heartbeat lapses reads as an orphan, and the answer to an orphan is to destroy it -- so
# the failure mode of a slow heartbeat is killing real work.
# SPIRE_RUN_LEASE_HEARTBEAT=30s
# How long a lease may go un-heartbeated before its sandbox is treated as an orphan and reclaimed
# (default 600s). Must be at least four times SPIRE_RUN_LEASE_HEARTBEAT, and the worker refuses to
# start otherwise: the two are one decision made in two places, and a threshold that does not
# comfortably exceed the interval destroys running work while looking like it is working.
# SPIRE_RUN_ORPHAN_STALE_AFTER_SECONDS=600
# How often the watchdog looks (default 5m). Sweeping faster than a unit can become an orphan only
# costs container listings.
# SPIRE_RUN_ORPHAN_SWEEP=5m
# Turns the watchdog off. It destroys containers on a timer, so an operator who sees it misbehave
# needs a control rather than a workaround; disabling it is logged at startup so it cannot be off
# by accident. A disabled watchdog means an abandoned sandbox keeps its credentials until somebody
# removes it by hand.
# SPIRE_RUN_ORPHAN_WATCHDOG_ENABLED=true
# How long either publish path waits for the broker to acknowledge a run result (default 30s). ONE
# number: the dispatcher and the reclamation reporter both read it, so they cannot drift. A
# non-positive value is refused at startup, because it would time out instantly and lose every
# result while the configuration looked correct.
# SPIRE_RUN_RESULT_ACK_SECONDS=30
# --- The corporate environment for run units (FR-F14) ---
# All optional; unset means absent. NONE of this is baked into an image: the agent and publisher
# images are what an operator derives their own toolchain from, so a proxy or a trust store built
# into one would follow it into every deployment that pulls it. The build fails if one appears
# there (NoCorporateEnvironmentIsBakedIntoAnImageTest).
#
# A path ON THE WORKER HOST to a PEM bundle, mounted read-only into all three containers of every
# run unit at /etc/spire/ca-bundle.crt.
#
# It must be a COMPLETE bundle -- your corporate root APPENDED to the public roots, not the
# corporate root alone. Two of the three variables it sets (SSL_CERT_FILE for OpenSSL,
# GIT_SSL_CAINFO for git) REPLACE the trust store rather than adding to it, so a corporate-only
# file makes the internal forge work and every public TLS call fail -- which presents as an outage
# at your model provider, not as a configuration mistake. The third (NODE_EXTRA_CA_CERTS, which is
# what the Codex arm reads) appends.
#
# The worker REFUSES TO START if this names something that is not a readable file, if the path is
# relative (a relative bind source is a VOLUME NAME to a container runtime, not a file), or if the
# file contains a PRIVATE KEY -- the shape a combined server.pem takes, which would hand the agent
# a key. A missing bind source is not an error in every runtime, so without the check a typo would
# mount an empty directory where the certificate should be and fail every run silently.
#
# THE PATH IS RESOLVED BY THE CONTAINER RUNTIME, not by the worker. They are the same filesystem
# while the worker runs directly on the host, which is how it runs today. If you later run the
# worker in a container, the bundle must exist at this same path on the machine the runtime runs
# on AND be mounted into the worker so its startup check can see it.
# SPIRE_RUN_CA_BUNDLE_PATH=/etc/ssl/certs/acme-full-bundle.crt
#
# Both cases are handled for you; give each URL once. Set NO_PROXY whenever you set a proxy:
# an internal forge or a self-hosted model endpoint is routinely reachable only directly, and a
# proxy with no exception list turns the clone into a hang that ends at the init timeout.
# SPIRE_RUN_HTTP_PROXY=http://proxy.acme.example:3128
# SPIRE_RUN_HTTPS_PROXY=http://proxy.acme.example:3128
# SPIRE_RUN_NO_PROXY=forge.acme.example,registry.acme.example,localhost,127.0.0.1
#
# A private registry holding the agent and publisher images. ALL THREE together or none: the
# worker refuses a partial credential, because a host and username with no secret would fall back
# to an anonymous pull and report a private image as NOT FOUND -- sending you to check the image
# reference rather than the password. The host must match the image reference: a credential for
# registry.acme.example is never offered to Docker Hub. A PORT is part of the host, so a registry
# at registry.acme.example:5000 must be configured with the port. Docker Hub may be written
# docker.io, index.docker.io or registry-1.docker.io -- all three are recognised as one registry.
#
# This credential reaches the image PULL and nothing else. It is never placed on a container, so
# `docker inspect` on a run unit does not print it and the agent cannot read it.
#
# The PROXY credential is different, and deliberately so: the proxy URL is set in every container
# because every container must route through it, so an agent -- which runs untrusted model output at
# full shell access -- can read it from its own environment. Give the proxy a service account scoped
# to proxying, not one reused elsewhere. What IS guaranteed is that it never reaches anything stored:
# the run transcript and factory_run.failure_detail are scrubbed of it in every form it takes --
# raw as written here, percent-decoded, and the Proxy-Authorization: Basic header. A SHORT password
# is scrubbed like any other: it is still a credential, and forge accounts issue six-character ones.
# The worker warns that redacting a short value also hides innocent text and makes failure details
# harder to read; use a longer password to get readable messages back.
# SPIRE_RUN_REGISTRY_HOST=registry.acme.example
# SPIRE_RUN_REGISTRY_USERNAME=spire-factory
# SPIRE_RUN_REGISTRY_SECRET=REPLACE_ME
# Vite dev server for spire-ui (reads the shell env, not this file; default 34000):
# UI_PORT=34000
# --- Encryption at rest (Tink AES-256-GCM) ---
# The master keyset (base64 Tink keyset) — the ONE bootstrap secret; everything
# else sensitive is encrypted with it. Generate your OWN, never reuse an example.
# Required by the ORCHESTRATOR always, and by the WORKER in active mode (ADR-015:
# the worker decrypts the per-command SCM credential the orchestrator brokers).
# SPIRE_ENCRYPTION_KEYSET=REPLACE_ME
# The DEDICATED webhook keyset — protects per-repo webhook secrets. Held ONLY by the
# GATEWAY (which owns the webhook registry, encrypting/decrypting its own secrets).
# The gateway never holds the master keyset, so a compromised internet-facing edge
# cannot decrypt the SCM/LLM API tokens. Generate a SEPARATE keyset (same tool).
# SPIRE_ENCRYPTION_WEBHOOK_KEYSET=REPLACE_ME
# --- LLM mode flag ---
# SPIRE_LLM_PROVIDER: stub | registry — MODE FLAG ONLY, not credentials. stub
# forces the canned dev/test LLM; registry uses the per-command brokered
# credential from the LLM provider registry (Settings -> LLM, ADR-018).
# SPIRE_LLM_PROVIDER=registry
# SPIRE_REVIEW_MAX_ATTEMPTS: bounded auto-retry per review on transient failures
# (ADR-016). Default 3.
# SPIRE_REVIEW_MAX_ATTEMPTS=3
# SPIRE_REVIEW_LOG_PROMPTS: writes the fully rendered prompt (system + user, with
# the fenced context slot) to the worker log before each model call — the only
# window onto what the model was actually given. OFF by default ON PURPOSE: the
# prompt carries the whole diff and every retrieved context item, which is
# otherwise Tink-encrypted at rest (ADR-014), so enabling it copies that into
# plaintext logs. Switch on for a debugging session, then switch back off.
# SPIRE_REVIEW_LOG_PROMPTS=false
# SPIRE_LLM_TIMEOUT_SECONDS: how long ONE model call may take. Default 180.
# Was 60 and hardcoded, which is not enough for a reasoning model on a real
# diff — the call was cut off and the review came back empty, having been
# charged for. Raising it alone is not safe: see the pairing below.
# SPIRE_LLM_TIMEOUT_SECONDS=180
# SPIRE_KAFKA_ACK_MAX_AGE_MS: how long the worker may hold ONE cs.commands record
# before acknowledging it. Default 900000 (15 min). SmallRye fails the whole
# channel past this, and the clock starts when the record is POLLED — so it must
# cover two model calls (ADR-019 runs reconcile then review) plus fetches,
# retries and posting. When it and the LLM timeout were both 60s, a slow review
# killed the consumer and its unacked record re-stalled it after every restart.
# The worker refuses to start when the pairing has no headroom, and warns when
# the value is so large that a wedged consumer would go undetected.
# SPIRE_KAFKA_ACK_MAX_AGE_MS=900000
#
# Cost note, since neither knob above is where it shows up: the default output cap
# for a provider with no per-model max_tokens is 16384 (was 4096, which a
# reasoning model exhausted before answering). ADR-025 bounds spend-cap overshoot
# by in-flight reviews x per-review cost, so that bound rose with it. Set
# max_tokens on the provider under Settings -> LLM to pin your own ceiling.
# --- Review mode ---
# NO env var. The observe/active posture is the Settings slider, stored in the DB
# (app_setting, key review.mode). A fresh DB seeds to "observe" (safe first
# contact) until the slider is flipped. To seed a fresh DB to active instead, set
# SPIRE_REVIEW_MODE=active (rarely needed).
# Observe refuses every SCM-originated trigger: PR events, /commands, and author replies.
# Your own admin REST actions (the Re-run button, POST /api/runs) still work — that is the
# operator override, and the only way to review one PR without going globally active.
# SPIRE_REVIEW_DRAFT_PRS=false
# true = review draft PRs immediately (default waits for ready_for_review)
# --- Learned memory + analytics (ADR-027) ---
# The finding corpus (review_finding) and the analytics over it need no env var --
# they are on for every deployment. Learned memory only ever acts on a preference
# an admin approved under Settings -> Memory, so it is also safe by default.
# SPIRE_MEMORY_MIN_EVIDENCE=10
# Judged findings one group needs before a preference can be proposed. Lower it
# and proposals are drawn from a corpus too thin to speak.
# SPIRE_MEMORY_MIN_DISMISSED_PERCENT=75
# Share of that group that must have been dismissed. Both numbers are shown on
# the proposal card, so an operator can weigh the evidence against the bar.
# SPIRE_MEMORY_WINDOW_DAYS=180
# How far back the nightly scan looks. Bounds an aggregate that would otherwise
# grow forever, and keeps a preference reflecting what the team believes now.
# --- Repository knowledge base (ADR-026) ---
# Rung 1 (import-resolved code snippets) needs no env var — it is on whenever a
# "code" context provider is registered under Settings -> Context.
# SPIRE_SYMBOL_INDEX_ENABLED=true
# Rung 2: the worker.code_symbol index that answers "what depends on this diff".
# false degrades to rung 1 exactly (nothing stored, no callers cited). The index
# holds identifiers and paths only, never source, but over many reviews it
# accumulates a repository-shaped dependency map — see docs/SECURITY.md.
# SPIRE_SYMBOL_INDEX_RETENTION_DAYS=90
# How long a row survives without being re-observed. Pruning costs recall only:
# a forgotten caller simply goes unmentioned.
# --- SCM providers ---
# NO env var. Which SCMs are reviewed is the UI registry (Settings -> Accounts),
# encrypted at rest and brokered per-command. The worker defaults to real.
# SPIRE_SCM_STUB=true forces the canned stub adapters ONLY for the local demo
# (SMOKE-TEST Mode A) — a dev/test toggle, not a provider selection.
# SPIRE_SCM_STUB=true
# --- SCM webhooks ---
# No env needed: every SCM (Bitbucket/GitHub/GitLab) is registered under
# Settings -> Repositories, which mints a per-repo key + secret. Paste the shown
# /webhooks/{provider}/{key} URL + secret into the provider's webhook config.
# --- LLM ---
# NO LLM env vars: the model + API key are registered in the app (Settings -> LLM,
# ADR-018), Tink-encrypted at rest and brokered per-command to the worker like SCM
# credentials. SPIRE_LLM_PROVIDER above is only the stub|registry mode flag.
# --- Attention panel (optional; shown defaults apply when unset) ---
SPIRE_ATTENTION_STUCK_MINUTES=15
# --- Operator authentication (D10) ---
# The dashboard and every REST/WebSocket endpoint require an operator identity in any
# packaged run. Dev boots with authentication OFF (see %dev in each service's
# application.yml); leaving it off anywhere else makes the service refuse to start.
#
# Two supported ways to get an identity provider, both driven by the SAME variable:
# A. the bundled Keycloak: docker compose -f docker-compose.yml -f docker-compose.idp.yml up -d
# B. one you already run: point the variable at it and skip that file
# Either way import infra/keycloak/realm-spire.json — it defines the four clients, both
# roles (spire-viewer, spire-admin), the audience mappers the services require, and two
# obviously-synthetic dev users.
#
# SPIRE_OIDC_AUTH_SERVER_URL=http://localhost:34567/realms/spire
#
# Each service authenticates as its OWN client, so a session minted for one cannot be
# replayed against another. Set these per service (the client id defaults to the service
# name; the secret has no default and must come from the realm):
# SPIRE_OIDC_CLIENT_ID=spire-orchestrator
# SPIRE_OIDC_CLIENT_SECRET=CHANGE_ME
# Bundled Keycloak container only (docker-compose.idp.yml). Not a deployment
# credential — it administers the local dev IdP and nothing else.
KEYCLOAK_PORT=34567
KEYCLOAK_ADMIN_USER=dev-admin
KEYCLOAK_ADMIN_PASSWORD=CHANGE_ME