-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
201 lines (192 loc) · 8.31 KB
/
Copy pathdocker-compose.yml
File metadata and controls
201 lines (192 loc) · 8.31 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
# OpenRWA full stack.
#
# cp .env.docker.example .env && docker compose up --build
#
# Brings up Postgres, the API, the public website and the admin console.
# For hot-reloading development, add the dev override:
# docker compose -f docker-compose.yml -f docker-compose.dev.yml up
#
# TESTNET ONLY. Chain sync is disabled by default and the gated modules
# (proof-of-reserves, redemption) stay inactive — see AGENTS.md.
name: openrwa
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER:-openrwa}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-openrwa}
POSTGRES_DB: ${POSTGRES_DB:-openrwa}
ports:
- "${POSTGRES_PORT:-5432}:5432"
volumes:
- openrwa_pg:/var/lib/postgresql/data
healthcheck:
# Gates dependent services so the API never races the database on boot.
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-openrwa} -d ${POSTGRES_DB:-openrwa}"]
interval: 5s
timeout: 5s
retries: 10
api:
build:
context: ./api
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
environment:
NODE_ENV: production
PORT: 4000
DATABASE_URL: postgresql://${POSTGRES_USER:-openrwa}:${POSTGRES_PASSWORD:-openrwa}@postgres:5432/${POSTGRES_DB:-openrwa}?schema=public
CORS_ORIGIN: ${CORS_ORIGIN:-http://localhost:3000,http://localhost:4100}
# Required. Both must be >= 32 characters; main.ts and AuthModule refuse
# to start otherwise. Never commit real values.
JWT_SECRET: ${JWT_SECRET:?JWT_SECRET is required — copy .env.docker.example to .env}
JWT_EXPIRES_IN: ${JWT_EXPIRES_IN:-12h}
SERVICE_API_TOKEN: ${SERVICE_API_TOKEN:?SERVICE_API_TOKEN is required — copy .env.docker.example to .env}
# P8 investor portal — its own signing key, must differ from JWT_SECRET.
INVESTOR_JWT_SECRET: ${INVESTOR_JWT_SECRET:?INVESTOR_JWT_SECRET is required — copy .env.docker.example to .env}
# Opt-in seeding, handled by docker-entrypoint.sh.
RUN_DB_SEED: ${RUN_DB_SEED:-false}
SEED_ADMIN_PASSWORD: ${SEED_ADMIN_PASSWORD:-}
SEED_COMPLIANCE_PASSWORD: ${SEED_COMPLIANCE_PASSWORD:-}
SEED_VIEWER_PASSWORD: ${SEED_VIEWER_PASSWORD:-}
# Chain sync — testnet only, disabled unless explicitly enabled.
CHAIN_SYNC_ENABLED: ${CHAIN_SYNC_ENABLED:-false}
CHAIN_RPC_URL: ${CHAIN_RPC_URL:-}
CHAIN_ID: ${CHAIN_ID:-11155111}
TOKEN_ADDRESS: ${TOKEN_ADDRESS:-}
CHAIN_SYNC_START_BLOCK: ${CHAIN_SYNC_START_BLOCK:-0}
# Gated modules stay inactive (AGENTS.md guardrail 2).
PROOF_OF_RESERVES_ENABLED: "false"
REDEMPTION_ENABLED: "false"
# Part of the P12 published shape; nothing reads it while redemption is
# inactive. Not overridable from .env on purpose — there is no operational
# reason to tune a threshold for a module that refuses every call.
REDEMPTION_REQUIRED_APPROVALS: "2"
# Wallet linking and token purchase. Hard-coded off, same as the two flags
# above, so a stray value in .env cannot open them.
WALLET_ENABLED: "false"
PURCHASE_ENABLED: "false"
ports:
- "${API_PORT:-4000}:4000"
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://127.0.0.1:4000/api/health || exit 1"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
web:
build:
context: .
restart: unless-stopped
depends_on:
api:
condition: service_healthy
environment:
NODE_ENV: production
PORT: 3000
NEXT_PUBLIC_SITE_URL: ${NEXT_PUBLIC_SITE_URL:-http://localhost:3000}
# The website no longer owns a waitlist store. It proxies signups to the
# API, which is the single source of truth. Server-side only, and no
# credential is sent — POST /waitlist is public by design.
WAITLIST_API_BASE: ${WAITLIST_API_BASE:-http://api:4000/api}
# Digital Asset Passport pages read the CMS's public endpoints.
# Server-side only; never NEXT_PUBLIC_, and no credential is sent.
CMS_API_BASE: ${CMS_API_BASE:-http://cms:3001/api}
# Website mode (FR-MODE, decision D4). The CMS `settings` row is the source
# of truth; src/lib/mode.ts reads this env value only as the fallback, when
# the CMS is unreachable or the row does not exist yet. It was documented in
# .env.docker.example from the start but never passed to this container, so
# the fallback always resolved to `development` regardless of .env — a
# documented knob wired to nothing. Never opens a gated module by itself.
SITE_MODE: ${SITE_MODE:-development}
ports:
- "${WEB_PORT:-3000}:3000"
admin:
build:
context: ./admin
restart: unless-stopped
depends_on:
api:
condition: service_healthy
environment:
NODE_ENV: production
PORT: 4100
# Server-side only; never exposed to the browser.
API_BASE_URL: http://api:4000/api
API_TOKEN: ${SERVICE_API_TOKEN:?SERVICE_API_TOKEN is required — copy .env.docker.example to .env}
ports:
- "${ADMIN_PORT:-4100}:4100"
# The CMS owns its own Postgres instance rather than a second database inside
# the API's. The official image only runs init scripts on a first-time empty
# data directory, so adding a database to the existing `postgres` service
# would silently skip every environment whose volume already exists. A
# separate service is also the honest shape: the CMS is its own trust
# boundary, with its own credentials, and must never reach API tables.
postgres-cms:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: ${CMS_POSTGRES_USER:-openrwa}
POSTGRES_PASSWORD: ${CMS_POSTGRES_PASSWORD:-openrwa}
POSTGRES_DB: ${CMS_POSTGRES_DB:-openrwa_cms}
ports:
# 5433 is a common second-Postgres port and collides with other local
# stacks often enough to be worth checking before you migrate:
# lsof -iTCP:5433 -sTCP:LISTEN -n -P
# Set CMS_POSTGRES_PORT in .env if it is taken, and change cms/.env to
# match. Pointing DATABASE_URI at the wrong Postgres does not error — it
# connects, and Payload migrates a database that belongs to someone else.
- "${CMS_POSTGRES_PORT:-5433}:5432"
volumes:
- openrwa_cms_pg:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${CMS_POSTGRES_USER:-openrwa} -d ${CMS_POSTGRES_DB:-openrwa_cms}"]
interval: 5s
timeout: 5s
retries: 10
cms:
build:
context: ./cms
restart: unless-stopped
depends_on:
postgres-cms:
condition: service_healthy
environment:
NODE_ENV: production
PORT: 3001
DATABASE_URI: postgresql://${CMS_POSTGRES_USER:-openrwa}:${CMS_POSTGRES_PASSWORD:-openrwa}@postgres-cms:5432/${CMS_POSTGRES_DB:-openrwa_cms}?schema=public
# Third token domain. Must be >= 32 chars and must differ from both
# JWT_SECRET and INVESTOR_JWT_SECRET; the CMS refuses to start otherwise.
PAYLOAD_SECRET: ${PAYLOAD_SECRET:?PAYLOAD_SECRET is required — copy .env.docker.example to .env}
PAYLOAD_PUBLIC_SERVER_URL: ${PAYLOAD_PUBLIC_SERVER_URL:-http://localhost:3001}
CORS_ORIGINS: ${CMS_CORS_ORIGINS:-http://localhost:3000,http://localhost:4100}
# Opt-in seeding, handled by docker-entrypoint.sh. The seed never creates
# users in production and never ships a password. It does NOT create the
# `settings` singleton row — that is a one-time manual step in the admin UI.
RUN_CMS_SEED: ${RUN_CMS_SEED:-false}
SEED_ADMIN_EMAIL: ${CMS_SEED_ADMIN_EMAIL:-}
SEED_ADMIN_PASSWORD: ${CMS_SEED_ADMIN_PASSWORD:-}
ports:
- "${CMS_PORT:-3001}:3001"
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://127.0.0.1:3001/health || exit 1"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
# One-off Foundry toolchain, not part of the running stack. Usage:
# docker compose run --rm contracts "forge build --sizes"
# docker compose run --rm contracts "forge test -vvv"
contracts:
image: ghcr.io/foundry-rs/foundry:latest
profiles: ["tools"]
working_dir: /contracts
entrypoint: ["/bin/sh", "-c"]
command: ["forge --version"]
volumes:
- ./contracts:/contracts
volumes:
openrwa_pg:
openrwa_cms_pg: