Repository navigation
Expand file tree
/
Copy path.env.example
More file actions
173 lines (155 loc) · 8.35 KB
/
Copy path.env.example
File metadata and controls
173 lines (155 loc) · 8.35 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
# ============================================================================
# Inkloom — environment contract
# ============================================================================
# Copy to `.env` for local development: cp .env.example .env
# `.env` is gitignored. NEVER commit real secrets.
#
# In staging/production these are NOT read from a file. Non-secret values live
# in wrangler.jsonc `vars`; every secret is set with `wrangler secret put NAME`
# and stored in Cloudflare Secrets. See docs/ENVIRONMENT.md.
# ============================================================================
# ---------------------------------------------------------------------------
# Runtime
# ---------------------------------------------------------------------------
NODE_ENV=development
# One of: development | staging | production. Controls cookie security,
# error verbosity, and which safety rails are armed.
INKLOOM_ENV=development
# Public origin of the app. Used for absolute links in email, for strict
# origin/CSRF validation, and for canonical/OG URLs. No trailing slash.
APP_URL=http://localhost:5173
# ---------------------------------------------------------------------------
# Database (PostgreSQL)
# ---------------------------------------------------------------------------
# Local: the Docker Compose Postgres in compose.yaml (loopback-bound, port 5433).
# Production: a Neon pooled connection string, set as a Cloudflare secret.
DATABASE_URL=postgresql://inkloom:inkloom_local_dev@127.0.0.1:5433/inkloom
# Separate database used only by the integration + concurrency test suites.
# Created automatically by scripts/sql/00-init-test-db.sql on first volume init.
TEST_DATABASE_URL=postgresql://inkloom:inkloom_local_dev@127.0.0.1:5433/inkloom_test
# ---------------------------------------------------------------------------
# Authentication (Better Auth)
# ---------------------------------------------------------------------------
# SECRET. Generate with: openssl rand -base64 32
# Rotating this invalidates every session and every signed token. See
# docs/SECURITY.md "Secret rotation".
BETTER_AUTH_SECRET=dev-only-insecure-secret-replace-me-0000000000
BETTER_AUTH_URL=http://localhost:5173
# Google OAuth — optional. Leave blank to disable the provider entirely;
# the sign-in button is hidden when these are absent.
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
# ---------------------------------------------------------------------------
# Access codes
# ---------------------------------------------------------------------------
# SECRET. Server-side pepper for the keyed HMAC that fingerprints access codes.
# Redeemable codes are NEVER stored in plaintext — only HMAC-SHA256(code, pepper).
# Rotating this invalidates every existing campaign fingerprint; see
# docs/SECURITY.md before rotating.
ACCESS_CODE_PEPPER=dev-only-insecure-pepper-replace-me-000000
# ---------------------------------------------------------------------------
# Abuse-monitoring pseudonymisation
# ---------------------------------------------------------------------------
# SECRET. Pepper for the rotating keyed hash applied to IP addresses before
# storage. Raw IPs are never written to the database.
IP_HASH_PEPPER=dev-only-insecure-ip-pepper-replace-me-00
# ---------------------------------------------------------------------------
# Email (Resend in staging/production, Mailpit locally)
# ---------------------------------------------------------------------------
# One of: mailpit | resend | console
#
# mailpit Delivers to the local fake inbox at http://localhost:8025 and
# NOWHERE ELSE. Nothing reaches a real address, whatever you type
# into the signup form. This is the default so development cannot
# email real people by accident.
# resend Delivers for real, over HTTPS. Allowed locally as well as in
# staging and production.
# console Records what would have been sent, in memory. Used by tests.
#
# Verify whichever you choose actually delivers, before trusting signup:
# pnpm email:test --to you@example.com
EMAIL_TRANSPORT=mailpit
MAILPIT_HOST=127.0.0.1
MAILPIT_PORT=1025
# SECRET in staging/production. Get one free at https://resend.com/api-keys
RESEND_API_KEY=
# Resend will REJECT any From whose domain you have not verified on your
# account. Until you verify a domain, use onboarding@resend.dev — which can
# only deliver to the address that owns the Resend account:
# EMAIL_FROM="Inkloom <onboarding@resend.dev>"
EMAIL_FROM="Inkloom <no-reply@mail.inkloom.art>"
EMAIL_REPLY_TO=support@inkloom.art
SUPPORT_EMAIL=support@inkloom.art
# Where support submissions are actually delivered. SUPPORT_EMAIL is the
# address printed on the site; this is the inbox that receives complaints,
# so routing them somewhere real does not mean publishing that address.
SUPPORT_INBOX=
# ---------------------------------------------------------------------------
# Local mail relay (development only)
# ---------------------------------------------------------------------------
# Workers cannot open an SMTP socket, so the app only ever speaks HTTP. Mailpit
# can speak SMTP, so it relays onward through an ordinary mailbox — which is how
# local development reaches a real inbox without a verified sending domain.
#
# pnpm mail:relay apply these and restart Mailpit
# pnpm mail:relay --off stop relaying
#
# For Gmail: enable 2-Step Verification, then create an APP PASSWORD at
# https://myaccount.google.com/apppasswords — not your account password.
# SECRET. Never leaves this file; .env is gitignored.
SMTP_RELAY_HOST=smtp.gmail.com
SMTP_RELAY_PORT=587
SMTP_RELAY_USERNAME=
SMTP_RELAY_PASSWORD=
# ---------------------------------------------------------------------------
# Cloudflare Turnstile
# ---------------------------------------------------------------------------
# Local defaults are Cloudflare's official always-passing test keys, so signup
# works offline without a real Turnstile site. Replace in staging/production.
# Test keys: https://developers.cloudflare.com/turnstile/troubleshooting/testing/
# ---------------------------------------------------------------------------
# Admin console
# ---------------------------------------------------------------------------
# Where the console is mounted. Leave as /admin locally; in production set a
# long random segment so scanners walking /admin, /wp-admin and the rest find
# nothing. Once set, /admin itself returns a plain 404.
#
# READ AT BUILD TIME AS WELL AS RUNTIME. The route table bakes this in so every
# link the console renders is correct, which means it must be present in the
# environment that runs `pnpm build`, not only in Cloudflare's runtime vars. A
# deploy that sets it in one place and not the other produces a console whose
# pages resolve but whose links 404, or the reverse.
#
# This is an obscurity layer and never the authorization. Every admin request is
# re-checked server-side for session, role, owner, 2FA and rate limit.
ADMIN_PATH=/admin
# The single account allowed into the console, checked in addition to the role.
# Leave unset outside production, where several staff accounts are legitimate.
# Set, it means a rewritten `users.role` row is no longer sufficient on its own.
OWNER_EMAIL=
TURNSTILE_SITE_KEY=1x00000000000000000000AA
# SECRET in staging/production.
TURNSTILE_SECRET_KEY=1x0000000000000000000000000000000AA
# Set to "false" to hard-disable Turnstile locally (never in production).
TURNSTILE_ENABLED=true
# ---------------------------------------------------------------------------
# Monitoring
# ---------------------------------------------------------------------------
# Leave blank locally to disable Sentry entirely.
SENTRY_DSN=
SENTRY_ENVIRONMENT=development
SENTRY_TRACES_SAMPLE_RATE=0.1
# Emitted with every structured log line and error report.
INKLOOM_RELEASE=dev
# ---------------------------------------------------------------------------
# Analytics
# ---------------------------------------------------------------------------
# Product events are stored first-party in Postgres. This only gates whether
# the client beacon is active at all.
ANALYTICS_ENABLED=true
# ---------------------------------------------------------------------------
# Rate limiting
# ---------------------------------------------------------------------------
# All limits are overridable at runtime from /admin/settings; these are the
# boot defaults used before any override row exists.
RATE_LIMIT_ENABLED=true