Repository navigation
Expand file tree
/
Copy path.env.example
More file actions
224 lines (198 loc) · 13.5 KB
/
Copy path.env.example
File metadata and controls
224 lines (198 loc) · 13.5 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
# =============================================================================
# mimir-stellar/telegram-bot — copy to .env and fill in. Never commit .env.
# =============================================================================
# ── Profiles (optional) ──────────────────────────────────────────────────────
# MIMIR_PROFILE=mock selects the local mock profile for anything below that is
# left unset: loopback RPC (127.0.0.1:8420), fixture contract ids, an isolated
# data/cursor.mock.json, and placeholder Telegram values so config validation
# passes without credentials. Explicit variables always win over profile
# defaults, and any other profile name fails fast at startup. Leave unset for
# normal Testnet operation — see README "Local mock profile".
# MIMIR_PROFILE=mock
# ── Telegram ─────────────────────────────────────────────────────────────────
# Bot token from @BotFather (/newbot). Required.
BOT_TOKEN=
# Where notifications are posted. A negative id is a group/channel
# (e.g. -1001234567890); a positive id is a private chat. Required.
# See the README for how to find it.
TELEGRAM_CHAT_ID=
# To send to multiple chats, separate ids with a comma: TELEGRAM_CHAT_ID=-100123,-45678
# Optional per-contract destinations. When omitted, both sources use
# TELEGRAM_CHAT_ID for backwards-compatible single-chat deployments.
# TELEGRAM_MARKET_CHAT_ID=
# TELEGRAM_SQUAD_CHAT_ID=
# Optional allowlist for /status, /health, /contracts and /preview.
# Comma-separated list of chat ids or @channelusernames. When set, those
# commands are silently ignored in any chat not on the list. When absent or
# empty, they work in any chat the bot can see.
# Example: ALLOWED_CHAT_IDS=-1001234567890,-1009876543210,@mypublicchannel
# ALLOWED_CHAT_IDS=
# ── Mimir Soroban contracts (Stellar Testnet) ────────────────────────────────
# The live deployment the bot watches. These are the same contract ids the
# Mimir app reads as NEXT_PUBLIC_STELLAR_MARKET_CONTRACT_ID /
# NEXT_PUBLIC_STELLAR_SQUAD_CONTRACT_ID — this repo is not a Next.js app, so
# there is no NEXT_PUBLIC_ prefix here. Both are required.
MARKET_CONTRACT_ID=CDV6JXIJCALSXQELCS6YUEWJWG5DFXQK5PJ5I7MWI6KVMQJBC5DLPKZI
SQUAD_CONTRACT_ID=CBPGVXHXLULUBVZ24D6XNSUX7NH45HYXGWHAJFWTBHXYNO72KDRKCDFY
# Optional contract versions (defaults to v1).
# MARKET_CONTRACT_VERSION=v1
# SQUAD_CONTRACT_VERSION=v1
# ── Network ──────────────────────────────────────────────────────────────────
# Named Stellar network. Accepted values: testnet (default), mainnet, futurenet, custom.
# When set to testnet, mainnet, or futurenet, RPC URL, Horizon URL, and network
# passphrase all default to the well-known values for that network — you only need
# to set them below if you want to override a specific endpoint. Set STELLAR_NETWORK=custom
# and supply all three when pointing at a private or non-standard network.
# STELLAR_NETWORK=testnet
# Soroban RPC is the only endpoint the poller needs: events are read with
# `getEvents`. Horizon is kept for the explorer/tx links and future account
# reads. The passphrase must match the RPC's own getNetwork().passphrase.
# These default to the values for the selected STELLAR_NETWORK above.
STELLAR_RPC_URL=https://soroban-testnet.stellar.org
STELLAR_HORIZON_URL=https://horizon-testnet.stellar.org
STELLAR_NETWORK_PASSPHRASE=Test SDF Network ; September 2015
# Optional explorer origin (no trailing slash required). Defaults to stellar.expert.
# STELLAR_EXPLORER_BASE_URL=https://stellar.expert/explorer
# ── Poller ───────────────────────────────────────────────────────────────────
# How often to ask the RPC for new events. Minimum 5000. Testnet closes a
# ledger about every 5s, so anything under ~15s mostly polls empty pages.
POLL_INTERVAL_MS=30000
# On a cold start with no data/cursor.json, how far back to look before
# tailing. Keep it small — this is a notifier, not a backfiller. ~5 ledgers/25s.
# START_LOOKBACK_LEDGERS=60
# Where the last-seen cursor is persisted. Needs to survive restarts; see the
# "Deployment" section of the README for persisting `data/` on an always-on host
# and the "Deploying on Railway" section for the volume-backed deployment.
# `npm run cursor -- backup` validates and copies this cursor without pausing
# polling; restore requires the bot to be stopped and an explicit --force when
# replacing an existing cursor. See README "Cursor backup and restore".
# CURSOR_FILE=./data/cursor.json
# Exclusive lock so only one process owns the cursor / chat. Defaults to
# ./data/poller.lock. A second live instance exits immediately with a clear error.
# INSTANCE_LOCK_FILE=./data/poller.lock
# Where the machine-readable status snapshot is written after every poll cycle.
# Read it with `npm start -- --status` (or `node dist/index.js --status`). It
# contains no secrets: the chat id is redacted and error strings are truncated.
# STATUS_FILE=./data/status.json
# Safety valve: most notifications sent in one poll cycle. The rest are
# skipped (logged, cursor commits after the page) so a burst cannot get the bot
# rate-limited by Telegram.
# MAX_NOTIFICATIONS_PER_CYCLE=20
# Milliseconds to wait between successive Telegram sends in one cycle.
# Telegram's rate limit for sending to one chat is roughly 20 messages/minute;
# the default 1500 ms keeps peak throughput safely under that.
# INTER_SEND_DELAY_MS=1500
# ── Metrics ───────────────────────────────────────────────────────────────────
# Optional Prometheus-compatible metrics HTTP server. When set, the bot exposes
# operational counters at http://127.0.0.1:<port>/metrics in text format 0.0.4.
# Also exposes GET /health returning 200 OK for liveness checks.
#
# Disabled by default (unset = no server started). The port must be 1–65535.
#
# Example Prometheus scrape config:
# scrape_configs:
# - job_name: mimir_telegram_bot
# static_configs:
# - targets: ["127.0.0.1:9090"]
#
# Exposed metrics:
# mimir_bot_poll_cycles_total — total poll cycles started
# mimir_bot_rpc_requests_total — successful Soroban RPC getEvents calls
# mimir_bot_rpc_errors_total — failed Soroban RPC calls (cursor unchanged)
# mimir_bot_events_decoded_total — events returned by the RPC to the decoder
# mimir_bot_events_skipped_total — events not notified (unknown/cap/no formatter)
# mimir_bot_notifications_sent_total — Telegram messages successfully sent
# mimir_bot_notifications_failed_total — Telegram sends that threw (cursor advanced)
# mimir_bot_stale_cursor_events_total — times a stale-cursor warning was emitted
# mimir_bot_consecutive_failures — gauge: current consecutive full-cycle failures
#
# Deployment note: the server binds to 127.0.0.1 and is not reachable from
# outside the host without a reverse proxy. If running in Docker, map the port
# explicitly: -p 9090:9090 (or use host networking for the scraper).
# METRICS_PORT=9090
# ── Notification feature flags ───────────────────────────────────────────────
# Coarse kill switches for Telegram posts. The poller still advances the cursor
# when a flag blocks a send (lossy notifications; chain is the record).
# Accepted values: true/false, 1/0, yes/no, on/off (case-insensitive).
# Invalid values abort startup so typos fail loud. Defaults are all enabled.
# NOTIFY_ENABLED=true
# NOTIFY_MARKET=true
# NOTIFY_SQUAD=true
# ── Link previews per contract ───────────────────────────────────────────────
# Control whether Telegram unfolds link previews in notifications from each
# contract. When false (default), link previews are disabled for that contract.
# When true, Telegram will unfold any URLs in the notification message.
# Accepted values: true/false, 1/0, yes/no, on/off (case-insensitive).
# This setting applies only to notifications; commands (/status, /help, etc.)
# keep link previews disabled regardless of this setting.
# LINK_PREVIEW_MARKET=false
# LINK_PREVIEW_SQUAD=false
# How many recent event ids to remember per contract so an event repeated by
# an overlapping page, a resumed cursor, or a restart is not posted twice.
# Entries are canonical dedup keys (the RPC's own event id, or a `v2:`-prefixed
# content-derived composite when a response omits one); mixed generations load
# as opaque strings, no migration. Oldest ids are evicted first, so the window
# never grows with history; 0 disables deduplication.
# EVENT_DEDUP_WINDOW=256
# On SIGTERM/SIGINT, how long to wait for the cycle already in progress before
# flushing the cursor file and exiting. Notifications that have not been sent
# yet are dropped (counted, logged) so the drain stays inside this budget.
# 0 exits without waiting — safe for the file, but the restart resumes from the
# last completed cycle. Raise it if your RPC regularly takes longer than 10s.
# SHUTDOWN_TIMEOUT_MS=10000
# ── Repetitive error sampling ────────────────────────────────────────────────
# Identical RPC/poller error lines are sampled so that an RPC which is down for
# hours cannot flood the log. Per key (scan:market, scan:squad, cycle), the
# first LOG_SAMPLE_MAX_PER_WINDOW lines inside a window print in full; repeats
# are counted, and the line that opens the next window reports how many were
# held back. Set LOG_SAMPLE_MAX_PER_WINDOW=1 to keep only the first line of a
# run. Sampling is in-memory and resets on restart.
# LOG_SAMPLE_MAX_PER_WINDOW=3
# LOG_SAMPLE_WINDOW_MS=300000
# ── Health endpoint (local HTTP) ─────────────────────────────────────────────
# Loopback-only by default. Supervisors / deploy probes hit GET /health.
# A built-in CLI probe is available for containers: node dist/healthcheck.js
# Set HEALTH_PORT=0 to disable the listener (e.g. one-shot tooling).
# Note: For Docker deployments, set HEALTH_HOST=0.0.0.0 to expose it outside the container.
# HEALTH_HOST=127.0.0.1
# HEALTH_PORT=8787
# After the first successful poll, mark degraded if no success within this
# many milliseconds. 0 disables this poll-success check. A cursor rejected as
# stale by Soroban independently marks GET /health degraded until that target
# scans successfully; no new setting is needed for that alert.
# HEALTH_STALE_MS=90000
# Startup RPC getHealth() retries: wall-clock deadline and delay between
# attempts. Deadline 0 = single attempt (fail fast). Never logs secrets.
# STARTUP_HEALTH_DEADLINE_MS=30000
# STARTUP_HEALTH_RETRY_MS=1000
# ── Railway (persistent-volume deployment) ───────────────────────────────────
# Railway injects `PORT`; when HEALTH_PORT is unset the health endpoint binds
# that port, so the deploy healthcheck (railway.json -> GET /health) probes the
# right listener with no override. The probe arrives via the container's
# network, not loopback, so override the loopback-only default to bind it:
# HEALTH_HOST=0.0.0.0
#
# The volume is declared in railway.json (requiredMountPath="/app/data") but a
# volume is never created by config: attach one at /app/data from the Railway
# dashboard or CLI (`railway volume add --mount-path /app/data`) before the
# first deploy. CURSOR_FILE keeps its default ./data/cursor.json, which resolves
# to /app/data/cursor.json in Railway's /app working directory — no code change.
# RAILWAY_VOLUME_NAME / RAILWAY_VOLUME_MOUNT_PATH are injected automatically.
#
# CaC (`railway.json`) is deprecated in favour of Railway Infrastructure as Code
# (`.railway/railway.ts`) with a hard cutoff of 2026-12-01; this file documents
# the current behaviour and simplifies migrating to IaC. See the README.
# ── Channel Preview Mode ─────────────────────────────────────────────────────
# When true, notifications sent to Telegram are formatted with a preview badge.
# CHANNEL_PREVIEW_MODE=false
# ── Dead-letter queue ────────────────────────────────────────────────────────
# A notification whose sends exhausted their bounded retries is parked here and
# replayed on a later cycle once Telegram recovers, instead of being dropped.
# The cursor still advances either way, so a revoked token cannot wedge the
# poller. The queue is bounded: the oldest entry is dropped when it is full, and
# an entry that fails DEAD_LETTER_MAX_ATTEMPTS replays is dropped as poison.
# Stored text is truncated and the recorded error is redacted (bot token).
# Omitting DEAD_LETTER_FILE disables the queue and restores drop-and-count.
# DEAD_LETTER_FILE=./data/dead-letter.json
# DEAD_LETTER_MAX=100
# DEAD_LETTER_MAX_ATTEMPTS=10