Files
0pt/docs/guides/document-workbench.md
admin dade37ee05
CI (SIAX Cloud) / sonar (pull_request) Skipped
masterplan-lock / check (pull_request) Successful in 4s
masterplan-lock / check (push) Successful in 5s
CI (SIAX Cloud) / security (pull_request) Successful in 16s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 16s
CI (SIAX Cloud) / integration (pull_request) Successful in 9m58s
CI (SIAX Cloud) / quality (pull_request) Successful in 10m25s
feat(credits): one-time trial grant so the ledger runs in production
- Ledger accepts kind='grant' (a server-owned balance grant, not a payment).
- OPT_TRIAL_CREDITS=N grants N credits once per tenant (idempotent key
  trial:<tenant>) on balance/structure, so reserve -> settle is exercised
  before any customer purchase.
- sql/020 extends the kind constraint (re-runnable).
- Tests for the grant helper; full suite 1345 passed.
2026-11-02 22:05:06 +01:00

4.8 KiB

Document workbench — upload → structure → validated export

The customer-facing slice that turns an uploaded file into evidence-bound data.

Routes

Route Purpose Fails closed with
POST /api/0pt/documents/upload multipart upload of the original bytes into DOC0, recorded in a durable attempt journal before dispatch 503 DOC0_NOT_CONFIGURED, 503 PROJECT_AUTHORIZATION_NOT_CONFIGURED, 503 UPLOAD_JOURNAL_NOT_CONFIGURED
POST /api/0pt/documents/structure re-reads the DOC0 segment index, calls INF0, validates every field against an exact source span, reserves/settles credits 503 WORKBENCH_NOT_CONFIGURED, 503 MODEL_POLICY_NOT_CONFIGURED, 503 CREDITS_NOT_CONFIGURED, 402 INSUFFICIENT_CREDITS
GET /api/0pt/documents/models the server-approved model routes and limits 503 MODEL_POLICY_NOT_CONFIGURED
GET /api/0pt/credits/balance tenant credit balance 503 CREDITS_NOT_CONFIGURED
GET /api/0pt/credits/packs sellable packs from the server-owned price book 503 PRICE_BOOK_NOT_CONFIGURED
POST /api/0pt/credits/checkout Stripe Checkout session + server price snapshot 503 PRICE_BOOK_NOT_CONFIGURED, 503 STRIPE_NOT_CONFIGURED
POST /api/webhooks/stripe re-reads the session and payment intent, then grants credits idempotently non-2xx until processed

UI: /portal/dokument (upload, structure, review, download) and /portal/krediter (balance, packs, checkout). The portal signs in through Zitadel OIDC (/api/auth/login → /api/auth/callback); the resulting signed session cookie is accepted by middleware as a verified principal (Bearer still wins).

Invariants

  1. Tenant and actor come only from middleware's verified headers. A customer route never trusts tenant_id/actor_id from the body.
  2. Project access is checked by OpenFGA (@siax/access), the same primitive DOC0 uses. Until the central relationship store is deployed, an explicit server-owned grant list (OPT_PROJECT_GRANTS) can be used instead. Unconfigured or unavailable means denied, never allowed.
  3. The model never sees unverified text. Doc0SourceClient verifies version, tenant, checksum, segment hashes and that the index was not truncated before anything is sent to INF0.
  4. Every returned value must point at an exact span in the source; otherwise the result is needs_review and nothing is exported.
  5. Credits are reserved before the model runs and settled to measured usage. An unknown model outcome keeps the reservation for reconciliation instead of releasing it.
  6. No price is ever read from the client. The price book is a versioned, server-owned JSON document (OPT_CREDIT_PRICE_BOOK).
  7. Trial credits are a grant, not a payment. OPT_TRIAL_CREDITS=N grants N credits once per tenant (kind='grant', idempotency key trial:<tenant>), so the ledger is fully exercised before any real purchase. It is never read from a client and never looks like a Stripe payment.
  8. Pilot mode is explicit. OPT_CREDITS_MODE=off skips reserve/settle and reports credits: null. It is not a silent fallback and it does not enable purchases; unset/on requires the price book and the ledger.

Reusable packages

  • packages/siax-access (@siax/access) — framework-neutral OpenFGA relationship checks and a fail-closed isProjectAccessAllowed. Intended for DOC0, N0D and every other SIAX product instead of per-repo copies of project-relationships.ts.
  • src/document-workbench/* — upload coordination, DOC0 source reading, INF0 admission, structured-output validation, export and credit ledger. These modules have no Next.js imports and can be lifted into a published package unchanged.

Which AI path is used

Order:

  1. Decision server model (DECISION_OLLAMA_URL + DECISION_OLLAMA_MODEL) — the self-hosted Ollama on server-decision-01, used while the external model API is on hold. Internal only, no provider key, no per-token cost.
  2. Live LiteLLM gateway (INF0_GATEWAY_LITELLM_KEY).
  3. Native INF0_URL surface.

Admission — tenant/project scope, model allow-list, data class, external-transfer policy and token limits — runs locally in every path before the request leaves the process. Evidence spans are computed deterministically from the returned values when a small local model omits offsets: a value must occur exactly once in the source, otherwise the result is needs_review.

What is still deliberately not claimed

  • The output is mechanically_validated, not proven complete or semantically correct. OCR quality and coverage require review.
  • Refunds and chargebacks do not automatically revoke credits; no such handler is wired.
  • Format breadth is limited by DOC0's parser. A file DOC0 cannot parse is reported, never guessed.