-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy path.env.example
More file actions
224 lines (217 loc) · 12.1 KB
/
Copy path.env.example
File metadata and controls
224 lines (217 loc) · 12.1 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
# Postgres
DATABASE_URL=postgresql://cora:cora@localhost:5432/cora
# App
APP_ENV=local
LOG_LEVEL=INFO
# Authorization — Trust BC
# When unset, the API runs with AllowAllAuthorize (every command
# permitted): the dev/test default. To enable real authz, set both vars
# below TOGETHER.
# Setting only TRUST_POLICY_ID without REQUIRE_AUTHENTICATED_PRINCIPAL
# refuses to boot: without the header check, anyone can spoof
# X-Principal-Id and become SYSTEM under the bootstrap policy. Test env
# (APP_ENV=test) exempt.
#
# In a production-tier env (APP_ENV=prod, production, or staging) the API
# refuses to boot when TRUST_POLICY_ID is unset, because that would run
# the permit-everyone AllowAllAuthorize stub. Set TRUST_POLICY_ID to
# enable real authz, or set ALLOW_PERMISSIVE_AUTHZ=true to run permissive
# on purpose (e.g. an airgapped single-operator pilot). staging counts as
# production-tier because it usually handles real data and is reachable.
#
# Bootstrap workflow (first deployment):
# 1. TRUST_POLICY_ID=00000000-0000-0000-0000-000000000002 # seeded by migration
# 2. REQUIRE_AUTHENTICATED_PRINCIPAL=true
# 3. Auth proxy strips client-supplied X-Principal-Id and sets the verified one.
# 4. Use SYSTEM_PRINCIPAL_ID (nil UUID) to register your real admin Actor +
# define your real admin Policy via the API, then re-point TRUST_POLICY_ID
# at the new policy.
# See docs/stack/deployment.md (when present) + memory project_bootstrap_policy_design.
# TRUST_POLICY_ID=00000000-0000-0000-0000-000000000002
# REQUIRE_AUTHENTICATED_PRINCIPAL=true
# Prod-only escape hatch: run AllowAllAuthorize (no command gating) on
# purpose. Ignored in non-prod (permissive is already the default there).
# ALLOW_PERMISSIVE_AUTHZ=true
# Database schema agreement
# CORA refuses to start when the applied migration version is not the one
# the build expects. Migrations are applied out of band (make
# migrate-apply) and the runtime image ships no way to apply them, so
# restoring a backup taken before a migration leaves a database older
# than the code. The refusal names both versions and the remedy; the two
# directions differ, because a database BEHIND the build is fixed by
# applying migrations while one AHEAD of it cannot be (forward-only
# leaves nothing to apply) and needs the matching image instead.
#
# ALLOW_SCHEMA_VERSION_MISMATCH=true boots anyway, with every append to
# the event log refused and reads still working, so a restored database
# can be READ without risking an append-only history that cannot be
# corrected afterwards. /readyz reports "schema": "degraded" throughout.
#
# Scope it honestly: this protects the EVENT LOG, not every write. The
# per-BC stores and projection workers build straight from the pool and
# are not wrapped. That ordering is deliberate (events are irreversible,
# derived state is rebuildable), but it means this is a way to inspect a
# restored database, NOT a way to run a mismatched deployment.
# ALLOW_SCHEMA_VERSION_MISMATCH=true
# Control substrate: Operation BC Conductor
# CONTROL_WRITES_ENABLED gates every write CORA makes through the
# ControlPort, the Conductor's modeled actuation path. It defaults to
# false: CORA observes every configured route and drives none. A write
# raises ControlWritesDisabledError before any substrate is contacted,
# and the Conductor records it as a step failure.
#
# READ THE SCOPE. This switch covers the ControlPort and nothing else. It
# does NOT cover COMPUTE_SUBSTRATE=local_process, which spawns
# operator-supplied argv as a subprocess and can reach a control system
# without passing through any ControlPort (see the COMPUTE_SUBSTRATE
# block below). Setting CONTROL_WRITES_ENABLED=false does not by itself
# make this host incapable of touching the beamline. See
# docs/stack/deployment.md on observe-only deployments.
#
# Within that scope it is the observe-only safety switch (the APS 2-BM
# pilot posture). It admits no per-substrate or per-route exemption, so a
# config that forgets something fails closed. A deployment that drives
# hardware sets it to true once, on purpose. Local runs and tests that
# exercise writes through Settings must set it too.
# CONTROL_WRITES_ENABLED=true
#
# CONTROL_PORT_ROUTES maps an address prefix to a substrate adapter, as
# JSON. Unset (default) builds an InMemoryControlPort: the conduct surface
# is reachable but no real substrate is exercised. Routes are matched by
# LONGEST prefix, and each prefix must be distinct (a duplicate is refused
# at boot rather than silently discarding the earlier route's flags).
#
# Per-route declarations, all declared facts and never inferred from substrate:
# is_simulated this route drives a simulator, even over real Channel
# Access (a soft IOC speaks real CA). Feeds the Dataset
# provenance gate that blocks promoting simulator data.
# read_only CORA may read and subscribe here but never write.
# Per-route expressiveness INSIDE a writable deployment
# ("drive the stage, never the shutter"). It defaults to
# false, so it is NOT how you make a deployment
# observe-only: use CONTROL_WRITES_ENABLED=false for that.
# text_addresses epics_ca only. Addresses whose EPICS DBR_CHAR waveform
# carries a NUL-terminated string (e.g. tomoscan's
# ScanStatus, FileName) rather than raw bytes (e.g. an
# NTNDArray image). EPICS gives both the same wire type,
# so undeclared addresses read as Measurement(kind=
# "Array", value=<tuple of ints>); declared ones decode
# to Measurement(kind="Scalar", value=<str>). A no-op on
# other substrates and on addresses that never resolve
# to DBR_CHAR.
#
# CONTROL_PORT_ROUTES='[
# {"prefix":"2bma:cam1:image","substrate":"epics_pva"},
# {"prefix":"2bma:shutter:","substrate":"epics_ca","read_only":true},
# {"prefix":"2bmb:TomoScan:","substrate":"epics_ca","read_only":true,
# "text_addresses":["2bmb:TomoScan:ScanStatus","2bmb:TomoScan:FileName",
# "2bmb:TomoScan:FilePath","2bmb:TomoScan:FullFileName"]},
# {"prefix":"2bma:","substrate":"epics_ca"}
# ]'
# Compute substrate: Operation BC conduct runtime
# COMPUTE_SUBSTRATE selects how a conduct job runs. It defaults to
# in_memory, which mints a Simulated result and never spawns anything.
# That default is what keeps this path inert; it is NOT gated by
# CONTROL_WRITES_ENABLED, which covers the ControlPort only.
#
# local_process runs the job's argv as an OS subprocess on this host, as
# the API service account. Understand three things before setting it:
#
# 1. The argv can be caller-supplied. With CORA_ALLOW_RAW_CONDUCT=true
# (the current default) a conduct request for a Method that has no
# launch_spec carries its own `command`, which is executed verbatim.
# 2. No Trust policy gates the spawn. The Authorize port gates the RUN
# TRANSITION (complete_run / abort_run), which happens AFTER the
# subprocess has already run. An authenticated principal holding
# zero permissions can reach it.
# 3. So an argv like ["caput", ...] would reach a control system
# without touching the ControlPort, whatever CONTROL_WRITES_ENABLED
# says. That switch gates the ControlPort only.
#
# COMPUTE_PERMITTED_EXECUTABLES bounds point 3: the substrate refuses any
# command[0] outside it, before spawning. It is EMPTY by default, which
# permits NOTHING, so setting COMPUTE_SUBSTRATE=local_process without
# also setting this yields a port that refuses every job. That is
# deliberate: name what this host may run.
#
# The CHECK matches command[0] exactly: no PATH resolution, no basename
# fallback, so /tmp/evil/tomopy does not ride in on an allowlisted
# "tomopy". Two rules follow, neither optional:
#
# - Allowlist TOOLS, never interpreters. Permitting "python" or "sh"
# re-opens arbitrary execution via -c and the check cannot tell.
# - Declare ABSOLUTE paths. The check is exact, but the spawn still
# PATH-resolves a bare name afterwards, so allowlisting "tomopy"
# permits whatever PATH finds then. A request cannot reach that (the
# conduct body carries no env), but a writable PATH entry on the host
# would still decide what runs.
#
# The allowlist is the belt, not the trousers. It bounds WHAT runs; it
# does not authorize the path. The real fix is to give every compute
# Method a launch_spec (argv then builds server-side from the vetted,
# event-sourced recipe) and set CORA_ALLOW_RAW_CONDUCT=false, which
# deletes the caller-supplied argv path rather than bounding it. See
# docs/stack/deployment.md.
# COMPUTE_SUBSTRATE=local_process
# COMPUTE_PERMITTED_EXECUTABLES='["/opt/conda/envs/tomopy/bin/tomopy"]'
# CORA_ALLOW_RAW_CONDUCT=false
# LLM provider: the switch and the credential
# Two settings, and BOTH are required before CORA calls an external model.
# LLM_ENABLED is the SWITCH and defaults to false; ANTHROPIC_API_KEY is the
# CREDENTIAL. Set only the key and nothing calls out: the switch decides.
#
# That split exists because a key is often present in an environment for an
# unrelated reason, and its mere presence used to be enough to register the
# RunDebriefer and CautionDrafter subscribers, which call an external API on
# EVERY terminal Run. That is experiment metadata leaving the facility and
# money being spent, switched on by a side effect. Every sibling subscriber
# already had its own default-off flag; this seam was the outlier.
#
# Leaving it off is graceful, never a crash: the two LLM-backed subscribers
# are skipped with a warning naming which of the two settings is missing,
# `regenerate_run_debrief` answers unavailable, and a conduct command that
# explicitly asks for the `llm` decide substrate gets a 422.
#
# UPGRADING: a deployment that ran the LLM subscribers on ANTHROPIC_API_KEY
# alone must now also set LLM_ENABLED=true, or those two stop registering.
# Nothing crashes; a boot warning names the switch and /readyz reports "off".
#
# RUN_DEBRIEFER_AGENT_ID / CAUTION_DRAFTER_AGENT_ID let a deployment
# designate WHICH Agent each LLM subscriber acts as, instead of always
# acting as the seeded singleton. Unset (the default) means the seeded
# singleton, so nothing changes on upgrade. The named Agent must already
# exist: define it first through the gated `POST /agents` (`define_agent`)
# path, and its declared model must be reachable by the configured
# LLM_PROVIDER (`anthropic` / `argo` / `local`, see `Settings.llm_provider`),
# or every call the subscriber makes will be refused by the adapter at
# request time. This matters for a deployment whose network cannot reach
# the seeded agents' declared provider (eg. `anthropic` from a controls
# network with no internet): define an Agent against a reachable provider
# (`argo` or `local`) and name it here.
# RUN_DEBRIEFER_AGENT_ID=
# CAUTION_DRAFTER_AGENT_ID=
#
# NOTE this is a DIFFERENT axis from actuation (CONTROL_WRITES_ENABLED and
# COMPUTE_SUBSTRATE above). Those bound what CORA can MOVE; these bound the
# LLM path. A facility told "read-only" hears the first and would still
# object to the second, so neither promise covers the other.
#
# Read the scope narrowly: this bounds the LLM path ONLY, not egress in
# general. The HTTP checksum adapter is wired unconditionally for http/https
# Distributions, so CORA can still make outbound requests with the LLM off.
# Verify the effective state at `GET /readyz` -> `"llm": "off" | "live"`.
# LLM_ENABLED=true
# ANTHROPIC_API_KEY=sk-ant-...
# Observability — OpenTelemetry
# OTEL_EXPORTER selects how spans leave the process:
# none -> no provider installed (default; tests and quiet local runs)
# console -> spans to stdout (verbose; great for one-shot dev sessions)
# otlp -> OTLP/HTTP to a collector configured via the standard OTel env vars below
# OTEL_EXPORTER=console
# OTEL_SERVICE_NAME=cora-api # overrides the resource attribute service.name
# OTEL_SAMPLER_RATIO=1.0 # only consulted when OTEL_EXPORTER=otlp
#
# Standard OTLP env vars (honoured by the OTel SDK exporter directly):
# OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces
# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
# OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer%20<token>