-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
161 lines (159 loc) · 8.19 KB
/
Copy pathdocker-compose.yml
File metadata and controls
161 lines (159 loc) · 8.19 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
services:
postgres:
image: postgres:16
container_name: rackstack-db
restart: unless-stopped
environment:
POSTGRES_USER: rackstack
POSTGRES_PASSWORD: rackstack
POSTGRES_DB: rackstack
volumes:
- ./pgdata:/var/lib/postgresql/data
# Creates the SEPARATE database the SuperTokens core needs (v1.8).
# Scripts here run only when the data directory is first initialised,
# so on an existing deployment this file does nothing and the database
# must be created by hand - see docs/supertokens-rollout-runbook.md.
# Creating it unconditionally is harmless: an unused empty database
# costs nothing, and having it ready removes a step from the rollout.
- ./docker/init-supertokens-db.sql:/docker-entrypoint-initdb.d/10-supertokens.sql:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U rackstack"]
interval: 10s
timeout: 5s
retries: 5
# The SuperTokens core (v1.8). Opt-in: it belongs to a compose profile, so
# `docker compose up` does NOT start it and the default passport auth stack
# is unaffected. Start it only when you are rolling SuperTokens out:
#
# docker compose --profile supertokens up -d
#
# It is deliberately not a `depends_on` of rackstack. In the default
# AUTH_MODE the server never contacts it, and making every deployment wait
# on a container it will not use is a good way to turn an unrelated
# SuperTokens problem into a RackStack outage.
supertokens:
# Pinned, not :latest. This container signs and validates every session in
# supertokens/dual mode; silently pulling a new major on the next `up -d`
# is not a risk worth taking for the component that IS the trust root.
#
# The MAJOR is pinned; the minor and patch float. That is the deliberate
# middle ground between `:latest` and a fully-frozen tag:
#
# - `:latest` would pick up a new MAJOR unannounced, and the major
# boundary is the only place core-driver-interface support realistically
# changes. supertokens-node@24 speaks CDI 5.4 *only* - a single version,
# not a range - so the compatible window is narrow, and leaving it
# breaks every login rather than degrading.
# - Freezing at a patch would mean sitting on a stale core forever,
# missing security fixes on the component that signs every session.
#
# Within a major it is very safe: core 12 still serves CDI 2.7 through 5.5,
# so minor upgrades do not drop protocol support.
#
# Core 12 is also the FLOOR: cores 9.x/10.x top out at CDI 5.2 and 11.x at
# 5.3 - each of which runs, answers health checks, accepts its API key, and
# then fails every request. This file pinned 9.3 until that was caught.
# initSuperTokens now verifies the negotiated version at BOOT (the SDK only
# notices from inside a request, i.e. on the first login), and
# `npm run supertokens:check` verifies it before cutover.
#
# Docker Hub rather than registry.supertokens.io: the latter's certificate
# chains to ISRG Root YR, a new Let's Encrypt root that older CA bundles
# (Unraid's included) do not carry yet, giving
# `x509: certificate signed by unknown authority` on pull. Same image.
image: supertokens/supertokens-postgresql:12
container_name: rackstack-supertokens
profiles: ["supertokens"]
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
# NOTE: the port is deliberately NOT published to the host.
#
# A SuperTokens core with no API key serves its entire API unauthenticated,
# and that API is the trust root of the whole stack: POST /recipe/session
# mints a session for ANY userId, and because the user-id mapping makes
# session.getUserId() return `github:37058311` verbatim, anyone who can
# reach this port can mint a valid RackStack session for any value in
# SUPER_ADMIN_IDS - which are deterministic and effectively public. That
# bypasses every guard in the Express app without touching it.
#
# rackstack reaches the core over the compose network as `supertokens:3567`
# and does not need a published port. To poke it by hand, use
# docker compose exec supertokens bash -c ':> /dev/tcp/127.0.0.1/3567'
# rather than re-adding a `ports:` mapping.
environment:
# Its OWN database, never the rackstack one - SuperTokens manages its
# own schema and must not share a database with application tables.
#
# The scheme must be postgresql://. The SuperTokens core rejects
# postgres:// at startup. (This is specific to the core: RackStack's
# own DATABASE_URL above accepts either - v1.7 verified that directly,
# against an earlier claim to the contrary.)
POSTGRESQL_CONNECTION_URI: postgresql://rackstack:rackstack@postgres:5432/supertokens
# Defence in depth behind the unpublished port: even on the compose
# network, the core should not answer to anything that merely reaches it.
# Must match SUPERTOKENS_API_KEY on the rackstack service. Generate with
# `openssl rand -hex 32` and put it in .env.
#
# Deliberately NOT the ${VAR:?error} form. Compose interpolates the whole
# file before it filters by profile, so a required-variable error here
# would break plain `docker compose up` for every deployment that never
# touches SuperTokens. The requirement is enforced in server code
# instead (see initSuperTokens), where it can fire only for operators who
# have actually opted in.
API_KEYS: ${SUPERTOKENS_API_KEY:-}
# The core phones home to api.supertokens.io on startup unless this is
# set. Off by default here for two reasons:
#
# 1. A self-hosted game server has no reason to report usage anywhere.
# 2. It is the fix for
# `javax.net.ssl.SSLHandshakeException: PKIX path building failed`
# in the core's log. api.supertokens.io's certificate chains to
# ISRG Root YR, a new Let's Encrypt root that the JVM truststore
# baked into the core image does not carry yet - so the call fails
# on every boot. It is non-fatal (the core starts and serves
# normally), but it is alarming noise in the log of the component
# that signs your sessions, and there is no reason to make the call
# at all.
DISABLE_TELEMETRY: ${SUPERTOKENS_DISABLE_TELEMETRY:-true}
healthcheck:
test: ["CMD-SHELL", "bash -c ':> /dev/tcp/127.0.0.1/3567' || exit 1"]
interval: 10s
timeout: 5s
retries: 5
rackstack:
build: .
container_name: rackstack
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
ports:
- "3000:3000"
volumes:
- ./data:/app/data
environment:
# Compose gives `environment:` precedence over `env_file:`, so a bare
# literal here would win over .env and make the documented rollback
# ("blank out DATABASE_URL and restart") a no-op on this deployment -
# the operator would have to edit this file instead, at exactly the
# moment they least want to be editing files. The ${VAR:-default} form
# keeps the working default for a fresh `docker compose up` while
# letting .env override it, including overriding it to empty to fall
# back to SQLite.
#
# `${VAR-default}`, NOT `${VAR:-default}`: the colon form treats an
# empty value as absent and would substitute the default right back in,
# so `DATABASE_URL=` in .env - the exact thing the rollback instructions
# tell you to do - would silently keep pointing at Postgres. Without the
# colon, only a genuinely unset variable takes the default.
DATABASE_URL: ${DATABASE_URL-postgresql://rackstack:rackstack@postgres:5432/rackstack}
# v1.8 auth stack selector. Defaults to the legacy passport + JWT stack,
# so `docker compose up` behaves exactly as it did before v1.8. Same
# non-colon ${VAR-default} form and the same reason: `AUTH_MODE=` in
# .env must mean "the default", not "unset, so substitute the default"
# - which is what the colon form would do, defeating the rollback.
AUTH_MODE: ${AUTH_MODE-passport}
env_file:
- .env