Skip to content

Commit 873cfbf

Browse files
committed
docs: refresh AGENTS.md
1 parent 3e45f6e commit 873cfbf

File tree

2 files changed

+20
-13
lines changed

2 files changed

+20
-13
lines changed

AGENTS.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ This is a **single-crate Rust binary** (`term-executor`) built with Axum. There
1212

1313
```
1414
Client → POST /submit (multipart archive) → term-executor
15-
1. Authenticate via X-Hotkey header (SS58 hotkey)
15+
1. Authenticate via X-Hotkey, X-Nonce, X-Signature, X-Api-Key headers
1616
2. Extract uploaded archive (zip/tar.gz) containing tasks/ and agent_code/
1717
3. Parse each task: workspace.yaml, prompt.md, tests/
1818
4. For each task (concurrently, up to limit):
@@ -33,7 +33,7 @@ Client → POST /submit (multipart archive) → term-executor
3333
| `src/main.rs` | Entry point — bootstraps config, session manager, executor, Axum server, reaper tasks |
3434
| `src/config.rs` | `Config` struct loaded from environment variables with defaults; `AUTHORIZED_HOTKEY` constant |
3535
| `src/handlers.rs` | Axum route handlers: `/health`, `/status`, `/metrics`, `/submit`, `/batch/{id}`, `/batch/{id}/tasks`, `/batch/{id}/task/{task_id}`, `/batches` |
36-
| `src/auth.rs` | Hotkey authentication: `extract_hotkey()`, `verify_hotkey()`, `validate_ss58()` |
36+
| `src/auth.rs` | Authentication: `extract_auth_headers()`, `verify_request()`, `validate_ss58()`, sr25519 signature verification via `verify_sr25519_signature()`, `NonceStore` for replay protection, `AuthHeaders`/`AuthError` types |
3737
| `src/executor.rs` | Core evaluation engine — spawns batch tasks that clone repos, run agents, run tests concurrently |
3838
| `src/session.rs` | `SessionManager` with `DashMap`, `Batch`, `BatchResult`, `TaskResult`, `BatchStatus`, `TaskStatus`, `WsEvent` types |
3939
| `src/task.rs` | Archive extraction (zip/tar.gz), task directory parsing, agent code loading, language detection |
@@ -43,7 +43,7 @@ Client → POST /submit (multipart archive) → term-executor
4343

4444
### Key Shared State (via `Arc`)
4545

46-
- `AppState` (in `handlers.rs`) holds `Config`, `SessionManager`, `Metrics`, `Executor`, `started_at`
46+
- `AppState` (in `handlers.rs`) holds `Config`, `SessionManager`, `Metrics`, `Executor`, `NonceStore`, `started_at`
4747
- `SessionManager` uses `DashMap<String, Arc<Batch>>` for lock-free concurrent access
4848
- Per-batch `Semaphore` in `executor.rs` controls concurrent tasks within a batch (configurable, default: 8)
4949
- `broadcast::Sender<WsEvent>` per batch for WebSocket event streaming
@@ -59,7 +59,7 @@ Client → POST /submit (multipart archive) → term-executor
5959
- **Archive Handling**: `flate2` + `tar` (tar.gz), `zip` 2 (zip)
6060
- **Error Handling**: `anyhow` 1 + `thiserror` 2
6161
- **Logging**: `tracing` + `tracing-subscriber` with env-filter
62-
- **Crypto/Identity**: `sha2`, `hex`, `base64`, `bs58` (SS58 address validation), `uuid` v4
62+
- **Crypto/Identity**: `sha2`, `hex`, `base64`, `bs58` (SS58 address validation), `schnorrkel` 0.11 (sr25519 signature verification), `rand_core` 0.6, `uuid` v4
6363
- **Time**: `chrono` with serde support
6464
- **Build Tooling**: `mold` linker via `.cargo/config.toml`, `clang` as linker driver
6565
- **Container**: Multi-stage Dockerfile — `rust:1.93-slim-bookworm` builder → `debian:bookworm-slim` runtime (includes python3, pip, venv, build-essential, git, curl)
@@ -168,4 +168,4 @@ Both hooks are activated via `git config core.hooksPath .githooks`.
168168

169169
## Authentication
170170

171-
Authentication uses SS58 hotkey validation via the `X-Hotkey` HTTP header combined with an API key via the `X-Api-Key` header. The authorized hotkey is hardcoded as `AUTHORIZED_HOTKEY` in `src/config.rs`. The API key is configured via the `WORKER_API_KEY` environment variable (required). Only requests with both a matching hotkey and a valid API key can submit batches via `POST /submit`. All other endpoints are open.
171+
Authentication requires four HTTP headers: `X-Hotkey` (SS58 address), `X-Nonce` (unique per-request), `X-Signature` (sr25519 hex signature of `hotkey + nonce`), and `X-Api-Key`. The authorized hotkey is hardcoded as `AUTHORIZED_HOTKEY` in `src/config.rs`. The API key is configured via the `WORKER_API_KEY` environment variable (required). Verification steps: hotkey must match `AUTHORIZED_HOTKEY`, API key must match, SS58 format must be valid, nonce must not have been seen before (replay protection via `NonceStore` in `src/auth.rs` with 5-minute TTL), and the sr25519 signature must verify against the hotkey's public key using the Substrate signing context. Only requests passing all checks can submit batches via `POST /submit`. All other endpoints are open.

src/AGENTS.md

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ This is a single-crate binary. All source files live in `src/` with no sub-modul
77
```
88
main.rs
99
├── config.rs (Config::from_env, AUTHORIZED_HOTKEY)
10+
├── auth.rs (NonceStore created in main, reaper_loop spawned from main)
1011
├── handlers.rs (Axum router + AppState)
11-
│ ├── auth.rs (hotkey verification for /submit)
12+
│ ├── auth.rs (extract_auth_headers + verify_request for /submit)
1213
│ ├── executor.rs (spawned from submit handler)
1314
│ │ ├── task.rs (extract, parse, load tasks)
1415
│ │ ├── session.rs (BatchResult/TaskResult mutation)
@@ -24,7 +25,7 @@ main.rs
2425

2526
### `main.rs`
2627
- Entry point. Initializes tracing, config, session manager, metrics, executor.
27-
- Creates `AppState`, builds Axum router, spawns background tasks (session reaper, stale dir reaper).
28+
- Creates `AppState`, builds Axum router, spawns background tasks (session reaper, nonce reaper, stale dir reaper).
2829
- Binds to `0.0.0.0:{PORT}` with graceful shutdown on CTRL+C.
2930
- **Convention**: Background tasks are spawned with `tokio::spawn` and run indefinitely.
3031

@@ -36,18 +37,22 @@ main.rs
3637
- **Convention**: Add new config fields here, with a `DEFAULT_*` constant and an env var name. Always provide a sensible default.
3738

3839
### `handlers.rs`
39-
- Defines `AppState` struct (`config`, `sessions`, `metrics`, `executor`, `started_at`).
40+
- Defines `AppState` struct (`config`, `sessions`, `metrics`, `executor`, `nonce_store`, `started_at`).
4041
- `router()` builds the Axum `Router` with all routes and shared state.
4142
- Route handlers: `health`, `status`, `metrics`, `submit_batch`, `get_batch`, `get_batch_tasks`, `get_task`, `list_batches`.
4243
- Routes: `GET /health`, `GET /status`, `GET /metrics`, `POST /submit`, `GET /batch/{id}`, `GET /batch/{id}/tasks`, `GET /batch/{id}/task/{task_id}`, `GET /batches`, `GET /ws`.
43-
- `submit_batch` handler does: hotkey auth → busy check → multipart upload → archive extraction → batch creation → executor spawn.
44+
- `submit_batch` handler does: auth header extraction → `verify_request` (hotkey + API key + nonce + signature) → busy check → multipart upload → archive extraction → batch creation → executor spawn.
4445
- **Convention**: Return `Result<impl IntoResponse, (StatusCode, Json<Value>)>` from handlers that can fail. Use `Json(serde_json::json!({...}))` for responses.
4546

4647
### `auth.rs`
47-
- `verify_hotkey(hotkey)` — compares submitted hotkey against `AUTHORIZED_HOTKEY`.
48-
- `extract_hotkey(headers)` — reads `X-Hotkey` header from request.
48+
- `NonceStore``DashMap`-backed nonce tracker with 5-minute TTL and background reaper loop for replay protection.
49+
- `AuthHeaders` — struct holding `hotkey`, `nonce`, `signature`, `api_key` extracted from request headers.
50+
- `extract_auth_headers(headers)` — reads `X-Hotkey`, `X-Nonce`, `X-Signature`, `X-Api-Key` headers from request.
51+
- `verify_request(auth, nonce_store, expected_api_key)` — full auth pipeline: hotkey match → API key match → SS58 validation → nonce replay check → sr25519 signature verification.
4952
- `validate_ss58(address)` — validates SS58 address format using `bs58`.
50-
- **Convention**: Auth is mandatory — `POST /submit` requires a valid `X-Hotkey` header matching the authorized hotkey.
53+
- `verify_sr25519_signature(ss58_hotkey, message, signature_hex)` — verifies an sr25519 signature using `schnorrkel` with the Substrate signing context.
54+
- `AuthError` — enum with `UnauthorizedHotkey`, `InvalidHotkey`, `InvalidApiKey`, `NonceReused`, `InvalidSignature` variants, each with `.code()` and `.message()` methods.
55+
- **Convention**: Auth is mandatory — `POST /submit` requires all four headers (`X-Hotkey`, `X-Nonce`, `X-Signature`, `X-Api-Key`). The signed message is `hotkey + nonce`.
5156

5257
### `executor.rs`
5358
- `Executor::spawn_batch(batch, archive, concurrent_limit)` — spawns a tokio task that runs all tasks in the batch.
@@ -63,7 +68,9 @@ main.rs
6368
- `TaskTestResult`, `TaskResult`, `BatchResult` — core result data types.
6469
- `WsEvent` — WebSocket event struct with `event`, `batch_id`, `task_id`, `data`.
6570
- `Batch` — holds id, created_at, result (`Arc<Mutex<BatchResult>>`), events_tx (`broadcast::Sender<WsEvent>`), cancel channel.
66-
- `SessionManager``DashMap`-backed batch store with create/get/list/has_active_batch/mark operations.
71+
- `SessionStats` — atomic counters for created/active/completed/failed batches.
72+
- `BatchSummary` — lightweight struct for `list_batches()` output.
73+
- `SessionManager``DashMap`-backed batch store with `SessionStats`, create/get/list/has_active_batch/mark_completed/mark_failed operations.
6774
- `reaper_loop()` — runs every 60s, removes batches older than TTL, sends cancel signal.
6875
- **Convention**: All enums use `#[serde(rename_all = "snake_case")]`. Batch IDs are UUID v4 strings.
6976

0 commit comments

Comments
 (0)