Skip to content

Commit dfd42f6

Browse files
authored
Merge pull request #23 from sekuba/v33
V33
2 parents cf21532 + 68f8d9d commit dfd42f6

146 files changed

Lines changed: 12606 additions & 16724 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Slashmon frontend build configuration.
1+
# slashveto.me frontend build configuration.
22
# Every VITE_* value is shipped to every browser. Secrets do not belong here.
33

44
# Production Pages builds use https://api.slashveto.me. Empty means same-origin
@@ -15,13 +15,12 @@ VITE_API_BASE_URL=
1515
# Pages monitor, but private notification watches are disabled on *.github.io.
1616
VITE_BASE_PATH=/
1717

18-
# Public Ethereum RPCs used by the browser's independent L1 view.
19-
# Comma-separated URLs enable failover. A missing value fails visibly instead
20-
# of probing the browser user's localhost.
18+
# One public Ethereum RPC per network for the independent browser view. A
19+
# missing value fails visibly instead of probing the browser user's localhost.
2120
VITE_L1_RPC_URL=https://1rpc.io/eth
2221
VITE_REGISTRY_ADDRESS=0x35b22e09Ee0390539439E24f06Da43D83f90e298
2322
VITE_TESTNET_L1_RPC_URL=https://0xrpc.io/sep
2423
VITE_TESTNET_REGISTRY_ADDRESS=0xA0BFb1B494FB49041e5c6e8c2C1BE09cD171c6Ba
2524

2625
# The Web Push public key is deliberately not a VITE_* variable. The frontend
27-
# fetches it from GET /api/v2/config; its private half stays on the backend.
26+
# fetches it from GET /api/config; its private half stays on the backend.

.github/workflows/deploy.yml

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -48,39 +48,28 @@ jobs:
4848
- name: Install dependencies
4949
run: pnpm install --frozen-lockfile
5050

51-
- name: Require reachable HTTPS endpoints for the PWA
51+
- name: Require HTTPS endpoints for the PWA
5252
env:
5353
PUBLIC_API_ORIGIN: https://api.slashveto.me
54-
MAINNET_L1_RPC_URLS: ${{ vars.VITE_L1_RPC_URL }}
55-
TESTNET_L1_RPC_URLS: ${{ vars.VITE_TESTNET_L1_RPC_URL }}
54+
MAINNET_L1_RPC_URL: ${{ vars.VITE_L1_RPC_URL }}
55+
TESTNET_L1_RPC_URL: ${{ vars.VITE_TESTNET_L1_RPC_URL }}
5656
run: |
57-
require_https_list() {
57+
require_https_url() {
5858
local name="$1"
5959
local value="$2"
6060
61-
if [[ -z "$value" ]]; then
61+
if [[ -z "$value" || "$value" != https://* || "$value" == *,* ]]; then
6262
echo "Set the $name repository variable; Pages must never ship a localhost fallback."
6363
return 1
6464
fi
65-
66-
local endpoints
67-
IFS=',' read -ra endpoints <<< "$value"
68-
for endpoint in "${endpoints[@]}"; do
69-
endpoint="${endpoint#"${endpoint%%[![:space:]]*}"}"
70-
endpoint="${endpoint%"${endpoint##*[![:space:]]}"}"
71-
if [[ "$endpoint" != https://* ]]; then
72-
echo "$name must contain only HTTPS URLs."
73-
return 1
74-
fi
75-
done
7665
}
7766
7867
if [[ -z "$PUBLIC_API_ORIGIN" || "$PUBLIC_API_ORIGIN" != https://* || "$PUBLIC_API_ORIGIN" == *,* ]]; then
7968
echo "VITE_API_BASE_URL must be one public HTTPS API origin."
8069
exit 1
8170
fi
82-
require_https_list VITE_L1_RPC_URL "$MAINNET_L1_RPC_URLS"
83-
require_https_list VITE_TESTNET_L1_RPC_URL "$TESTNET_L1_RPC_URLS"
71+
require_https_url VITE_L1_RPC_URL "$MAINNET_L1_RPC_URL"
72+
require_https_url VITE_TESTNET_L1_RPC_URL "$TESTNET_L1_RPC_URL"
8473
8574
- name: Lint, test, syntax-check, and build
8675
run: pnpm check

README.md

Lines changed: 64 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,89 @@
1-
# Slashmon
1+
# slashveto.me
22

3-
Slashmon watches Aztec slashing and has two deliberately separate parts:
3+
slashveto.me tracks an Aztec sequencer from the first observed duty miss to an
4+
L1 slash and ejection:
45

5-
- **Monitor** is a browser-only view of public Ethereum state. It resolves the
6-
canonical Aztec contracts and checks slashing rounds directly through public
7-
L1 RPCs. Its on-page details panel can select a browser-local RPC and inspect
8-
the resolved deployment metadata.
9-
- **PINGME** is the backend UI. It can search the complete journal for one
10-
sequencer, manage address watches, and send matched alerts through Telegram
11-
or Web Push. The backend records per-duty inactivity precursors and
12-
registered offenses from one Aztec node, then verifies L1 slashing state.
6+
```text
7+
duty miss → node offense → L1 vote → quorum → execution delay
8+
→ executable → executed → stake removed → ejection
9+
```
1310

14-
Node-local offenses are early warnings, not consensus. Slashmon labels them
15-
`pending`. Ethereum observations are labelled `confirmed`. The backend never
16-
turns one node's opinion into L1 truth.
11+
A path can stop at any step. The product keeps candidate amounts, executed
12+
rounds, and actual stake deductions separate. L1 votes do not encode an
13+
offense reason; a displayed reason is always labelled as evidence from the
14+
attached Aztec node.
1715

18-
## Repository
16+
## Surfaces
17+
18+
| Surface | Source | Purpose |
19+
| --- | --- | --- |
20+
| **Monitor** | Ethereum RPC queried in the browser | Independent view of canonical contracts, votes, candidates, execution, and slash logs. |
21+
| **PINGME** | Backend using one Aztec node and Ethereum RPC | Earlier Sentinel/offense evidence, durable cases, and Telegram or Web Push alerts. |
1922

20-
- `src/` — React/Vite PWA containing Monitor and PINGME
21-
- `collector/` — Node backend, SQLite journal, and notification delivery
22-
- [`docs/architecture.md`](docs/architecture.md) — data flow and trust boundaries
23-
- [`docs/notifications.md`](docs/notifications.md) — notification stages, required context, and links
24-
- [`docs/runbook.md`](docs/runbook.md) — production deployment and operations
25-
- [`docs/privacy.md`](docs/privacy.md) — stored data and provider exposure
23+
Monitor never calls the backend. PINGME keeps the last known state when one
24+
source fails and reports that source as stale. Neither surface is an oracle:
25+
node evidence is one observer's report, while L1 establishes contract state
26+
without revealing the reason behind a vote.
27+
28+
The primary object is a slashing case: network, contract lineage, sequencer,
29+
and target epoch with its source observations and state transitions. Linking is
30+
exact and conservative. An actual slash joins through its execution transaction
31+
and action order, never by address and approximate time.
32+
33+
## Repository
2634

27-
The ignored `apiReference.md` and `onchainSources.md` files are research
28-
material. Runtime behavior must live in committed code, ABIs, and tests.
35+
- `shared/protocol/` contains the pure case projection, tallying, transitions,
36+
and notification wording shared by frontend and backend.
37+
- `src/` contains the React PWA and independent browser L1 collector.
38+
- `collector/` contains the Node backend, SQLite repository, three evidence
39+
collectors, API, durable outbox, Telegram, and Web Push.
40+
- `scripts/deploy-backend.sh` installs an immutable backend release under
41+
systemd.
42+
43+
The backend API is rooted at `/api`. `/live` reports process liveness and
44+
`/health` reports whether the required evidence sources are current.
45+
46+
| Method | Route | Purpose |
47+
| --- | --- | --- |
48+
| `GET` | `/api/config`, `/api/status`, `/api/network` | Capabilities, freshness, protocol state, and public cases |
49+
| `GET` | `/api/sequencers/:address`, `/api/cases/:id` | Public sequencer and exact-case views |
50+
| `POST` | `/api/watches` | Create a private watch and return its management token once |
51+
| `GET/PATCH/DELETE` | `/api/watches/:id` | Bearer-authenticated watch management |
52+
| `PUT/DELETE` | `/api/watches/:id/channels/web_push` | Web Push enrollment |
53+
| `POST` | `/api/watches/:id/channels/telegram-link` | One-time Telegram enrollment link |
54+
| `POST` | `/api/watches/:id/channels/test` | Queue a test alert |
2955

3056
## Development
3157

32-
Use Node 24 and the pinned pnpm release:
58+
Node 24 and the pinned pnpm version are required:
3359

3460
```bash
3561
corepack enable
36-
pnpm install
62+
pnpm install --frozen-lockfile
3763
cp .env.example .env
3864
cp collector/.env.example collector/.env
39-
```
40-
41-
Run the two processes in separate terminals:
42-
43-
```bash
4465
pnpm dev
4566
pnpm dev:backend
4667
```
4768

48-
For local cross-origin development, set
49-
`VITE_API_BASE_URL=http://127.0.0.1:8790`; the backend example already allows
50-
`http://localhost:5173`. Alternatively leave `VITE_API_BASE_URL` empty and set
51-
`SLASHMON_DEV_API_PROXY_TARGET` for Vite's same-origin development proxy.
52-
53-
Run the complete quality gate with:
69+
Run the release gate with:
5470

5571
```bash
5672
pnpm check
5773
```
5874

59-
All `VITE_*` values are public browser configuration. Backend RPC credentials,
60-
Telegram tokens, and VAPID private keys belong only in `collector/.env` or the
61-
production environment file.
75+
Every `VITE_*` value is public. RPC credentials, Aztec admin credentials,
76+
Telegram tokens, VAPID private keys, and the SQLite database belong only in the
77+
backend environment. The ignored `apiReference.md` and `onchainSources.md` are
78+
local research inputs, not runtime dependencies or published documentation.
79+
80+
## Documentation
6281

63-
Notification watches use a bearer capability stored by browser origin. Host a
64-
production PINGME installation on a dedicated origin and do not add third-party
65-
scripts. A shared GitHub Pages origin is suitable only for the public Monitor.
82+
- [Protocol and correctness model](docs/protocol.md)
83+
- [Notification contract](docs/notifications.md)
84+
- [Production runbook](docs/runbook.md)
6685

67-
Production backend deployments use `scripts/deploy-backend.sh`: `--fresh`
68-
resets all state, while `--upgrade` preserves and backs up the current database.
86+
The protocol model was checked against the active Aztec mainnet deployment and
87+
[`aztec-packages` commit `def7152a`](https://github.com/AztecProtocol/aztec-packages/tree/def7152aa13dc0f880f24e45ce39442908170878)
88+
on 2026-07-29. Contracts and parameters are upgradeable; runtime code discovers
89+
the responsible lineage and reads its values.

collector/.env.example

Lines changed: 14 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,22 @@
1-
# Backend identity and browser origin.
2-
SLASHMON_NETWORK=mainnet
1+
# Local backend. These URLs are the built-in defaults and are shown here so
2+
# the three required upstreams are explicit.
33
SLASHMON_PUBLIC_URL=http://localhost:5173
44
BACKEND_CORS_ORIGIN=http://localhost:5173
5-
6-
# Public node RPC and private admin RPC for the same Aztec node.
75
AZTEC_NODE_URL=http://127.0.0.1:8080
8-
AZTEC_NODE_API_KEY=
96
AZTEC_ADMIN_URL=http://127.0.0.1:8880
10-
AZTEC_ADMIN_API_KEY=
11-
AZTEC_SENTINEL_POLL_INTERVAL_MS=60000
12-
AZTEC_SENTINEL_LOOKBACK_EPOCHS=3
13-
AZTEC_SENTINEL_EPOCH_END_BUFFER_SLOTS=2
14-
AZTEC_SENTINEL_VALIDATOR_CONCURRENCY=8
15-
AZTEC_SENTINEL_VALIDATOR_MAX_RESPONSE_BYTES=2097152
16-
17-
# Comma-separated Ethereum RPCs. The network selects the chain and Registry.
187
L1_RPC_URL=http://127.0.0.1:8545
19-
# L1_REGISTRY_ADDRESS=
20-
L1_SLASH_LOG_LOOKBACK_BLOCKS=600
21-
22-
# Enable Telegram only when both values are set.
23-
TELEGRAM_BOT_TOKEN=
24-
TELEGRAM_BOT_USERNAME=
25-
26-
# Enable Web Push only when all three values are set.
27-
VAPID_SUBJECT=
28-
VAPID_PUBLIC_KEY=
29-
VAPID_PRIVATE_KEY=
308

31-
BACKEND_DATABASE_PATH=./data/slashmon.sqlite
32-
BACKEND_BIND_HOST=127.0.0.1
33-
BACKEND_PORT=8790
34-
BACKEND_TRUST_PROXY=false
9+
# Add only when required by the endpoints above.
10+
# AZTEC_NODE_API_KEY=
11+
# AZTEC_ADMIN_API_KEY=
3512

36-
# Abuse controls. Defaults suit a small public deployment; raise deliberately
37-
# if normal traffic grows. Per-IP limits require a trusted loopback proxy to
38-
# overwrite X-Real-IP or X-Forwarded-For.
39-
BACKEND_READ_RATE_LIMIT_MAX_PER_MINUTE=180
40-
BACKEND_READ_RATE_LIMIT_MAX_PER_MINUTE_GLOBAL=600
41-
BACKEND_MUTATION_RATE_LIMIT_MAX_PER_MINUTE=20
42-
BACKEND_WATCHLIST_MUTATION_RATE_LIMIT_MAX_PER_MINUTE=20
43-
BACKEND_SUBSCRIPTION_CREATE_MAX_PER_HOUR_PER_IP=3
44-
BACKEND_SUBSCRIPTION_CREATE_MAX_PER_DAY_PER_IP=10
45-
BACKEND_SUBSCRIPTION_CREATE_MAX_PER_HOUR_GLOBAL=10
46-
BACKEND_SUBSCRIPTION_CREATE_MAX_PER_DAY_GLOBAL=50
47-
BACKEND_NOTIFICATION_TEST_COOLDOWN_MS=300000
48-
BACKEND_NOTIFICATION_TEST_MAX_PER_HOUR_GLOBAL=30
49-
BACKEND_NOTIFICATION_TEST_MAX_PER_DAY_GLOBAL=100
50-
BACKEND_WEB_PUSH_ENROLLMENT_MAX_PER_HOUR_PER_WATCHLIST=3
51-
BACKEND_WEB_PUSH_ENROLLMENT_MAX_PER_DAY_PER_WATCHLIST=10
52-
BACKEND_WEB_PUSH_ENROLLMENT_MAX_PER_HOUR_GLOBAL=20
53-
BACKEND_WEB_PUSH_ENROLLMENT_MAX_PER_DAY_GLOBAL=100
54-
TELEGRAM_SEND_MAX_PER_SECOND=20
55-
TELEGRAM_LOW_PRIORITY_SEND_MAX_PER_SECOND=5
56-
TELEGRAM_CHAT_SEND_INTERVAL_MS=1000
13+
# Uncomment for an exact mainnet backfill from the first current-stack Rollup
14+
# block. Historical observations are indexed without sending notifications.
15+
# L1_SLASH_LOG_START_BLOCK=25533241
5716

58-
BACKEND_LOG_LEVEL=info
17+
# Optional channels. Each group must be complete.
18+
# TELEGRAM_BOT_TOKEN=
19+
# TELEGRAM_BOT_USERNAME=
20+
# VAPID_SUBJECT=mailto:operator@example.com
21+
# VAPID_PUBLIC_KEY=
22+
# VAPID_PRIVATE_KEY=

0 commit comments

Comments
 (0)