-
Notifications
You must be signed in to change notification settings - Fork 111
Expand file tree
/
Copy path.env.example
More file actions
245 lines (222 loc) · 13.9 KB
/
Copy path.env.example
File metadata and controls
245 lines (222 loc) · 13.9 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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# Tinybird
TINYBIRD_HOST=http://localhost:7181
TINYBIRD_TOKEN=your-tinybird-token
# Required only when raw SQL is routed to Tinybird. These values mint scoped,
# per-org read JWTs and are intentionally independent from the API token.
# TINYBIRD_SIGNING_KEY=your-tinybird-jwt-signing-key
# TINYBIRD_WORKSPACE_ID=your-tinybird-workspace-id
# Optional Tinybird-side ceiling shared by raw SQL from the API and alerting,
# with an independent rate-limit bucket for each org. Unset means no JWT RPS limit.
# TINYBIRD_RAW_SQL_JWT_RPS_LIMIT=100
# A second Tinybird workspace the ingest gateway mirrors writes into, for the
# duration of a workspace migration. Best-effort: the mirror has its own WAL
# lane and can never slow or fail ingest. Set BOTH or neither — the gateway
# refuses to start on half a config. Reads always stay on TINYBIRD_HOST.
# TINYBIRD_MIRROR_HOST=https://api.us-east.aws.tinybird.co
# TINYBIRD_MIRROR_TOKEN=your-mirror-workspace-token
# Ramp knob: percentage of orgs to mirror, by org hash (default 100). Start at 1.
# INGEST_TINYBIRD_MIRROR_SAMPLE_PERCENT=1
# INGEST_TINYBIRD_MIRROR_MAX_ATTEMPTS=5
# INGEST_TINYBIRD_MIRROR_TIMEOUT_MS=3000
# ClickHouse
CLICKHOUSE_URL=http://localhost:9000
# tinybird is the default for compatibility with existing gateway deployments.
# Set clickhouse when CLICKHOUSE_URL points at a vanilla/self-managed server so
# raw SQL preserves CLICKHOUSE_PASSWORD instead of substituting a scoped JWT.
# Env-level vanilla ClickHouse raw SQL is allowed only in single-org self-hosted mode.
CLICKHOUSE_PROVIDER=clickhouse
CLICKHOUSE_USER=maple
CLICKHOUSE_DATABASE=default
CLICKHOUSE_PASSWORD=maple
# Database
# Local non-worker entrypoints + tests use embedded PGlite. MAPLE_DB_URL is the
# PGlite data dir: blank = apps/api/.data/pglite (persistent), or `memory://`
# for ephemeral. `wrangler dev` ignores this and dials the docker-compose
# Postgres via the Hyperdrive localConnectionString in wrangler.jsonc
# (`bun run db:up && bun run db:migrate:local` to set it up).
MAPLE_DB_URL=
# Deployed stages (set in Infisical, not here): one PlanetScale Postgres connection
# string for the stage's branch (direct port 5432), used by the CI `drizzle-kit
# migrate` step and the import scripts. stg/prd bind their Hyperdrive by dashboard
# config ID and never read this at deploy time; only dev stages have alchemy parse
# it into a Hyperdrive origin. PR previews bind no database at all. NB the connect-time database is `postgres` (the cluster default),
# NOT the PlanetScale resource name `maple` (that's only for `pscale` commands).
# MAPLE_PG_URL=postgres://user:pass@host.pg.psdb.cloud:5432/postgres?sslmode=verify-full
# Base64-encoded 32-byte key (AES-256-GCM) used to encrypt private ingest keys at rest
MAPLE_INGEST_KEY_ENCRYPTION_KEY=
# HMAC key used to derive non-reversible ingest key lookup hashes
MAPLE_INGEST_KEY_LOOKUP_HMAC_KEY=
# Auth mode: clerk | self_hosted
MAPLE_AUTH_MODE=self_hosted
MAPLE_ROOT_PASSWORD=change-me
# Required when MAPLE_AUTH_MODE=clerk
# CLERK_SECRET_KEY=sk_test_xxx
# CLERK_PUBLISHABLE_KEY=pk_test_xxx
# Optional: networkless JWT verification
# CLERK_JWT_KEY=-----BEGIN PUBLIC KEY-----...
# Svix signing secret of the Clerk webhook endpoint pointing at POST /webhooks/clerk
# (user.created → signup_completed product event). Route answers 503 while unset.
# CLERK_WEBHOOK_SECRET=whsec_xxx
# Required when MAPLE_AUTH_MODE=self_hosted
MAPLE_DEFAULT_ORG_ID=default
# Web app overrides (auto-derived from above if not set)
# When running `bun dev:portless`, VITE_API_BASE_URL / VITE_INGEST_URL /
# VITE_CHAT_AGENT_URL are auto-set to https://<worktree>-<app>.localhost so
# multiple worktrees can run side by side. Values set here still win.
VITE_API_BASE_URL=http://localhost:3472
# Origin of the standalone ElectricSQL shape-proxy worker (apps/electric-sync).
VITE_ELECTRIC_SYNC_URL=http://localhost:3476
VITE_MAPLE_AUTH_MODE=self_hosted
VITE_CLERK_SIGN_IN_URL=/sign-in
VITE_CLERK_SIGN_UP_URL=/sign-up
# Ingest endpoint the browser SDK posts traces/sessions to. Auto-derived under
# portless; falls back to https://ingest.maple.dev.
# VITE_INGEST_URL=http://localhost:3474
# "off" disables rrweb self-recording in the web app.
# VITE_MAPLE_REPLAY=off
# Self-observability: the *public* ingest key the browser SDKs ship in the
# client bundle (web reads it as VITE_MAPLE_INGEST_KEY, landing as
# PUBLIC_MAPLE_INGEST_KEY). Unset means no client telemetry is sent.
# MAPLE_OTEL_PUBLIC_INGEST_KEY=maple_pk_xxx
# Landing app (Astro)
# PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxx
# Forces the visitor-id cookie's Domain=. Only needed locally: browsers make
# *.localhost cookies host-only, so landing.localhost and web.localhost would
# otherwise never resolve to the same visitor.
# PUBLIC_MAPLE_COOKIE_DOMAIN=localhost
# Ingest service
INGEST_PORT=3474
INGEST_FORWARD_OTLP_ENDPOINT=http://127.0.0.1:4318
# Required. The org the gateway's own traces/logs/metrics are filed under
# (`maple_org_id` on the resource, which the collector writes into `OrgId`).
# There is no default: an unset value used to mean a full self-telemetry stream
# landing under the literal "internal", an id no org has and no UI can read.
MAPLE_INTERNAL_ORG_ID=default
# INGEST_WRITE_MODE=tinybird
INGEST_FORWARD_TIMEOUT_MS=10000
INGEST_MAX_REQUEST_BODY_BYTES=20971520
INGEST_REQUIRE_TLS=false
# Ingest key store backend:
# - In single-tenant local dev (MAPLE_SELF_HOSTED_MODE=single_tenant +
# MAPLE_ORG_ID_OVERRIDE set), ingest auto-selects a no-DB static KeyStore
# that resolves any well-formed key to the override org. No CF creds needed.
# - In multi-tenant / production, ingest reads org_ingest_keys from
# PlanetScale Postgres — set MAPLE_PG_URL below (PSBouncer port 6432 is
# fine here; ingest is not behind Hyperdrive).
# - Force a backend with INGEST_KEY_STORE_BACKEND=static|postgres (overrides
# the mode-based default).
# INGEST_KEY_STORE_BACKEND=
# MAPLE_PG_URL=
# Billing (Autumn)
# AUTUMN_SECRET_KEY=am_sk_test_xxx
# Svix signing secret of the Autumn webhook endpoint pointing at POST /webhooks/autumn
# (billing.updated → plan_started / plan_changed / plan_cancelled). 503 while unset.
# AUTUMN_WEBHOOK_SECRET=whsec_xxx
# A configured Autumn account is authoritative for ingest entitlements. Checks
# fail open when Autumn is unavailable.
# Stripe key for Settings → Billing → Billing details (company name, address, tax
# IDs on the Stripe customer Autumn links). A restricted key with Customers
# read/write is enough. Unset → that card reports billing as not configured.
# STRIPE_SECRET_KEY=rk_test_xxx
# Email (Cloudflare Email Service)
# Delivery is via the EMAIL worker binding (no API key). EMAIL_FROM must be on an
# onboarded sending domain. Quote the value — the display-name form contains a
# space and angle brackets, which an unquoted dotenv value can't hold.
# EMAIL_FROM="Maple <notifications@noreply.maple.dev>"
# Hazel integration (OAuth)
# Maple acts as an OIDC client against Hazel's Clerk-hosted OAuth provider.
# Register Maple as an OAuth Application in Hazel's Clerk dashboard, then drop
# the credentials here. Defaults target https://clerk.hazel.sh + https://api.hazel.sh.
# HAZEL_API_BASE_URL=https://api.hazel.sh
# HAZEL_OAUTH_DISCOVERY_URL=https://clerk.hazel.sh/.well-known/openid-configuration
# HAZEL_OAUTH_CLIENT_ID=
# HAZEL_OAUTH_CLIENT_SECRET=
# HAZEL_OAUTH_SCOPES=openid email profile organizations:read channels:read channel-webhooks:write
# Slack integration (bot install via OAuth v2)
# Maple installs a Slack app into a workspace via OAuth. Create a Slack app at
# https://api.slack.com/apps, add the bot scopes, and set the redirect URL to
# <API_BASE>/oauth/slack/callback. Both values are optional — the integration
# stays disabled until they are set.
# SLACK_CLIENT_ID=
# SLACK_CLIENT_SECRET=
# Dedicated bearer secret for the internal Slack-bot resolve endpoint
# (`GET /internal/slack/workspaces/:teamId`), which hands out decrypted bot
# tokens and a full-access Maple key. It never falls back to
# INTERNAL_SERVICE_TOKEN — the endpoint answers 401 until this is set.
# SLACK_INTERNAL_SERVICE_TOKEN=
# Slack only allows one Events API Request URL per app, and it's already
# pointed at the Railway-hosted bot (apps/slack-agent — see its own
# SLACK_SIGNING_SECRET). That bot detects app_uninstalled/tokens_revoked and
# calls this API's POST /internal/slack/workspaces/:teamId/revoke (same
# SLACK_INTERNAL_SERVICE_TOKEN auth as the resolve endpoint above) so the
# binding is dropped immediately instead of only via the 6-hourly
# reconciliation cron (worker.ts `scheduled`) backstop.
# GitHub integration (GitHub App)
# Maple connects to GitHub via a GitHub App for repo access, OAuth sign-in, and
# webhooks. Create one at https://github.com/settings/apps (or your org's
# settings), then drop the credentials here. All values are optional — the
# integration stays disabled until they are set.
# --- Apple Push Notifications (iOS app) ---------------------------------------
# All three required for push to be live; device registration works without them.
# APNS_TEAM_ID= # Apple Developer team id (10 chars)
# APNS_KEY_ID= # The .p8 key id from Certificates, IDs & Profiles → Keys
# APNS_PRIVATE_KEY= # The .p8 file contents, PEM. Multi-line: quote or use \n
# GITHUB_APP_ID= # Numeric App ID from the app's settings page
# GITHUB_APP_SLUG= # App URL slug (e.g. "maple"); used to build the install URL
# GITHUB_APP_PRIVATE_KEY= # PEM private key. Multi-line: wrap in quotes or use \n escapes
# GITHUB_APP_CLIENT_ID= # OAuth client ID (for user sign-in)
# GITHUB_APP_CLIENT_SECRET= # OAuth client secret
# GITHUB_APP_WEBHOOK_SECRET= # Shared secret used to verify inbound webhook signatures
# GITHUB_API_BASE_URL=https://api.github.com # Override for GitHub Enterprise Server
# Cloudflare integration (account OAuth — Authorization Code + PKCE)
# Maple connects a user's Cloudflare account via OAuth to poll edge analytics and
# push Workers observability. Register an OAuth client in the Cloudflare dashboard,
# then drop the credentials here. All values are optional — the integration stays
# disabled until CLOUDFLARE_OAUTH_CLIENT_ID is set. The client may only request
# scopes it was created with, so keep CLOUDFLARE_OAUTH_SCOPES in sync with the
# registered client's granted set. NOTE: account-analytics.read authorizes Workers
# (account-scoped) analytics; analytics.read authorizes zone (HTTP) analytics; zone.read
# only lists zones. All three are required — omit analytics.read and zone traffic
# collection is rejected "not authorized" while Workers still works.
# CLOUDFLARE_OAUTH_CLIENT_ID= # OAuth client ID (required to enable the integration)
# CLOUDFLARE_OAUTH_CLIENT_SECRET= # OAuth client secret (omit for PKCE-only public clients)
# CLOUDFLARE_OAUTH_SCOPES=account-settings.read account-analytics.read analytics.read zone.read workers-observability.write workers-observability-telemetry.write workers-scripts.read workers-scripts.write
# The URL/base vars default to production Cloudflare. Only set them to point at
# scripts/mock-cloudflare-oauth.ts for local end-to-end testing (see its header).
# CLOUDFLARE_OAUTH_AUTHORIZE_URL=https://dash.cloudflare.com/oauth2/auth
# CLOUDFLARE_OAUTH_TOKEN_URL=https://dash.cloudflare.com/oauth2/token
# CLOUDFLARE_OAUTH_REVOKE_URL=https://dash.cloudflare.com/oauth2/revoke
# MAPLE_CLOUDFLARE_API_BASE_URL=https://api.cloudflare.com/client/v4
# OpenTelemetry (API self-observability) — read by @maple-dev/effect-sdk
# MAPLE_ENVIRONMENT=local # "local" = no export; any other value enables OTLP
# MAPLE_ENDPOINT=http://127.0.0.1:3474 # Ingest gateway endpoint (enriches with org_id)
# MAPLE_INGEST_KEY= # Ingest key (maple_pk_* or maple_sk_*) for self-observability
# MAPLE_PRODUCT_EVENTS_INGEST_KEY= # Optional override for server-side product events (defaults to MAPLE_INGEST_KEY)
# COMMIT_SHA= # Git commit SHA for service version
# Scraper internal token
SD_INTERNAL_TOKEN=dev-internal-service-token
# ElectricSQL sync (read-path). The standalone apps/electric-sync worker's
# /api/sync/shape proxy forwards authenticated, org-scoped shape requests here.
# Locally this is the docker `electric` service; in prod it's Electric Cloud
# (set SOURCE_ID + SECRET too). Leave ELECTRIC_URL empty to disable sync — the
# proxy 503s and the web app stays on its existing effect-atom fetches.
ELECTRIC_URL=http://localhost:3473
# ELECTRIC_SOURCE_ID= # Electric Cloud source id (prod only)
# ELECTRIC_SECRET= # Electric Cloud source secret (prod only)
# CI-only (PR previews): scripts/electric-pr-branch.ts provisions a per-PR Electric
# Cloud environment + Postgres source via @electric-sql/cli and exports the three
# vars above to the deploy. Set these in Infisical, not locally:
# ELECTRIC_API_TOKEN= # Electric CLI token (sv_live_...); also the workflow gate
# ELECTRIC_PROJECT_ID= # Electric project id (proj_...) the pr-<n> environment lives under
# ELECTRIC_REGION=us-east-1 # source region (default us-east-1)
# ELECTRIC_CLOUD_URL=https://api.electric-sql.cloud # Cloud base the script exports as the
# # preview worker's ELECTRIC_URL (default cloud base). Kept separate from
# # the local-dev ELECTRIC_URL above so a dev value can't leak into previews.
# LLM provider for AI chat + triage. Both paths stay wired in apps/api/src/platform/Llm.ts, so
# switching is this one var: "openrouter" (default) or "workers-ai". Model overrides are
# provider-scoped so both can stay set across a flip.
# MAPLE_LLM_PROVIDER=openrouter
# OPENROUTER_API_KEY= # required when the provider is openrouter
# MAPLE_TRIAGE_MODEL_OPENROUTER=openai/gpt-5.6-luna
# MAPLE_TRIAGE_MODEL_WORKERS_AI=@cf/moonshotai/kimi-k2.6
INTERNAL_SERVICE_TOKEN=dev-internal-service-token