Integration prompt — ahu-ocr-tidyup: fill X-User-Id so verifier actions attribute per-person
Paste into a Claude Code session inside the
ahu-ocr-tidyuprepo. Follow-up toprompt-ahu-ocr-tidyup.md(thegateway-headers.tshelper +getRequestContextthreading are DONE). This prompt only sharpens identity attribution in the audit trail. No gateway change is required.
Why (read first)
The observatory + dashboard now run a per-actor security layer (egress outliers, error/probing rate, volume outliers, off-hours activity, bulk body-reading), where an actor is engine / surface / user_id. Today OCR traffic shows up as ahu-ocr/internal/unattributed — hundreds of calls, no person attached. prompt-ahu-ocr-tidyup.md §1 deliberately scoped X-User-Id to "the verifier/operator id when a request is user-triggered; omit for autonomous pipeline stages." That was the right call, but two things now need tightening so the security view can name a person:
- User-triggered verifier actions must actually carry the verifier's id. The verifier workspace has an authenticated operator on every user-initiated request (upload, re-run extraction, approve/reject, e-voting,
cek-bukti, summarize). Those are exactly the calls where attribution matters — a verifier bulk-pulling documents or hammering extraction should resolve to that verifier, not tounattributed. - Autonomous pipeline stages should be labeled as a system actor, not left blank — so "unattributed" shrinks to genuinely nothing rather than "could be a person or a cron, we can't tell." A background stage should send
X-User-Id: system:<pipeline-stage>(e.g.system:akta-extract) so the security view can cleanly separate machine work from human work.
No gateway change needed: the gateway already reads X-User-Id on /v1, the Job API, and every façade and writes it into user_id. This is client-side + dormant-safe (unset MODEL_GATEWAY_URL → current upstreams ignore the header).
Scope
1. Thread the authenticated verifier id into the header helper
backend/src/lib/gateway-headers.ts already assembles the header set from a context object, and getRequestContext (in backend/src/index.ts) already carries per-request state. Add the authenticated operator/verifier id to that context (from the existing auth/session middleware — find where the request's user is resolved) and have the helper emit it as X-User-Id for every user-triggered call: the OCR job submit, Azure DI, PaddleOCR, classifier, /summarize, and /cek-bukti egress points from §2–§4 of the base prompt.
- Use the stable internal user id, never PII (no name/email/NIK — it lands in the audit trail).
- Keep
X-Surface: internal(this engine is the verifier workspace).
2. Label autonomous stages as a system actor
For pipeline stages that run without a user in the request context (background extraction, queued re-processing), set X-User-Id: system:<stage> instead of omitting it. Pick short stable stage slugs (e.g. system:akta-extract, system:classify, system:reprocess). This makes machine traffic a first-class, filterable actor rather than blank.
- The distinction is: is there an authenticated human on this request? Human → their id. No human →
system:<stage>. Never random-per-call.
3. Keep trace + doc identity intact
Do not regress the X-Request-Id trace propagation or the doc_hash/pages headers from prompt-ahu-ocr-tidyup.md §5 — the security view's drill-down (actor → their calls → the document bodies they touched) depends on trace + doc identity lining up with the now-populated user_id.
Dormancy + verification
- Dormancy proof: with no new env set, request URLs, bodies, and headers minus the additive
X-*are unchanged against today's upstreams. - Tests: extend the header-helper unit tests to assert (a) a user-triggered context emits the verifier's
X-User-Id; (b) a no-user pipeline context emitssystem:<stage>, not blank and not random; (c) the id is stable across the multiple calls of one document's flow. - Post-flip check (once the OCR/Job adapters are live — Phase A/B): process one document as an authenticated verifier, then in the observatory Security view confirm the actor resolves to
ahu-ocr/internal/<verifier-id>for the human-triggered calls andahu-ocr/internal/system:<stage>for the background ones — and that the trace ties them to the samedoc_hash.
Report back
Files changed; where the authenticated verifier id is resolved and how it reached the helper; the list of system:<stage> slugs chosen; confirmation that trace + doc_hash still line up; and anything in the identity contract that needed interpretation.