-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy path.env.example
More file actions
224 lines (210 loc) · 10.6 KB
/
Copy path.env.example
File metadata and controls
224 lines (210 loc) · 10.6 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
# Which backend. Set exactly one — starting with both or neither is a
# startup error. DATABASE_URL runs everything; SQLITE_PATH runs core auth
# only, with the whole admin console answering 501 (its tables are
# Postgres-only). See README's "The two backends".
DATABASE_URL=postgresql://postgres.xxxxxxxx:your-password@aws-1-eu-west-3.pooler.supabase.com:5432/postgres?sslmode=require
# SQLITE_PATH=/var/lib/cryden/api.db
JWT_SECRET=
CORS_ORIGINS=http://localhost:5173,https://yourapp.com
PORT=8080
ACCESS_TOKEN_TTL_MINUTES=15
BASE_URL=http://localhost:8080
# Migrations run automatically on boot, so there is no migrate step in the
# setup instructions. Set this to true if you would rather schema changes
# be a reviewed step of their own — then `api migrate` is that step, and
# the server starts without touching the schema. See README's "Migrations".
# If your database already has this schema but no schema_migrations table
# (i.e. you applied the SQL by hand), run `api migrate --baseline` once
# before your first start with this setting either way.
SKIP_AUTO_MIGRATE=false
# OAuth providers are optional; one missing its ID or secret is simply
# unavailable (404 oauth_provider_not_configured), not a startup error.
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
MICROSOFT_CLIENT_ID=
MICROSOFT_CLIENT_SECRET=
DISCORD_CLIENT_ID=
DISCORD_CLIENT_SECRET=
GITLAB_CLIENT_ID=
GITLAB_CLIENT_SECRET=
# Apple needs all four (its "secret" is a JWT this API signs with the .p8
# key, so there is no static secret). Write the key's newlines as \n.
APPLE_CLIENT_ID=
APPLE_TEAM_ID=
APPLE_KEY_ID=
APPLE_PRIVATE_KEY=
# Second factors. ENCRYPTION_KEY is required for TOTP and/or passkeys and
# encrypts TOTP secrets at rest — treat it like JWT_SECRET. Leave it unset
# to run password-only; the second-factor endpoints then answer 404
# rather than the server refusing to start.
ENCRYPTION_KEY=
TOTP_ISSUER_NAME=
WEBAUTHN_RP_ID=
WEBAUTHN_RP_DISPLAY_NAME=
WEBAUTHN_RP_ORIGINS=
# Seals the credentials this api stores ITSELF, in the settings table:
# the LLM provider's API key and the read-only database's password behind
# the AI-assisted admin features. AES-256-GCM, the same encryptor cryden
# uses for TOTP secrets, with the key derived from this value — so treat
# it with the same care as JWT_SECRET.
#
# Deliberately a separate value from ENCRYPTION_KEY rather than a reuse
# of it. That one is cryden's, the engine derives from it whatever it
# needs to read TOTP secrets, and the two have different lifetimes: a
# rotation of either must not silently make the other's rows unreadable.
# The same reasoning is why CLOUD_LOG_HASH_KEY is its own value.
#
# Leave it unset to run without the AI settings screens; GET/PUT
# /v1/admin/settings/llm-provider and its database counterpart then answer
# 404 not_configured rather than the server refusing to start, the same
# shape ENCRYPTION_KEY itself uses for the second factors.
#
# If it ever changes, rows written under the old value become unreadable
# and say so (a decryption failure, distinct from "never configured") —
# re-enter those credentials. Clearing a setting still works without the
# key, so there is a way out that is not direct database access.
SETTINGS_ENCRYPTION_KEY=
# Login anomaly detection and credential-stuffing detection. Both are
# report-only — a flagged attempt writes an audit event and nothing else,
# no login is ever blocked — and both are off until ANOMALY_DETECTION is
# set, since they share one store as their on/off switch. The threshold
# vars below default to the engine's own values and only need setting to
# tune them. Zero means whatever the engine says it means per knob — off
# for most, "no event suppression" for the stuffing cooldown — so check
# cryden's own security package before setting one to 0.
ANOMALY_DETECTION=
ANOMALY_WINDOW_MINUTES=
ANOMALY_HISTORY_SIZE=
ANOMALY_USER_FAILURE_VELOCITY=
ANOMALY_IP_FAILURE_VELOCITY=
ANOMALY_MAX_CONCURRENT_SESSIONS=
ANOMALY_TOKEN_REUSE_LOOKBACK_MINUTES=
CREDENTIAL_STUFFING_WINDOW_MINUTES=
CREDENTIAL_STUFFING_TARGET_ACCOUNTS=
CREDENTIAL_STUFFING_COOLDOWN_MINUTES=
# Engine-level rate limiter (per-user, on login/signup/magic-link).
# Leave REDIS_URL unset to keep the in-process limiter — correct for a
# single instance, but with several replicas each keeps its own counters,
# so the effective limit is the configured one times the replica count.
# Set REDIS_URL to share one window across every replica; an unreachable
# Redis then fails those calls closed rather than letting them run
# unlimited. RATE_LIMIT_ATTEMPTS / RATE_LIMIT_WINDOW_SECONDS default to
# cryden's own 10 per minute. This does not affect EDGE_RATE_LIMIT, the
# coarse per-IP limiter, which stays in-process either way.
REDIS_URL=
RATE_LIMIT_ATTEMPTS=
RATE_LIMIT_WINDOW_SECONDS=
# Account lockout: after LOCKOUT_THRESHOLD consecutive failed passwords the
# account is locked for LOCKOUT_DURATION_MINUTES. Defaults are cryden's own
# (5 and 15), restated here because the engine does NOT fill these in — it
# reads whatever it is handed, and both zero values are wrong in the same
# direction. A zero threshold locks every account on its first bad
# password; a zero duration locks it until an instant already past, which
# is to say never. LOCKOUT_THRESHOLD below 1 is refused rather than read as
# "off", because cryden has no way to switch lockout off.
#
# These are also what GET /v1/admin/config-tuning quotes when it says a
# lockout setting is in force, so the report and the engine cannot disagree
# about what this deployment is running.
LOCKOUT_THRESHOLD=
LOCKOUT_DURATION_MINUTES=
# Password hashing. bcrypt is the engine's default; argon2id is the
# current recommendation for new deployments (memory-hard, and the knob a
# GPU attacker cannot parallelize around). Switching is safe at any time
# and needs no migration — existing bcrypt hashes keep verifying and are
# rewritten one successful login at a time, which is what
# GET /v1/admin/security/hash-migration reports on.
#
# The ARGON2ID_* vars default to RFC 9106's second recommended option
# (64 MiB, t=3, p=4) and only need setting to tune them to your hardware.
# Raise ARGON2ID_MEMORY_KIB before ARGON2ID_ITERATIONS if you have
# headroom: memory hardness is the whole reason to pick Argon2id. Note
# that each var replaces exactly one field — leaving the rest at cryden's
# defaults, never at zero.
PASSWORD_HASHER=
ARGON2ID_MEMORY_KIB=
ARGON2ID_ITERATIONS=
ARGON2ID_PARALLELISM=
ARGON2ID_SALT_LENGTH=
ARGON2ID_KEY_LENGTH=
# The non-secret label every generated API key starts with ("ck_9f3a1c02…").
# Set it to something recognisable as yours so a key leaked into a commit
# is greppable by your own secret scanners.
API_KEY_PREFIX=
# Cloud logging — a second, redacted, filtered copy of the engine's log
# records, alongside the full-detail JSON line on stdout. Off unless
# CLOUD_LOGGING is set. LOG_LEVEL is the threshold the shipped copy
# drops below and must be debug/info/warn/error: an unrecognized value is
# a startup failure rather than a silent fallback, because defaulting a
# typo to debug multiplies a vendor bill and defaulting it to error
# throws away the records you were trying to keep.
#
# There is no vendor here — this repo ships no SDK, so the shipped copy
# is recorded in the shipped_log_events table, which
# GET /v1/admin/logging/recent (admin) reads back. It is the same bytes a
# hosted aggregator would have received, which is what makes it a stand-in
# for one rather than a second, different log beside it.
#
# CLOUD_LOG_REDACTION is "mask" (value replaced with [redacted]) or
# "hash" (keyed HMAC digest, so the same address still reads as the same
# address across records — "one IP, forty accounts" is the shape
# credential stuffing has, and a mask destroys it). "hash" requires
# CLOUD_LOG_HASH_KEY, which must be identical on every replica and should
# be a value of its own rather than a reuse of JWT_SECRET.
CLOUD_LOGGING=
LOG_LEVEL=
CLOUD_LOG_REDACTION=
CLOUD_LOG_HASH_KEY=
# A directory holding message templates, rendered instead of the console
# senders' built-in lines. Optional — unset keeps today's behaviour.
# Recognised files are verification.txt and magic_link.txt; supply either
# or both. Available fields: {{.To}}, {{.Token}}, {{.URL}} (empty for a
# verification message, and for a magic link when BASE_URL is unset).
# A directory that is set but holds neither file, or a file that does not
# parse, is a startup failure — a template directory that silently did
# nothing is worse than one that refused to start.
EMAIL_TEMPLATE_DIR=
# Webhook deliveries. WEBHOOK_URL is the on/off switch: leave it unset and
# the engine dispatches nothing, no delivery worker runs, and
# GET /v1/admin/webhooks/deliveries answers 404 not_configured. The other
# three are only read when it is set, and setting any of them without it
# is a startup failure rather than a setting that silently does nothing.
#
# Events are queued in the webhook_deliveries table and delivered by a
# background worker, never inline: cryden calls the sender on the login
# request path, so an HTTP call there would be your receiver's downtime
# becoming your users' login latency. A delivery is retried with
# exponential backoff (30s doubling to 30m) until WEBHOOK_MAX_ATTEMPTS is
# spent, then recorded as failed and left readable — retrying forever is a
# load generator pointed at someone else's server.
#
# Each request carries X-Cryden-Signature: "sha256=" plus the lowercase
# hex HMAC-SHA256 of the raw body under WEBHOOK_SECRET. Unset means
# deliveries go out unsigned and the header is absent entirely; a receiver
# on a private network is a legitimate reason to do that, and a signature
# over an empty key is not.
#
# WEBHOOK_EVENTS is a comma-separated list of audit event types. Unset
# uses cryden's own default set, which deliberately excludes
# login_success, login_failed and token_rotated.
WEBHOOK_URL=
WEBHOOK_SECRET=
WEBHOOK_EVENTS=
WEBHOOK_MAX_ATTEMPTS=
# The weekly digest schedule. Unset (or 0) means no schedule at all: no
# goroutine runs, nothing is written, and GET /v1/admin/digest/history
# answers 404 not_configured. GET /v1/admin/digest works either way — it
# builds a digest on demand and records nothing.
#
# Set it and this repo builds the engine's digest every N hours and stores
# the rendered report in the digest_runs table, which is what the history
# endpoint lists. 168 is weekly. cryden has no scheduling concept, so this
# table, this job and that endpoint are all this repo's own.
#
# The first run happens one full interval after startup, not at boot: a
# process that restarts more often than the interval elapses would
# otherwise write one row per restart, and a history that grows with
# restarts is not a history of anything.
DIGEST_INTERVAL_HOURS=