summaryrefslogtreecommitdiff
path: root/docs/backend-repair-contract.md
blob: 2b02703a2976bf1ee489438267e186aeee95c844 (plain)
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
# Backend repair contract (mandatory for all repair workers)

Prior broad edits introduced cascading signature errors. Parent stopped the old backend process. Repair in narrow file ownership; other workers may be editing their own files. Do NOT edit outside your allocation. Do not weaken tests, delete API handlers, or change language/framework.

## Fixed signatures
- state.rs exports `pub type Cx = axum::extract::State<std::sync::Arc<AppState>>;`
- AppState owns cfg:Config, db:tokio::sync::Mutex<rusqlite::Connection>, run_sem/fetch_sem Arc<Semaphore> (preserve other needed state).
- `impl AppState { pub async fn with_db<R>(&self, f: impl FnOnce(&mut rusqlite::Connection) -> R) -> R { let mut db = self.db.lock().await; f(&mut db) } }`.
- This returns exactly R after await. If closure returns Result<T,E>, use `.await?`; if closure returns number/unit, `.await` only. Never await inside database closure. Keep transactions and atomic operations inside ONE closure. No nested locks.
- Handler argument is `cx: Cx` (State extractor), not a bare Arc alias. Helper taking `&Cx` receives `&cx`. Background jobs may use Arc and create `State(cx.clone())` only where necessary. All handlers futures must be Send.
- util::now_iso() -> String is SYNCHRONOUS. hash/random token helpers synchronous unless they actually await.
- error::AppError supports new(StatusCode,code,message), bad(code,message), unauthorized(message), forbidden(message), not_found(message), conflict(code,message), internal(message), with_details(Value), with_code(code). One inherent impl, one IntoResponse, one From<rusqlite::Error>, one From<std::io::Error>, one From<serde_json::Error>. Never duplicate From impl elsewhere.
- `auth::make_admin_token` existing admin call sites need coherent async Result signature; auth/admin worker owns both.
- Router Axum0.8 paths use `{id}`, NEVER `:id` (runtime panic).
- Do not change actual JSON HTTP contract: SPEC.md + RELEASE_SCOPE.md authoritative.

## Worker partition
CORE: config.rs state.rs error.rs util.rs main.rs store.rs worker.rs Cargo.toml (+ core tests). Main integrates call signatures but not other module sources.
AUTH: auth.rs admin.rs (+ auth tests inside module).
DOMAIN: projects.rs datasets.rs runs.rs ai.rs jobs.rs (+ domain tests inside module).
Read other files freely. Compile often, filter diagnostics for OWN modules; leave other-worker diagnostics alone. Final shared compile gate performed by parent.

## Parent findings to address
- main.rs origin substring match is exploitable. Compare exact canonical origin (scheme+host+port), reject `https://allowed.example.evil.invalid`. Empty canonical allows exact local origin only. No trust of X-Forwarded host.
- Serve frontend/dist static SPA with index.html fallback for UI only; /api unknown returns JSON 404 (not SPA).
- Instrument search current stub must connect to actual worker catalog command; no fake successful empty list. Docker worker fetch/search with bounded time and resource.
- Concurrent fetch duplicate cache key must serialize/recheck. request/cache key and manifest hash are distinct columns/concepts.
- All raw+normalized referenced artifacts retained; worker manifest fields must be checked against live worker code.
- Read-only nonroot container UID65534 needs writable output (parent job dir0700, output mode0777 inside; no secrets). Input/data are read-only. Stdout/stderr bounded and drained; timeout/cancel container cleanup real.
- Finished run state only succeeded if worker JSON status says succeeded. Failed/cancelled state must not be overwritten by racing completion.
- Durable queued jobs recheck account active, dataset ownership/readiness, per-user budget.
- Invitation email binding, one-time transaction, no role escalation; disable/reset revoke sessions; last admin protection.

Parent independent API tests live at scripts/qa_api.py, including substring CSRF and admin/private owner separation. READ these gates and make them genuinely pass. Parent prepares isolated QA environment, do not read any secrets outside project.