Repository navigation
Expand file tree
/
Copy path.env.example
More file actions
592 lines (475 loc) · 22.1 KB
/
Copy path.env.example
File metadata and controls
592 lines (475 loc) · 22.1 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
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
# OpenAnalytics environment template.
#
# GENERATED FILE — do not edit by hand.
# pnpm run env:example regenerates it
# pnpm run env:example:check fails if it has gone stale
#
# Its inventory comes from packages/domain/src/env.ts, which is what every
# service validates against at startup, so nothing here can be missing and
# nothing here can describe a variable the code does not read.
#
# THIS FILE IS A REFERENCE, NOT A STARTING POINT. To actually configure a
# deployment use `infra/selfhost/`: `generate-secrets.sh` writes one env file
# per service from `env/*.env.example`, already split the way the next
# paragraph requires and with every secret generated. Read this one to find
# out what a variable is for and what stops working without it.
#
# If you do write values here, quote every one — several may contain `&` or
# `?` that a plain `set -a; . ./.env` would treat as shell syntax and
# silently truncate — and DELETE the lines you are not setting rather than
# leaving them empty. `FOO=` is the empty string, not an absent variable, and
# the schema rejects it: a service handed this file verbatim does not start.
#
# "optional" below means optional *to boot*. A missing required variable stops
# the process; a missing optional one leaves the surface it powers unmounted,
# which is why nearly everything here is optional and why a half-configured
# deployment fails at startup rather than at a customer request. What stops
# working without each one is in the section headings.
#
# THIS IS NOT ONE FILE PER DEPLOYMENT. Each service refuses to start when it
# is handed a secret it has no business holding — that boundary is the reason
# a leaked collector cannot reach ClickHouse — so the `Never give this to:`
# lines below are startup failures, not advice. Split this into one env file
# per service accordingly.
# ==========================================================================
# Postgres — the control plane
# ==========================================================================
# Users, sites, tracking keys, jobs and the outbox. Every service but
# the query gateway and the realtime gateway reads it.
# api, collector, worker · optional
# Never give this to: query-gateway, realtime.
# Needed in practice by everything above the analytics read path, even though each
# service boots without it.
DATABASE_URL='postgres://openanalytics:change-me@127.0.0.1:5432/openanalytics'
# ==========================================================================
# ClickHouse — events and rollups
# ==========================================================================
# Least-privilege users, and deliberately no superuser at runtime:
# oa_ingest only INSERTs, oa_read only SELECTs, oa_maintenance runs the
# ALTER DELETEs account deletion needs, and the only account with DDL is
# oa_migration, further down under Migration tooling.
#
# They must already exist on the ClickHouse server before the migrations
# run. Neither this template nor the migration runner creates them.
# worker, query-gateway · optional
# Never give this to: api, collector, realtime.
CLICKHOUSE_URL='http://127.0.0.1:8123'
# worker, query-gateway · optional, default analytics
CLICKHOUSE_DB='analytics'
# worker · optional
# INSERT on analytics.* only (worker).
CLICKHOUSE_INGEST_USER='oa_ingest'
# worker · optional
# Never give this to: api, collector, query-gateway, realtime.
CLICKHOUSE_INGEST_PASSWORD=''
# query-gateway · optional
# SELECT on analytics.* only (query gateway).
CLICKHOUSE_READ_USER='oa_read'
# query-gateway · optional
# Never give this to: api, collector, realtime.
CLICKHOUSE_READ_PASSWORD=''
# worker · optional
# ALTER DELETE + SELECT, for account deletion (worker). Separate from the ingest
# user on purpose.
CLICKHOUSE_MAINTENANCE_USER='oa_maintenance'
# worker · optional
# Never give this to: api, collector, query-gateway, realtime.
CLICKHOUSE_MAINTENANCE_PASSWORD=''
# ==========================================================================
# Valkey / Redis — two instances, different guarantees
# ==========================================================================
# The event stream can hold the only copy of an event (noeviction + AOF);
# the realtime cache holds losable state. If the collector reaches the
# queue across the public internet, use rediss:// with AUTH — the
# connection factory requires TLS+AUTH for that hop.
# collector, worker · optional
# Never give this to: api.
EVENT_STREAM_REDIS_URL='redis://127.0.0.1:6379'
# all services · optional
REALTIME_CACHE_REDIS_URL='redis://127.0.0.1:6380'
# ==========================================================================
# Deployment shape
# ==========================================================================
# Where each piece lives, and what the product calls itself.
# all services · optional, default local
ENVIRONMENT='production'
# all services · optional, default development
NODE_ENV='production'
# all services · optional, default info
LOG_LEVEL='info'
# all services · optional, default 3000
# Per service. Each one reads its own PORT, so this belongs in the per-service env
# file, not a shared one.
PORT='3000'
# api, worker · optional, default Open Analytics
PRODUCT_NAME='Open Analytics'
# api · optional
APP_BASE_URL='https://app.analytics.example'
# api · optional
AUTH_BASE_URL='https://api.analytics.example'
# api · optional
COLLECTOR_BASE_URL='https://c.analytics.example'
# api · optional
QUERY_GATEWAY_URL='http://127.0.0.1:8090'
# all services · optional, default 0.0.0
SERVICE_VERSION='0.0.0'
# all services · optional, default unknown
GIT_COMMIT='unknown'
# ==========================================================================
# Identity and session secrets
# ==========================================================================
# Generate each one fresh: `openssl rand -hex 32`. None is derived from another.
# api · optional
# Never give this to: collector, worker, query-gateway, realtime.
# Generate with `openssl rand -hex 32`. The api and `pnpm run create-admin` must use
# the SAME value.
AUTH_SECRET=''
# api · optional
# Comma-separated. The browser origins allowed to hold a session; the CORS and CSRF
# boundary.
AUTH_TRUSTED_ORIGINS='https://app.analytics.example'
# api · optional
AUTH_COOKIE_SAMESITE=''
# api · optional, default disabled
# Password sign-in. Off here, on by default in `infra/selfhost`: it is the door a
# self-hosted install has that needs neither an OAuth app nor a mail server, and the
# first-run screen at /login creates the account it signs in with. Not a bootstrap
# flag to switch back afterwards.
AUTH_PASSWORD_SIGNIN='disabled'
# api · optional, default required
# Leave required. The first account is written verified — there is no mail to verify
# it with, which is why it exists — so the usual install never needs this.
# `optional` is the escape hatch for an install whose transport broke later.
AUTH_EMAIL_VERIFICATION='required'
# collector, worker · optional
# Never give this to: api, query-gateway, realtime.
# Keys the anonymous visitor id. Rotating it makes every returning visitor look new
# for a day.
ANONYMOUS_IDENTITY_SECRET=''
# collector, worker · optional, default 1
ANONYMOUS_IDENTITY_KEY_VERSION='1'
# api · optional
# Never give this to: collector, worker, query-gateway, realtime.
# HMACs the address a read credential connects from. Unset, no credential event is
# journalled at all.
CREDENTIAL_SOURCE_SECRET=''
# api · optional, default 1
CREDENTIAL_SOURCE_KEY_VERSION='1'
# ==========================================================================
# Email — how anyone signs in at all
# ==========================================================================
# The front door is a magic link, so a deployment with no transport here
# has no way for anyone to log in. Set the SMTP block for a self-hosted
# install; RESEND_API_KEY is the hosted alternative and wins if both are
# set. None of this is required to get in — the first-run screen at /login
# creates an account with a password — and none of it has to be a file:
# the dashboard stores a relay too, and a stored one wins over everything
# here.
# worker · optional
EMAIL_FROM='Analytics <hello@analytics.example>'
# worker · optional
# Set this and the magic link can be delivered — the whole of what a fresh install
# needs to let anyone in.
SMTP_HOST='smtp.analytics.example'
# worker · optional
SMTP_PORT='587'
# worker · optional
# `true` for implicit TLS on port 465; leave empty for 587, which upgrades with
# STARTTLS. Only the literals 'true' and 'false' are accepted.
SMTP_SECURE=''
# worker · optional
SMTP_USER='analytics@analytics.example'
# worker · optional
# Never give this to: api, collector, query-gateway, realtime.
# Worker only — the api refuses to start if it is given this.
SMTP_PASS=''
# worker · optional
# Overrides EMAIL_FROM for SMTP, for relays that refuse a From other than the
# authenticated mailbox.
SMTP_FROM=''
# worker · optional
# Never give this to: api, collector, query-gateway, realtime.
# Hosted email. Leave empty and set the SMTP block instead for a self-hosted
# install.
RESEND_API_KEY=''
# ==========================================================================
# Configuring the deployment from the dashboard
# ==========================================================================
# The mail relay and the model provider can be typed into Account →
# Deployment instead of into these files, stored encrypted under
# OA_CREDENTIAL_KEYRING and taking effect with no restart. Only the
# account that claimed the deployment — the oldest one — may use the
# screen. Set `disabled` on a multi-tenant deployment, where the
# environment IS the configuration; the api and the worker must agree.
# api, worker · optional, default enabled
DEPLOYMENT_SETTINGS='enabled'
# ==========================================================================
# OAuth sign-in (optional)
# ==========================================================================
# A provider is offered only when both halves are present.
# api · optional
GOOGLE_CLIENT_ID=''
# api · optional
# Never give this to: collector, worker, query-gateway, realtime.
GOOGLE_CLIENT_SECRET=''
# api · optional
GITHUB_CLIENT_ID=''
# api · optional
# Never give this to: collector, worker, query-gateway, realtime.
GITHUB_CLIENT_SECRET=''
# ==========================================================================
# Ed25519 signing pairs — api to gateway, api to realtime, api to collector
# ==========================================================================
# Three pairs. The PRIVATE half goes only to the api; the PUBLIC half only
# to the service that verifies. Each private key is on the verifying
# service's FORBIDDEN list, so a copy-paste that hands a verifier its own
# signing key is a startup failure rather than a silent forgery surface.
# api · optional
# Never give this to: collector, worker, query-gateway.
# Ed25519 pair with QUERY_SIGNING_PUBLIC_KEY. Generate: `openssl genpkey -algorithm
# ed25519`. Multi-line PEM cannot live in an env FILE — inject it through compose
# `environment:` or a secret manager.
QUERY_SIGNING_PRIVATE_KEY=''
# query-gateway · optional
QUERY_SIGNING_PUBLIC_KEY=''
# api, query-gateway · optional
QUERY_SIGNING_KEY_ID=''
# api, query-gateway · optional
QUERY_GATEWAY_AUDIENCE=''
# api · optional, default 60000
QUERY_SIGNATURE_LIFETIME_MS='60000'
# query-gateway · optional, default 60000
QUERY_SIGNATURE_MAX_LIFETIME_MS='60000'
# api · optional, default 20000
QUERY_GATEWAY_TIMEOUT_MS='20000'
# api · optional
# Never give this to: realtime.
# Signs realtime tokens; REALTIME_TOKEN_VERIFY_KEY is its verifying half on the
# realtime service.
REALTIME_TOKEN_SIGNING_KEY=''
# realtime · optional
REALTIME_TOKEN_VERIFY_KEY=''
# ==========================================================================
# Query gateway limits
# ==========================================================================
# Cost and concurrency guards on the analytics read path.
# query-gateway · optional, default 65536
QUERY_MAX_BODY_BYTES='65536'
# query-gateway · optional, default 15000
QUERY_TIMEOUT_MS='15000'
# query-gateway · optional, default 16
QUERY_MAX_CONCURRENCY='16'
# query-gateway · optional
QUERY_RATE_LIMIT_PER_MINUTE=''
# query-gateway · optional, default 20
QUERY_CH_MAX_EXECUTION_SECONDS='20'
# query-gateway · optional, default 200000
QUERY_CH_MAX_RESULT_ROWS='200000'
# query-gateway · optional, default 30000
QUERY_CACHE_TTL_MS='30000'
# query-gateway · optional, default 500
QUERY_CACHE_MAX_ENTRIES='500'
# query-gateway · optional, default 3600000
QUERY_CACHE_FINALIZED_TTL_MS='3600000'
# ==========================================================================
# Site cards on GET /v1/sites
# ==========================================================================
# The all-time figures and sparkline the sites grid renders (ADR-0080).
# All three have working defaults. The timeout is deliberately far below
# QUERY_GATEWAY_TIMEOUT_MS: the site list is the dashboard shell load and
# these figures only decorate it, so past the deadline the list still
# answers with null figures rather than making the shell wait.
# api · optional, default 300000
SITES_CARD_CACHE_TTL_MS='300000'
# api · optional, default 3000
SITES_CARD_GATEWAY_TIMEOUT_MS='3000'
# api · optional, default 100
SITES_CARD_MAX_SITES='100'
# ==========================================================================
# Geo (optional)
# ==========================================================================
# A licensed deploy artifact, refreshed on the host and never committed.
# collector · optional
# A City-schema .mmdb (GeoLite2 or DB-IP). Unset means every event carries null geo
# — a degradation, never a failure.
GEOIP_DB_PATH='/srv/geoip/GeoLite2-City.mmdb'
# ==========================================================================
# Object storage (optional) — import, export, backups
# ==========================================================================
# Any S3-compatible endpoint. All five, or the surface is not mounted:
# a signature needs every one of them.
# api, worker · optional
OBJECT_STORAGE_ENDPOINT='http://127.0.0.1:9000'
# api, worker · optional
OBJECT_STORAGE_REGION='local'
# api, worker · optional
OBJECT_STORAGE_BUCKET='openanalytics'
# api, worker · optional
# Never give this to: collector, query-gateway, realtime.
OBJECT_STORAGE_ACCESS_KEY_ID=''
# api, worker · optional
# Never give this to: collector, query-gateway, realtime.
OBJECT_STORAGE_SECRET_ACCESS_KEY=''
# ==========================================================================
# Revenue provider credentials (optional)
# ==========================================================================
# Encrypts customers' own payment-provider keys. Held by the api and the
# worker and by nobody else.
# api, worker · optional
# Never give this to: collector, query-gateway, realtime.
# JSON: {"active":"k1","keys":{"k1":"<base64 32 bytes>"}}. Encrypts customers' own
# provider credentials.
OA_CREDENTIAL_KEYRING=''
# ==========================================================================
# Embedded widgets (optional)
# ==========================================================================
# Where an embedded widget’s footer mark points. Unset, the footer renders
# the same line as plain text with no link at all — there is no default,
# because the mark names a product and this build does not know yours.
# api · optional
WIDGET_WATERMARK_URL=''
# ==========================================================================
# AI assistant (optional)
# ==========================================================================
# Unset, the assistant routes still answer and say the provider is not configured.
# api · optional
# Never give this to: collector, worker, query-gateway, realtime.
# The assistant. Unset, the assistant routes still answer and report that no
# provider is configured.
OPENAI_API_KEY=''
# api · optional
OPENAI_BASE_URL=''
# api · optional
ASSISTANT_MODEL=''
# ==========================================================================
# Metrics (optional)
# ==========================================================================
# Prometheus remote-write. Without it every service keeps its structured-log
# metrics floor, which is a degradation and not a failure.
# all services · optional
# Prometheus remote-write. Unset, every service keeps its structured-log metrics
# floor.
METRICS_REMOTE_WRITE_URL=''
# all services · optional
METRICS_REMOTE_WRITE_USER=''
# all services · optional
METRICS_REMOTE_WRITE_TOKEN=''
# all services · optional, default 15
METRICS_FLUSH_INTERVAL_SECONDS='15'
# ==========================================================================
# Migration tooling
# ==========================================================================
# Read directly by the two migration CLIs, not by any service.
# Optional. `pnpm run migrate:postgres` falls back to DATABASE_URL; set this only
# when migrations run as a different, DDL-capable role.
POSTGRES_MIGRATION_URL=''
# DDL on analytics.* — used by `pnpm run migrate:clickhouse` and by nothing else. On
# every service FORBIDDEN list.
CLICKHOUSE_MIGRATION_USER='oa_migration'
CLICKHOUSE_MIGRATION_PASSWORD=''
# The ClickHouse migration CLI reads CLICKHOUSE_DATABASE; the services read
# CLICKHOUSE_DB. Set both to the same value.
CLICKHOUSE_DATABASE='analytics'
# ==========================================================================
# Frontend (apps/web)
# ==========================================================================
# BUILD-TIME, not run-time: Next.js inlines every NEXT_PUBLIC_* value into
# the bundle, so these must be present when `next build` runs and changing
# them later requires a rebuild, not a restart. They are public by
# definition — never put a secret behind this prefix.
# The api service, as the browser reaches it.
NEXT_PUBLIC_API_URL='https://api.analytics.example'
# The collector, as the browser reaches it. Also the host that serves oa.js.
NEXT_PUBLIC_COLLECTOR_URL='https://c.analytics.example'
# Development only: forces the live api even when the mock flag is on.
NEXT_PUBLIC_LIVE_API=''
# Development only: renders the dashboard against fixtures instead of a live api.
NEXT_PUBLIC_MOCK_API=''
# The collector the injected tracker posts to, and the host it loads oa.js from.
NEXT_PUBLIC_OA_COLLECTOR_URL='https://c.analytics.example'
# Comma-separated hostnames to measure. Only the marketing hosts belong here —
# listing the dashboard host counts signed-in sessions as landing traffic.
NEXT_PUBLIC_OA_TRACKED_HOSTS='analytics.example,www.analytics.example'
# Optional self-measurement: the tracking_write key of the site this install serves,
# so the marketing pages report to your own dashboard. Unset, no tracker is
# injected. All three OA_ variables are needed together.
NEXT_PUBLIC_OA_TRACKING_KEY=''
# The realtime gateway, as the browser reaches it.
NEXT_PUBLIC_REALTIME_URL='https://rt.analytics.example'
# The one-line description in the metadata and the OG card.
NEXT_PUBLIC_SITE_DESCRIPTION=''
# Where a human reaches whoever runs this install. Unset, the surfaces that would
# print it (the error screens, the troubleshooting page) say nothing rather than
# offering an address nobody reads.
NEXT_PUBLIC_SITE_EMAIL=''
# The product name shown in page titles and metadata.
NEXT_PUBLIC_SITE_NAME='Open Analytics'
# The origin this dashboard is served from. Canonical URLs, sitemap.xml and the OG
# card hang off it; unset, the app publishes no absolute URLs and no structured data
# rather than claiming an address it does not have.
NEXT_PUBLIC_SITE_URL='https://analytics.example'
# ==========================================================================
# Tuning — every one of these has a working default
# ==========================================================================
# Validated by the cross-service policy schema, which also enforces the
# invariants *between* them: a dedup window shorter than the accepted event
# lateness, for instance, is refused at startup rather than silently
# double-billing a retry. Left commented out on purpose — the defaults are
# the decisions, and the values below are those defaults, shown so an
# operator can see what is in force without reading the source.
# ACKED_QUEUE_RETENTION_DAYS='7'
# BOT_RULESET_VERSION='1'
# CLICKHOUSE_DEDUP_WINDOW_BLOCKS='1000'
# DELETION_ACCEPTANCE_MARGIN_MS='30000'
# EVENT_MAX_FUTURE_SKEW_SECONDS='300'
# EVENT_MAX_LATENESS_HOURS='24'
# EXPORT_MAX_CHUNK_BYTES='67108864'
# EXPORT_OBJECT_TTL_DAYS='7'
# IDEMPOTENCY_CLAIM_MAX_AGE_HOURS='1'
# IDEMPOTENCY_KEY_RETENTION_HOURS='24'
# IMPORT_MAX_ARCHIVE_BYTES='268435456'
# IMPORT_MAX_ENTRIES='32'
# IMPORT_MAX_ENTRY_BYTES='1073741824'
# IMPORT_MAX_ROW_BYTES='65536'
# IMPORT_MAX_TOTAL_UNCOMPRESSED_BYTES='2147483648'
# IMPORT_STAGING_CHUNK_BYTES='8388608'
# IMPORT_UPLOAD_TTL_DAYS='7'
# INGEST_CONFIG_CACHE_TTL_SECONDS='30'
# INGEST_DEDUP_TTL_DAYS='7'
# JOB_MAX_ATTEMPTS='100'
# LIFECYCLE_SWEEP_INTERVAL_MS='300000'
# LIFECYCLE_SWEEP_PAGE_SIZE='100'
# LIMITER_FAIL_OPEN_MAX_SECONDS='300'
# LIMITER_FALLBACK_IP_PER_MINUTE='120'
# LIMITER_FALLBACK_LRU_ENTRIES='10000'
# QUEUE_FAILURE_RETRY_AFTER_SECONDS='2'
# QUOTA_BUFFER_EVENTS='5000'
# RAPID_BURN_DAILY_FRACTION='0.5'
# RATE_LIMIT_IDENTITY_PER_MINUTE='100'
# RATE_LIMIT_IP_SITE_BURST='120'
# RATE_LIMIT_SITE_PER_MINUTE='60000'
# REALTIME_EPOCH_CHECK_SECONDS='15'
# REALTIME_SNAPSHOT_CACHE_SECONDS='2'
# REALTIME_SNAPSHOT_MAX_VISITORS='5000'
# REALTIME_SNAPSHOT_REFRESH_SECONDS='10'
# REALTIME_TOKEN_TTL_SECONDS='60'
# REVENUE_BACKFILL_DAYS='90'
# REVENUE_CURRENCY_REFRESH_HOURS='6'
# REVENUE_RECONCILE_BATCH='10'
# REVENUE_RECONCILE_STALE_MINUTES='15'
# REVENUE_RECONCILE_WINDOW_HOURS='48'
# REVENUE_SYNC_PAGE_SIZE='100'
# SESSION_INACTIVITY_MINUTES='30'
# SESSION_MAX_LENGTH_HOURS='24'
# SITE_CEILING_ALERT_CONSECUTIVE_DAYS='3'
# SITE_DAILY_EVENT_CEILING='6000000'
# SITE_QUEUE_INDEX_TTL_DAYS='8'
# WORKER_BATCH_MAX_ATTEMPTS='8'
# WORKER_BATCH_MAX_BYTES='4194304'
# WORKER_BATCH_MAX_FLUSH_MS='1500'
# WORKER_BATCH_MAX_ROWS='1000'
# WORKER_BATCH_MIN_FLUSH_MS='500'
# WORKER_CLAIM_MIN_IDLE_MS='120000'
# WORKER_LEASE_TTL_MS='60000'
# WORKER_RETRY_BASE_MS='500'
# WORKER_RETRY_MAX_MS='30000'