Skip to content

Commit 1c4a0af

Browse files
authored
Merge pull request #22 from sekuba/v2-backend
V2 backend and tings
2 parents da7892f + 668808f commit 1c4a0af

98 files changed

Lines changed: 17670 additions & 2603 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: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
1-
# ============================================================
2-
# Required: Mainnet Configuration
3-
# ============================================================
1+
# Slashmon frontend build configuration.
2+
# Every VITE_* value is shipped to every browser. Secrets do not belong here.
43

5-
# L1 RPC URL (supports comma-separated URLs for automatic failover)
4+
# Production Pages builds use https://api.slashveto.me. Empty means same-origin
5+
# for local development or a combined frontend/backend deployment.
6+
VITE_API_BASE_URL=
7+
8+
# Optional development-only same-origin proxy. This lets a localhost Vite UI
9+
# exercise a production backend without weakening that backend's exact CORS
10+
# origin. Keep VITE_API_BASE_URL empty when using it.
11+
# SLASHMON_DEV_API_PROXY_TARGET=https://api.slashveto.me
12+
13+
# URL path where the static PWA is installed. Keep / for local development and
14+
# dedicated same-origin hosting. /slashmon/ supports a public GitHub project
15+
# Pages monitor, but private notification watches are disabled on *.github.io.
16+
VITE_BASE_PATH=/
17+
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.
621
VITE_L1_RPC_URL=https://1rpc.io/eth
722
VITE_REGISTRY_ADDRESS=0x35b22e09Ee0390539439E24f06Da43D83f90e298
8-
9-
# testnet
1023
VITE_TESTNET_L1_RPC_URL=https://0xrpc.io/sep
1124
VITE_TESTNET_REGISTRY_ADDRESS=0xA0BFb1B494FB49041e5c6e8c2C1BE09cD171c6Ba
1225

13-
# ============================================================
14-
# Performance & Behavior Configuration (Optional)
15-
# All values below have sensible defaults and can be omitted
16-
# ============================================================
17-
18-
# Polling & Update Intervals (in milliseconds)
19-
# VITE_POLL_INTERVAL=180000 # Background L1 poll interval (default: 3 minutes)
20-
# VITE_REALTIME_COUNTDOWN_INTERVAL=1000 # Countdown timer update interval (default: 1 second)
21-
22-
# UI/UX Configuration
23-
# VITE_HOURS_THRESHOLD_FOR_DAY_DISPLAY=24 # Hours before showing days (default: 24)
24-
25-
# Debug Configuration
26-
# VITE_CONSOLE_LOG_PROBABILITY=0.2 # Probability of logging polls (0-1, default: 0.2 = 20%)
26+
# 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.

.github/workflows/ci.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Checks
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches-ignore: [main]
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
check:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@v4
18+
19+
- name: Setup Node
20+
uses: actions/setup-node@v4
21+
with:
22+
node-version: '24'
23+
24+
- name: Setup pnpm
25+
uses: pnpm/action-setup@v4
26+
with:
27+
run_install: false
28+
29+
- name: Get pnpm store directory
30+
id: pnpm-cache
31+
run: echo "STORE_PATH=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
32+
33+
- name: Cache pnpm store
34+
uses: actions/cache@v4
35+
with:
36+
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
37+
key: ${{ runner.os }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
38+
restore-keys: |
39+
${{ runner.os }}-pnpm-
40+
41+
- name: Install dependencies
42+
run: pnpm install --frozen-lockfile
43+
44+
- name: Lint, test, syntax-check, and build
45+
run: pnpm check

.github/workflows/deploy.yml

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Deploy to GitHub Pages
1+
name: Deploy public monitor to GitHub Pages
22

33
on:
44
push:
@@ -26,7 +26,7 @@ jobs:
2626
with:
2727
node-version: '24'
2828

29-
- name: Setup PNPM
29+
- name: Setup pnpm
3030
uses: pnpm/action-setup@v4
3131
with:
3232
run_install: false
@@ -35,7 +35,8 @@ jobs:
3535
id: pnpm-cache
3636
run: echo "STORE_PATH=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
3737

38-
- uses: actions/cache@v4
38+
- name: Cache pnpm store
39+
uses: actions/cache@v4
3940
with:
4041
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
4142
key: ${{ runner.os }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
@@ -45,13 +46,49 @@ jobs:
4546
- name: Install dependencies
4647
run: pnpm install --frozen-lockfile
4748

48-
- name: Build
49-
run: pnpm build
49+
- name: Require reachable HTTPS endpoints for the PWA
5050
env:
51-
VITE_L1_RPC_URL: ${{ secrets.VITE_L1_RPC_URL }}
52-
VITE_REGISTRY_ADDRESS: ${{ secrets.VITE_REGISTRY_ADDRESS }}
53-
VITE_TESTNET_L1_RPC_URL: ${{ secrets.VITE_TESTNET_L1_RPC_URL }}
54-
VITE_TESTNET_REGISTRY_ADDRESS: ${{ secrets.VITE_TESTNET_REGISTRY_ADDRESS }}
51+
PUBLIC_API_ORIGIN: https://api.slashveto.me
52+
MAINNET_L1_RPC_URLS: ${{ vars.VITE_L1_RPC_URL }}
53+
TESTNET_L1_RPC_URLS: ${{ vars.VITE_TESTNET_L1_RPC_URL }}
54+
run: |
55+
require_https_list() {
56+
local name="$1"
57+
local value="$2"
58+
59+
if [[ -z "$value" ]]; then
60+
echo "Set the $name repository variable; Pages must never ship a localhost fallback."
61+
return 1
62+
fi
63+
64+
local endpoints
65+
IFS=',' read -ra endpoints <<< "$value"
66+
for endpoint in "${endpoints[@]}"; do
67+
endpoint="${endpoint#"${endpoint%%[![:space:]]*}"}"
68+
endpoint="${endpoint%"${endpoint##*[![:space:]]}"}"
69+
if [[ "$endpoint" != https://* ]]; then
70+
echo "$name must contain only HTTPS URLs."
71+
return 1
72+
fi
73+
done
74+
}
75+
76+
if [[ -z "$PUBLIC_API_ORIGIN" || "$PUBLIC_API_ORIGIN" != https://* || "$PUBLIC_API_ORIGIN" == *,* ]]; then
77+
echo "VITE_API_BASE_URL must be one public HTTPS API origin."
78+
exit 1
79+
fi
80+
require_https_list VITE_L1_RPC_URL "$MAINNET_L1_RPC_URLS"
81+
require_https_list VITE_TESTNET_L1_RPC_URL "$TESTNET_L1_RPC_URLS"
82+
83+
- name: Lint, test, syntax-check, and build
84+
run: pnpm check
85+
env:
86+
VITE_API_BASE_URL: https://api.slashveto.me
87+
VITE_BASE_PATH: ${{ vars.VITE_BASE_PATH || '/slashmon/' }}
88+
VITE_L1_RPC_URL: ${{ vars.VITE_L1_RPC_URL }}
89+
VITE_REGISTRY_ADDRESS: ${{ vars.VITE_REGISTRY_ADDRESS }}
90+
VITE_TESTNET_L1_RPC_URL: ${{ vars.VITE_TESTNET_L1_RPC_URL }}
91+
VITE_TESTNET_REGISTRY_ADDRESS: ${{ vars.VITE_TESTNET_REGISTRY_ADDRESS }}
5592

5693
- name: Setup Pages
5794
uses: actions/configure-pages@v4

README.md

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,66 @@
1-
# Aztec Slashing Monitor
1+
# Slashmon
22

3-
This is a tool by and for the [Slash Veto Council](https://github.com/aztec-slash-veto/council/) and the Aztec community. It shows slashing rounds before they execute, including the targeted validators, amounts, veto status, and precomputed payload address.
3+
Slashmon watches Aztec slashing and has two deliberately separate parts:
44

5-
The monitor discovers the canonical Rollup, Slasher, and SlashingProposer from Aztec's stable Registry contract. It checks that the contracts point back to each other and follows Registry upgrades automatically.
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 alerting UI for the backend. The backend journals early
10+
offenses from one Aztec node, verifies L1 slashing state, and sends matched
11+
alerts through Telegram or Web Push.
612

7-
See [V5_UPGRADE_REVIEW.md](V5_UPGRADE_REVIEW.md) for the contract/source review, cutover details, and monitor impact.
13+
Node-local offenses are early warnings, not consensus. Slashmon labels them
14+
`pending`. Ethereum observations are labelled `confirmed`. The backend never
15+
turns one node's opinion into L1 truth.
816

9-
To run it locally:
17+
## Repository
1018

11-
- Use Node 24.
12-
- Copy [.env.example](.env.example) to `.env` and optionally replace the public RPC endpoints.
13-
- Run `pnpm install` and `pnpm dev`.
19+
- `src/` — React/Vite PWA containing Monitor and PINGME
20+
- `collector/` — Node backend, SQLite journal, and notification delivery
21+
- [`docs/architecture.md`](docs/architecture.md) — data flow and trust boundaries
22+
- [`docs/runbook.md`](docs/runbook.md) — production deployment and operations
23+
- [`docs/privacy.md`](docs/privacy.md) — stored data and provider exposure
1424

15-
Mainnet is the default. Add `?network=testnet` to the URL for Sepolia testnet.
25+
The ignored `apiReference.md` and `onchainSources.md` files are research
26+
material. Runtime behavior must live in committed code, ABIs, and tests.
27+
28+
## Development
29+
30+
Use Node 24 and the pinned pnpm release:
31+
32+
```bash
33+
corepack enable
34+
pnpm install
35+
cp .env.example .env
36+
cp collector/.env.example collector/.env
37+
```
38+
39+
Run the two processes in separate terminals:
40+
41+
```bash
42+
pnpm dev
43+
pnpm dev:backend
44+
```
45+
46+
For local cross-origin development, set
47+
`VITE_API_BASE_URL=http://127.0.0.1:8790`; the backend example already allows
48+
`http://localhost:5173`. Alternatively leave `VITE_API_BASE_URL` empty and set
49+
`SLASHMON_DEV_API_PROXY_TARGET` for Vite's same-origin development proxy.
50+
51+
Run the complete quality gate with:
52+
53+
```bash
54+
pnpm check
55+
```
56+
57+
All `VITE_*` values are public browser configuration. Backend RPC credentials,
58+
Telegram tokens, and VAPID private keys belong only in `collector/.env` or the
59+
production environment file.
60+
61+
Notification watches use a bearer capability stored by browser origin. Host a
62+
production PINGME installation on a dedicated origin and do not add third-party
63+
scripts. A shared GitHub Pages origin is suitable only for the public Monitor.
64+
65+
The destructive one-time backend switch is documented in the runbook and runs
66+
as `scripts/switch-backend.sh --fresh`.

SLASHING_DURATIONS.md

Lines changed: 0 additions & 80 deletions
This file was deleted.

collector/.env.example

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
1-
# Aztec admin JSON-RPC endpoint. Keep this port private.
2-
AZTEC_ADMIN_URL=http://127.0.0.1:8880
1+
# Backend identity and browser origin.
2+
SLASHMON_NETWORK=mainnet
3+
SLASHMON_PUBLIC_URL=http://localhost:5173
4+
BACKEND_CORS_ORIGIN=http://localhost:5173
35

4-
# API key printed by the Aztec node at startup. Leave unset only if admin API
5-
# authentication was explicitly disabled on the local node.
6+
# Public node RPC and private admin RPC for the same Aztec node.
7+
AZTEC_NODE_URL=http://127.0.0.1:8080
8+
AZTEC_NODE_API_KEY=
9+
AZTEC_ADMIN_URL=http://127.0.0.1:8880
610
AZTEC_ADMIN_API_KEY=
711

8-
# Collector storage and polling.
9-
COLLECTOR_DATABASE_PATH=./data/offenses.sqlite
10-
COLLECTOR_POLL_INTERVAL_MS=15000
11-
COLLECTOR_MAX_BACKOFF_MS=60000
12-
COLLECTOR_REQUEST_TIMEOUT_MS=10000
13-
COLLECTOR_STALE_AFTER_MS=60000
14-
COLLECTOR_WITHDRAW_AFTER_MISSED_POLLS=3
12+
# Comma-separated Ethereum RPCs. The network selects the chain and Registry.
13+
L1_RPC_URL=http://127.0.0.1:8545
14+
# L1_REGISTRY_ADDRESS=
15+
L1_SLASH_LOG_LOOKBACK_BLOCKS=50000
16+
17+
# Enable Telegram only when both values are set.
18+
TELEGRAM_BOT_TOKEN=
19+
TELEGRAM_BOT_USERNAME=
20+
21+
# Enable Web Push only when all three values are set.
22+
VAPID_SUBJECT=
23+
VAPID_PUBLIC_KEY=
24+
VAPID_PRIVATE_KEY=
1525

16-
# Read-only HTTP API. It binds to loopback by default.
17-
COLLECTOR_BIND_HOST=127.0.0.1
18-
COLLECTOR_PORT=8790
19-
COLLECTOR_CORS_ORIGIN=*
20-
COLLECTOR_LOG_LEVEL=info
26+
BACKEND_DATABASE_PATH=./data/slashmon.sqlite
27+
BACKEND_BIND_HOST=127.0.0.1
28+
BACKEND_PORT=8790
29+
BACKEND_TRUST_PROXY=false
30+
BACKEND_LOG_LEVEL=info

0 commit comments

Comments
 (0)