Repository navigation
Expand file tree
/
Copy path.env.localhost.example
More file actions
124 lines (115 loc) · 9.13 KB
/
Copy path.env.localhost.example
File metadata and controls
124 lines (115 loc) · 9.13 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
# Private (machine-local) environment file. Copy to .env.localhost and fill in real values.
# NEVER commit .env.localhost — it holds secrets. (.env* is already gitignored.)
#
# cp .env.localhost.example .env.localhost
# docker compose -f docker-compose.yml -f docker-compose.prod.yml --env-file .env.localhost pull
# docker compose -f docker-compose.yml -f docker-compose.prod.yml --env-file .env.localhost up -d
#
# SAME OVERLAY AS THE INTERNET-FACING DEPLOYMENT. This is not a lighter mode: docker-compose.prod.yml
# is used unchanged, so you get Production, the runtime hardening, unpublished api/mariadb/client
# ports, the persisted DataProtection keys volume and real TLS. Only two values below differ from
# .env.prod, and they are the first two sections.
#
# THIS IS NOT THE DEV STACK. `docker compose up` on the base file alone is the development stack —
# Development environment, demo data, and four seeded accounts sharing a password published in this
# repository's README. This file deploys the real application with no demo data and no default
# accounts, which is why it needs DB passwords and a bootstrap administrator just like a public
# deployment does. If you want the demo dataset to click around in, you want the base stack instead;
# see the repository README.
#
# Read docs/deployment.md alongside this file — "Running Odyssey privately (localhost only)" for
# what these two settings do, and "Backups" for the part people get wrong: the dataprotection_keys
# volume is secret-bearing, must be backed up SEPARATELY from the database dump, and is destroyed by
# `docker compose down -v` — permanently, for anything derived from it. A laptop is not a backup
# strategy, and "it's only local" is how the legal pseudonymisation salt gets lost.
# ── Loopback binding ─────────────────────────────────────────────────────────
# THE trailing colon is required and is part of the value — see the comment on the caddy service's
# ports in docker-compose.prod.yml. With this set, Caddy publishes 127.0.0.1:80 and 127.0.0.1:443
# and the stack is unreachable from any other machine. Unset (as .env.prod leaves it) Caddy binds
# 0.0.0.0 and the deployment is public.
#
# Caddy is the ONLY host-facing surface in the overlay; every other service resets its ports to [].
# So this single line is sufficient to take the whole deployment off the network — and conversely,
# publishing any other service "just to check something" undoes it. Don't. The client's nginx
# honours an inbound X-Forwarded-Proto, which is safe only because Caddy is the sole path in.
#
# Note this leaves ports 80 and 443 in use on loopback. Nothing else in this repository wants them
# (the dev stack uses 5199/5188/3307), but another project might.
CADDY_BIND=127.0.0.1:
# ── Local hostname / TLS ─────────────────────────────────────────────────────
# A *.localhost name, NOT bare "localhost". Caddy recognises it as non-public and issues a
# certificate from its own internal CA instead of attempting Let's Encrypt (which could not validate
# a private host anyway), and browsers resolve *.localhost to loopback without a hosts-file entry.
#
# USE THE SUBDOMAIN. The Caddyfile sends `Strict-Transport-Security: max-age=31536000;
# includeSubDomains`, and browsers key HSTS by HOSTNAME AND IGNORE THE PORT. Pinning bare "localhost"
# would therefore force every http://localhost:<any port> request in that browser to https for a
# year — breaking the dev stack on :5199 and Swagger on :5188, and recoverable only through
# chrome://net-internals/#hsts. Scoped to odyssey.localhost the pin is harmless, which is why no
# change to the Caddyfile is needed for a private deployment.
ODYSSEY_DOMAIN=odyssey.localhost
# ── Release selection ────────────────────────────────────────────────────────
# The GitHub user or org that owns the published images (the <owner> in ghcr.io/<owner>/odyssey-api).
GHCR_OWNER=
# The release to deploy, e.g. 1.4.0 — see the repository's Releases page. Bump it and re-run pull+up
# to upgrade. Pin an exact version rather than "latest" so you decide when schema migrations run.
IMAGE_TAG=
# ── Database ─────────────────────────────────────────────────────────────────
# Still required, and still generated — being on loopback narrows who can reach MariaDB, but these
# are what stop any other local process or container on this machine from reading the database.
# Generate each separately — do not reuse one for the other:
# openssl rand -base64 36
MARIADB_ROOT_PASSWORD=
MARIADB_USER=odyssey
MARIADB_PASSWORD=
# KEEP THE TWO NAMES IDENTICAL — MARIADB_DATABASE is read by the MariaDB image's entrypoint, which
# creates that database on first start; ODYSSEY_DATABASE goes into the connection string both .NET
# hosts use. Change one only and the migrations job connects to a database that was never created.
MARIADB_DATABASE=odyssey
ODYSSEY_DATABASE=odyssey
# ── The first administrator ──────────────────────────────────────────────────
# Required. Registration order grants nobody any privilege, so without these there is no way in: the
# migrations job creates this account, and refuses to finish if it cannot, which holds the API down
# behind it. There are no seeded accounts — that is the dev stack, not this one.
#
# The password must satisfy the app's policy (16+ characters, upper + lower + digit + symbol) and is
# a ONE-TIME secret — the account can do nothing until it is changed at first sign-in. Blank the line
# again afterwards; it is read only while the user table is empty.
BOOTSTRAP_ADMIN_EMAIL=
BOOTSTRAP_ADMIN_PASSWORD=
# ── Mail transport ───────────────────────────────────────────────────────────
# NOTHING here, exactly as in .env.prod — relay host, port, STARTTLS, link origin, sender identity,
# throttle and credential are all entered at /settings → Email after your first sign-in.
#
# A private deployment makes the first-run trap SHARPER, not milder. Mail starts off, so the
# forgot-password flow does not work; if you lose BOOTSTRAP_ADMIN_PASSWORD before configuring SMTP
# there is no self-service recovery and the account has to be repaired directly in the database.
# It is easy to skip mail entirely on a machine-local install on the grounds that it is "just me" —
# and then you are the single user who cannot get back in.
#
# If you do configure it, set Settings → Email → client base URL to https://odyssey.localhost (the
# value of ODYSSEY_DOMAIN above). Links in reset mail have to work in a browser on THIS machine.
# ── Upload size ──────────────────────────────────────────────────────────────
# The TRANSPORT ceiling only — the Kestrel request-body limit and the multipart reader, both fixed at
# startup. The cap users are validated against is set at /settings and cannot exceed this number.
# Lowering needs no change here. RAISING it means raising three hops, OUTERMOST FIRST:
# `request_body max_size` in ./Caddyfile, then `client_max_body_size` in Odyssey.Client/nginx.conf,
# then this. See docs/deployment.md, "Upload size: three ceilings, in order".
FILE_UPLOAD_MAX_BYTES=67108864
# ── Fixed by the production overlay ──────────────────────────────────────────
# docker-compose.prod.yml pins all of these in the container environment, so editing them here does
# nothing. Listed because what they guarantee is worth knowing — and none of it is relaxed just
# because the deployment is private: no Development environment, no published Swagger UI, and no
# in-memory database (which nothing in the app refuses in Production, and which would silently
# discard every write on container restart).
ASPNETCORE_ENVIRONMENT=Production
SWAGGER_ENABLED=false
USE_IN_MEMORY_DATABASE=false
# Demo data is never seeded outside Development/Testing, and the overlay pins this off as well.
SEED_DEMO_DATA=false
# ── Not in this file: everything you set at /settings ────────────────────────
# Sign in as the bootstrap administrator, then work through Settings → Credentials (the five
# secrets, encrypted at rest with the Data Protection keys), Email, Registration and File analysis.
# .env.prod.example documents each one; nothing about that inventory differs for a private
# deployment. One note that does: file analysis sends uploaded documents to Anthropic, so enabling
# it on a machine-local install still sends personal data off this machine. It ships OFF.