summaryrefslogtreecommitdiff
path: root/docs/backend-repair-contract.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/backend-repair-contract.md')
-rw-r--r--docs/backend-repair-contract.md34
1 files changed, 34 insertions, 0 deletions
diff --git a/docs/backend-repair-contract.md b/docs/backend-repair-contract.md
new file mode 100644
index 0000000..2b02703
--- /dev/null
+++ b/docs/backend-repair-contract.md
@@ -0,0 +1,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.