Documentation · Operate
Run the alerts service
Run the service that warns margin holders by email or Telegram before a weekend and before a liquidation, with its API, environment variables and what it stores.
The alerts service warns the holder of a margin position before a weekend and before a liquidation, by email or Telegram as the holder chose. It sends no transaction, and it keeps nothing an alert does not need. You can run your own for your users, with your own sender address and bot.
Run it#
You need a registry, an RPC URL, an indexer, a public address for the links in emails, a secret of at least 32 characters, and one channel.
export ROBINHOOD_TESTNET_RPC_URL=https://rpc.testnet.chain.robinhood.com
export TAPEHOUSE_INDEXER_URL=https://indexer-production-f5dc.up.railway.app
export TAPEHOUSE_ALERTS_URL=https://alerts.example.org
export TAPEHOUSE_ALERTS_SECRET=$(openssl rand -hex 32)
export TAPEHOUSE_SMTP_URL=smtp://user:password@smtp.example.org:587
export TAPEHOUSE_ALERTS_FROM=alerts@example.org
make run-alerts CHAIN=46630CHAIN is 4663, 46630, 42161 or 412346, and REGISTRY=<file> overrides the registry. The service listens on 127.0.0.1:8090 unless you set TAPEHOUSE_ALERTS_LISTEN.
Environment variables#
| Variable | Meaning | Default |
|---|---|---|
TAPEHOUSE_DEPLOYMENTS | Path of the registry file. Required. | none |
TAPEHOUSE_RPC_URL | RPC of the registry's chain. Required. | none |
TAPEHOUSE_INDEXER_URL | Indexer that names the positions. Required. | none |
TAPEHOUSE_ALERTS_URL | The http(s) address the links in emails point to. Required. | none |
TAPEHOUSE_ALERTS_SECRET | At least 32 characters. Each unsubscribe token is derived from it. Required. | none |
TAPEHOUSE_RPC_FALLBACK_URLS | More RPC URLs, separated by commas. | none |
TAPEHOUSE_RPC_RATE | Most requests a second sent to the RPC. | 20 |
TAPEHOUSE_ALERTS_DB | Path of the SQLite file. | alerts.db |
TAPEHOUSE_ALERTS_LISTEN | Address to listen on. | 127.0.0.1:8090 |
TAPEHOUSE_ALERTS_INTERVAL | Time between evaluations, a Go duration of at least one second. | 60s |
TAPEHOUSE_SMTP_URL | SMTP server URL. Goes with TAPEHOUSE_ALERTS_FROM. | none |
TAPEHOUSE_ALERTS_FROM | Sender address for email. | none |
TELEGRAM_BOT_TOKEN | Telegram bot token. | none |
TELEGRAM_API_URL | Telegram Bot API base URL. | https://api.telegram.org |
At least one channel is required: the SMTP pair, or the bot token. Setting only one of TAPEHOUSE_SMTP_URL and TAPEHOUSE_ALERTS_FROM is an error.
How a position is judged#
Each position is read through the Go SDK at one block and valued as the liquidator values it: Stock Tokens at their band's low edge, a recalled loan less its haircut, USDG at par, and WETH at 84% of Chainlink's price. A position is left unjudged while the sequencer is unsettled, while an asset it holds is halted or has an unconfirmed multiplier step, or while WETH has no price. The computed requirement must equal the engine's currentRequirement at the block's own prices, or the position is not evaluated and nothing is said of it.
For each Stock Token held, with every other asset at its band's centre, an integer bisection finds two prices:
- the weekend price, the highest centre at which the position falls short at both edges of a closed band;
- the reopening price, the highest centre at which it falls short at its low edge against the open requirement, which is where the reopening auction may enrol it.
The accounts' own open-market liquidationPrice is shown beside them. See Liquidation.
Which alerts it sends#
The service evaluates every position on the chain's clock, once for each interval.
| Alert | When |
|---|---|
| Threshold | Equity over requirement falls under the subscriber's threshold (1.25 unless set between 1.05 and 3). Sent once, and armed again after the ratio rises 5% above the threshold. |
| Weekend | Once for each closure, when the seven-hour ramp before a weekend or holiday close begins. |
| Liquidation | The position falls short. Once for each time and each auction. |
| Unjudged | Once each time a position cannot be judged, such as during a halt. |
Shorts get threshold and liquidation alerts, not a weekend price. A failed send is tried at the next two ticks and then dropped, logged by subscription ID and never by destination.
The subscription API#
POST /v1/subscriptions takes {account, channel, destination, threshold, issuedAt, signature}. The signature is an EIP-191 personal_sign by the account itself over a fixed text that names the chain, the account, the channel and issuedAt, which may be at most ten minutes old.
| Route | What it does |
|---|---|
POST /v1/subscriptions | Creates a subscription. |
GET /v1/confirm/<token> | Confirms an email subscription. The link is valid for a day. |
GET /v1/unsubscribe/<token> | Deletes a subscription. It is the link at the foot of every email. |
DELETE /v1/subscriptions/<id> | Deletes a subscription, with a fresh signature. |
A Telegram subscription is confirmed with the code the response returns, opened as t.me/<bot>?start=<code> within an hour. /stop in Telegram deletes every subscription linked to that chat.
What it stores#
One table holds the subscriptions: the account, channel, destination, threshold, whether it is confirmed, the state of each position's alerts, and the SHA-256 of the unsubscribe token. A second table holds the codes and tokens awaiting confirmation, as digests, and deletes them when used or expired. An unconfirmed subscription goes with its code. No message is kept.
Channels#
Email goes over SMTP with STARTTLS, which the server must offer before the password is sent. Telegram goes over the Bot API. Both carry plain text.
Deploy on Railway#
alerts deploys from services/Dockerfile with SERVICE=alerts. Set TAPEHOUSE_ALERTS_LISTEN=[::]:8090, TAPEHOUSE_ALERTS_DB=/data/alerts.db, TAPEHOUSE_INDEXER_URL, TAPEHOUSE_ALERTS_URL and TAPEHOUSE_ALERTS_SECRET, with the channel's variables. It keeps its subscriptions in SQLite, so it needs a volume at /data, RAILWAY_RUN_UID=0 because Railway mounts volumes as root, and exactly one replica. It serves only /v1/... routes and has no health check path. To check it after a deployment, add its URL to SERVICE_URLS in scripts/verify-deployment.sh to confirm that it answers below 500.
Security#
The RPC URLs, the SMTP password, the bot token and the secret never appear in a log. Treat TAPEHOUSE_ALERTS_SECRET as a key: changing it invalidates every unsubscribe link already sent.