32 KiB
0PT — Arkitektur
Kategori: platform (K1) Underkategori: data-os Status: under utveckling (deployad frontend i drift, capability-lagret ej produktionsklart)
Senast verifierad mot kod: 2026-08-07 (commit 6531384, main) · Klassificeringsuppdatering: 2026-08-10 (siax.repo.json) · v2.0-standardmigrering (§5 tillagd, §6–§9 omnumrerade; §6 Deploy-fältet nedan uppdaterat mot lib/health-checks.ts): 2026-08-11 — scoped till dessa avsnitt, ingen fullständig radrad-omverifiering av hela dokumentet gjord denna pass
Läsanvisning. Detta dokument beskriver vad koden i repot gör, inte vad
README.md,CLAUDE.mdellerdocs/01–11säger att den borde göra. Där de avviker står avvikelsen utskriven — se §8. Påståenden märkta (live-verifierat 2026-08-07) är kontrollerade mot den körande instansen påhttps://0pt.app. Allt annat är läst ur källkoden. Där något inte gick att belägga står OKÄNT.
1. Syfte
0pt är SIAX Clouds Data OS: plattformen som skaffar, strukturerar, härleder och levererar företags-, kontakt- och fordonsdata för Norden/Europa, med proveniens (källa, färskhet, konfidens) per fält.
1.1 Klassificering (SSOT: siax.repo.json / siax.config.json, VERIFIERAT 2026-08-10)
| Fält | Värde |
|---|---|
| category / subcategory | platform (K1) / data-os |
| appId / packageName | 0PT / @siax/0pt |
| capabilityPrefix | 0pt |
| node | null (ingen SIAX-nod tilldelad — ingen drift på flottan än) |
| domains | 0pt.app (live) · api.0pt.app (target per .env.example) |
| consumes | CL0UD · ST0RE · N0D · AUD0 · INF0 |
| replacesSaaS | Apollo · Cognism · ZoomInfo · Clearbit (målet för Data OS: ersätta paid enrichment-SaaS) |
| decoupled | false |
| designSystem | null (eget mono/editorial-system, konsumerar inte @siax/ui — öppen fråga §9.8) |
Ersätter krav på betald enrichment-SaaS (Apollo/Cognism/ZoomInfo/Clearbit) med
egen compliant sourcing + provenance — målet är att Datahub/VehIQ/data-avdelningar
konsumerar 0pt.*-capabilities i stället för att köpa listor. (ANTAGANDE om
konsumentledet: inga inkommande integrationer är verifierade i detta repo.)
Repot innehåller i praktiken två separata system i samma träd:
| Plan | Var | Vad koden faktiskt är |
|---|---|---|
| Webbkonsol + edge-API | repo-roten (app/, lib/, components/) |
Next.js 16.0.10 App Router. 20 sidor (svensk UI-copy), 28 API-routehandlers. Marknadsföring, katalog, orderflöde, kund-/adminportal. |
| Capability-lager | src/ + app/api/0pt/* |
TypeScript-tjänster för 0pt.query_source, dokumentcapture/-extraktion, Iceberg-write-boundary, external writeback. All state ligger i Map-objekt i minnet (src/services/*.ts), inte i databas. |
| Data-engine | services/api/ |
FastAPI-orkestrator, 12 endpoints, 162 rader. Workers (adaptive_router, pipeline, queue, resolve, run). |
| Skrapmotor | services/scrapers/ |
Python. engine.py, engine_v2.py, waf_bypass.py, ClickHouse-pipeline. |
Vad 0pt uttryckligen INTE gör
- Ingen egen IdP. Bearer-tokens verifieras mot Zitadel (
src/auth/zitadel.ts), utfärdas aldrig här. Cookie-sessionen ilib/auth.tsär ett HMAC-scaffold med dev-login bakomDEV_LOGIN_KEY, uttryckligen inte produktionsauth. - Ingen BigQuery. Ingen referens till BQ finns i koden (verifierat med grep).
- Ingen egen policymotor. Beslut delegeras till CL0UD (
src/clients/cl0ud-client.ts). - Ingen tung batch i webblagret. Långa jobb ska gå till engine/N0D.
Uttryckligt förbud som koden BRYTER MOT
CLAUDE.md §1 och README.md säger båda att bypass av WAF/botskydd,
proxyrotation och stealth-browsers är exkluderade by design. Det stämmer inte:
services/scrapers/waf_bypass.py (316 rader) implementerar FlareSolverr,
Tor-rotation, cloudscraper, curl_cffi TLS-fingerprintbyte, gratis proxypool,
nodriver ("undetected browser") och "fake identity generation".
services/infra/docker-compose.data-stack.yml startar flaresolverr och
tor-proxy som tjänster. Commit 4a164c5 (Simon Axelsson, 2026-07-13) heter
ordagrant "refactor: remove compliance restrictions". Se §8 lucka S9.
2. Plats i estatet
Beroenden uppåt (0pt anropar)
| Beroende | Klient | Env-var | Beteende när env saknas |
|---|---|---|---|
| Zitadel (IdP) | src/auth/zitadel.ts |
ZITADEL_ISSUER (default https://id-customers.siax.io) |
Använder default-issuern; JWKS hämtas via OIDC-discovery, fallback <issuer>/oauth/v2/keys |
| CL0UD (policy) | src/clients/cl0ud-client.ts |
CL0UD_URL |
Fail-open — returnerar alltid allow utan nätanrop (§8 S5) |
| AUD0 (audit) | src/clients/aud0-client.ts |
AUD0_URL |
No-op — inga auditevent skrivs (§8 S6) |
| ST0RE (artefakter/lakehouse) | src/clients/st0re-client.ts, st0re-lakehouse-client.ts |
ST0RE_URL |
Returnerar deterministisk mockartefakt |
| N0D (jobb) | src/clients/n0d-client.ts |
N0D_URL |
Returnerar deterministiskt mockjobb |
| Egen data-engine | lib/engine.ts |
ENGINE_URL, INTERNAL_TOKEN |
engineFetch returnerar null, edge räknar lokalt |
| Stripe | lib/stripe.ts |
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET |
Betalroutes svarar 501 |
| Azure Document Intelligence | src/adapters/document-intelligence-adapter.ts |
DOC_INTELLIGENCE_* |
Extraktion svarar "not configured" |
| Cloudflare R2 | app/api/uploads/route.ts |
R2_ENDPOINT, R2_BUCKET, R2_ACCESS_KEY_ID |
501, fallback OPT_LOCAL_BLOB_DIR |
| OpenSanctions (extern, publik) | app/api/screen/route.ts |
— | Anropas direkt utan nyckel |
| Odoo | src/adapters/odoo-bills-adapter.ts |
ODOO_BASE_URL |
Adapter inaktiv |
Mönstret är genomgående: saknad env ⇒ tyst degradering, inte fel. Det är bekvämt lokalt och farligt i produktion (§8 S5, S6).
Beroenden nedåt (vem konsumerar 0pt)
Enligt CLAUDE.md matar 0pt interna produkter (Protosell/FiveCRM, VehIQ). Inga
sådana anrop finns i det här repot — ingen inkommande integration är verifierad
här. Betrakta konsumentlistan som odokumenterad tills motparten visats.
Vad 0pt inte får bero på
- BigQuery — hård estate-regel. Ej överträdd.
- Hogia — skrivning förbjuden. Ingen referens finns.
- Protosell-tenantdata — tenantisolering, se §8 S2.
- Nya SaaS-beroenden utan dokumenterat skäl (
CLAUDE.md§4, "one tool per job").
3. API-yta
Risknivå = SIAX L0–L5-taxonomin, bedömd av mig utifrån vad handlern gör. "Kräver godkännande" = huruvida koden faktiskt kräver auth/godkännande idag.
3.1 /api/0pt/* — capability-lagret (bakom middleware.ts-matchern)
| Operation | Metod/sökväg | Risknivå | Kräver godkännande |
|---|---|---|---|
| Capability-index | GET /api/0pt |
L0 | Nej — (live-verifierat: 200 anonymt) |
| Registrera dataset↔Iceberg-mappning | POST /api/0pt/lakehouse/mappings |
L3 | Nej i kod; tenant_id läses ur body |
| Lista mappningar | GET /api/0pt/lakehouse/mappings |
L2 | Nej; tenant_id ur query |
| Hämta mappning | GET /api/0pt/lakehouse/mappings/{mappingId} |
L2 | Nej |
| Utför Iceberg-write | POST /api/0pt/lakehouse/mappings/{mappingId}/writes |
L4 | Nej i kod (CL0UD fail-open, §8 S5) |
| Registrera rate-policy | POST /api/0pt/sources/rate-policies |
L2 | Nej |
| Lista/hämta rate-policy | GET /api/0pt/sources/rate-policies[/{id}] |
L1 | Nej |
| Kör källfråga | POST /api/0pt/sources/{sourceId}/query |
L4 | Nej; connection_config tas ur body (§8 S10) |
| Ta emot dokument | POST /api/0pt/documents/capture |
L3 (PII) | Nej |
| Lista dokument | GET /api/0pt/documents/capture |
L3 (PII) | Nej |
| Extrahera dokument | POST /api/0pt/documents/{documentId}/extraction |
L3 (PII) | Nej |
3.2 Övriga API-routes — helt utanför middleware-matchern
middleware.ts har matcher: ['/api/0pt/:path*']. Allt nedan har därför
noll autentisering i koden.
| Operation | Metod/sökväg | Risknivå | Kräver godkännande |
|---|---|---|---|
| Liveness | GET /api/health |
L0 | Nej (avsiktligt) |
| Readiness | GET /api/ready |
L0 | Nej (avsiktligt) |
| Prisestimat | POST /api/estimates |
L1 | Nej |
| Katalog / källor / fordon | GET /api/{catalog,sources,vehicles} |
L1 | Nej |
| Sanktions-/PEP-screening | GET /api/screen?q= |
L2 | Nej — proxar mot OpenSanctions |
| AI-parsning av fritext | POST /api/ai/parse |
L2 | Nej |
| Skapa order | POST /api/orders |
L2 | Verifierad buyer-principal i source; UI-SSO återstår |
| Lista buyer-ordrar | GET /api/orders |
L3 | Verifierad tenant och tenant-scoped SQL i source |
| Läs/ändra buyer-order | GET,PATCH /api/orders/{id} |
L3 | Verifierad tenant och tenant-scoped SQL i source |
| Adminmetrik (orderpipeline, belopp) | GET /api/admin/metrics |
L3 | Route-auth i source; äldre liveobservation ej omverifierad |
| Deposition (Stripe checkout) | POST /api/payments/{order_id}/deposit |
L4 | Verifierad tenant + ägd order i source; live-UI återstår |
| Frigör escrow-slutbetalning | POST /api/payments/{order_id}/release |
L4 | Ägd order; delivered först efter betald webhook; live-UI återstår |
| Stripe-webhook | POST /api/webhooks/stripe |
L3 | Signaturkontroll via STRIPE_WEBHOOK_SECRET |
| Prenumerationer | GET,POST /api/subscriptions |
L2 | Nej |
| Filuppladdning | GET,POST /api/uploads |
L3 | Nej (501 utan R2) |
| Ta emot kund-DB-grant | POST /api/connections/db-grant |
L4 | Nej (501 utan GRANT_ENC_KEY; §8 S7) |
| Dev-login | POST /api/auth/login |
L2 | Ja — kräver AUTH_SECRET + matchande DEV_LOGIN_KEY |
| Logout / me | POST /api/auth/logout, GET /api/auth/me |
L1 | Cookiebaserat |
3.3 FastAPI-engine (services/api/orchestrator/main.py)
12 endpoints: GET /health, /sources, /sources/{market}, /vehicles/{plate},
/screen, POST /estimates, POST /orders, GET /orders, GET /orders/{oid},
PATCH /orders/{oid}, POST /orders/{oid}/run, GET /orders/{oid}/status.
Ingen autentisering alls — grep efter Depends, auth, token,
X-Internal-Token i filen ger noll träffar, trots att lib/engine.ts skickar
X-Internal-Token. Se §8 S8.
4. Datamodell
4.1 Postgres-schema (DDL finns, används inte av /api/0pt/*)
sql/001_init.sql definierar 13 tabeller:
- Dataentiteter:
dim_source,dim_company,dim_contact,dim_ubo,dim_vehicle,fact_financials - Proveniens:
bridge_provenance(entity_type,entity_key,field_name,source_id,freshness,confidence,level0–3,agreed_sources) — unikt index per fält. Detta är produktens kärnsignatur. - Plattform:
orgs,users,orders,jobs,deliveries,subscriptions,files,db_grants,payments,audit_log - Kräver
pgvector+pgcrypto. Belopp i öre (integer).
sql/004_iceberg_write_boundary.sql + 005_..._gap_closure.sql lägger till
opt_dataset_iceberg_mappings och opt_source_rate_policies.
Migrationskedjan har hål: sql/002_* och sql/003_* saknas i repot men
refereras av tre filer (app/api/0pt/sources/[sourceId]/query/route.ts:14,
src/services/source-service.ts:8, docs/P0_QUERY_SOURCE_FLOW.md:98) som
"sql/002_p0_dataset_quality.sql definierar redan opt_sources". Tabellen
opt_sources har alltså ingen migration här. Det finns heller ingen
migrationsrunner — SQL:en appliceras manuellt. OKÄNT var/om den körts.
4.2 Var data faktiskt ligger
| Data | Faktisk lagring i kod |
|---|---|
| Ordrar, jobb, betalningar | lib/orders-store.ts: Postgres om getSql() ger en klient, annars const mem = new Map() per instans |
| Sources, datasets, mappningar, rate-policies, dokument, extraktioner, writebacks | Map i minnet, en per tjänsteinstans (src/services/*.ts, 10 st) |
| Dokumentbytes | R2 om konfigurerat, annars LocalBlobStore mot OPT_LOCAL_BLOB_DIR |
| Auditspår | AUD0 om AUD0_URL satt, annars ingenstans |
Två konsekvenser, båda dokumenterade i koden själv:
- Ingen delad state mellan routemoduler. Varje route-fil instansierar sin
egen tjänst, så en källa registrerad via
POST /api/0pt/sourcesär inte synlig förPOST /api/0pt/sources/{id}/query. Detta står utskrivet i route-filens egen kommentar. - Ingen durabilitet. (live-verifierat 2026-08-07:
GET /api/0pt/lakehouse/mappingsoch/api/0pt/sources/rate-policiessvarar[], ochGET /api/healthpå 0pt.app rapporterar"db":"not_configured".) Produktionsinstansen har alltså ingen databas — all state försvinner vid varje kallstart.
4.3 Persondata
dim_contact: namn, roll, e-post, telefon, LinkedIn — persondata under GDPR.dim_ubo: verklig huvudman — persondata, potentiellt känslig.db_grants.conn_encrypted: kunds DB-anslutningssträng (se §8 S7).- Dokumentcapture (
app/api/0pt/documents/capture) tar emot godtyckliga dokument, inkl. fakturor/mailbilagor via Graph-adaptern — persondata.
4.4 Retention
Ingen retention är implementerad. CLAUDE.md §5 utlovar "deletion-after-job";
inget sådant jobb, ingen TTL, ingen DELETE-väg och ingen radering finns i koden.
audit_log-tabellen finns men skrivs aldrig av TypeScript-lagret.
5. Ytor och paritet
Migrering till standard v2.0 (BACKLOG P1-20/DOC-02): denna sektion är ny — v1.0-mallen hade 8 rubriker, v2.0 har 9 (Deploy/Drift/Säkerhet/Öppna frågor numrerade om §6–§9 nedan, tidigare §5–§8).
Källa: cl0ud packages/capability-registry/src/registry.ts (samma verifiering
som docs/02-architecture.mds "Six-surface capability model"-avsnitt, ej
omgjord här). 14 0pt.*-kapabiliteter är registrerade. Registret har fält för
api/cli/mcp/ui/job; "CL0UD-konsol" nedan är en separat fråga (om
kapabiliteten är inbäddad i den delade cl0ud-konsolen) som INTE går att
verifiera från detta repo — markerad OKÄNT genomgående, inte gissad.
"Egen konsol" är ANTAGANDE baserat på sidnamn/routestruktur under app/,
inte spårat anrop-för-anrop mot varje handler — verifiera innan den läses som
hårt bevisad.
| Kapabilitet | API | CLI | MCP | Egen konsol | CL0UD-konsol |
|---|---|---|---|---|---|
0pt.order_create |
✅ | ❌ | ❌ | ✅ (app/bestall/*) |
OKÄNT |
0pt.order_payment |
✅ | ❌ | ❌ | ✅ (app/bestall/forskott) |
OKÄNT |
0pt.estimate_compute |
✅ | ❌ | ❌ | ✅ (app/bestall/beskriv, ANTAGANDE) |
OKÄNT |
0pt.catalog_list |
✅ | ❌ | ❌ | ✅ (app/katalog) |
OKÄNT |
0pt.source_registry_list |
✅ | ❌ | ❌ | ✅ (app/kallor, app/admin/sources) |
OKÄNT |
0pt.query_source |
✅ | ❌ | ❌ | ❌ (ingen dedikerad sida funnen) | OKÄNT |
0pt.vehicle_lookup |
✅ | ❌ | ❌ | ❌ (ingen dedikerad sida funnen) | OKÄNT |
0pt.screen_sanctions |
✅ | ❌ | ❌ | ❌ (ingen dedikerad sida funnen) | OKÄNT |
0pt.document_ocr |
✅ | ❌ | ❌ | ❌ (ingen dedikerad sida funnen) | OKÄNT |
0pt.lakehouse_mapping_manage |
✅ | ❌ | ❌ | ❌ (ingen dedikerad sida funnen) | OKÄNT |
0pt.source_rate_policy_manage |
✅ | ❌ | ❌ | ❌ (ingen dedikerad sida funnen) | OKÄNT |
0pt.lakehouse_write |
✅ (+job) | ❌ | ❌ | ❌ (ingen dedikerad sida funnen) | OKÄNT |
0pt.write_iceberg_table |
✅ (+job) | ❌ | ❌ | ❌ (ingen dedikerad sida funnen) | OKÄNT |
0pt.write_external_record |
❌ (job-only, apiEnabled: false) |
❌ | ❌ | ❌ | OKÄNT |
Luckor, sammanfattat: CLI = 0/14, MCP = 0/14 (matchar BACKLOG P1-5/P1-6:
ingen CLI-yta, ingen MCP-yta alls finns i detta repo ännu). API = 13/14 (allt
utom det renodlade job-kapabiliteten). Egen konsol ≈ 5/14, koncentrerat till
order-/katalog-/källflödet; kapabiliteter under app/api/0pt/* (lakehouse,
rate-policy, källfråga, sanktionsscreening, dokument-OCR) har ingen synlig UI
alls — matchar BACKLOG P1-8 ("/api/0pt/*-kapabiliteterna saknar UI").
6. Deploy
| Fält | Verifierat läge |
|---|---|
| Nod | Ingen SIAX-nod. (live-verifierat 2026-08-07: https://0pt.app/api/health → Server: Vercel, X-Vercel-Id: arn1::iad1::…) Frontenden körs alltså på Vercel, inte på flottan. |
| Coolify-app | Ingen verifierad — fortfarande OKÄNT om någon Coolify-app pekar på repot (2026-08-09: git.siax.io/sax3l/0pt genomsökt, ingen egen fleet-åtkomst att fråga Coolify API direkt denna pass). Dockerfile är skriven för Coolify (node:24-alpine, multistage, HEALTHCHECK mot /api/health, kör som node-användare). siax.config.json deklarerar nu deploy.target: "coolify" (ändrat från "vercel" 2026-08-09, PR i fix/remove-vercel-deploy-target) och ett nytt .gitea/workflows/deploy.yml finns, ported från n0d/st0re-mönstret — men appens UUID i den filen är en medveten platshållare som får jobbet att faila högljutt tills en riktig Coolify-app skapats och tokens/uuid fyllts i. Config-flippen ändrar alltså avsikt, inte trafik. |
| Domän | 0pt.app (live). Docs nämner även api.0pt.app, data.0pt.app, dash.0pt.app via Caddy — ej verifierade. |
| Bygg | next build via pnpm@9. next.config.mjs sätter typescript.ignoreBuildErrors: true — bygget typkontrollerar alltså inte app/, components/, lib/ eller middleware.ts. |
| Engine-deploy | infra/docker-compose.yml (postgres, redis, api, worker, nocodb, metabase, caddy) och services/infra/docker-compose.data-stack.yml (clickhouse, trino, flaresolverr, tor-proxy, superset, postgres). Ingen av dem är verifierad som körande. |
| Hemligheter | Rena miljövariabler (.env.example listar 20+). Infisical är inte inkopplat — ingen referens till Infisical/vault finns i repot. Vercel-projektets env är källan för webblagret. |
| CI | .gitea/workflows/ci.yml. quality-jobb: typecheck (blockerande, endast src/ + tests/), vitest (blockerande, 17 testfiler), eslint (rådgivande), next build (blockerande). security-jobb: Trivy --exit-code 0 (rådgivande). Kör på ubuntu-latest. |
Konflikt att fatta beslut om: estate-projektet GCP/Vercel-exit säger att flottan ska bort från Vercel. 0pt:s enda körande instans ligger på Vercel. Se §9.
7. Drift
Hälsokontroll
Fixat (BACKLOG P0-3): GET /api/health → {ok, service:"0pt-web", db, queue, engine, stripe, ts}. db/engine/stripe rapporterar nu en riktig
beroendeprobe (lib/health-checks.ts): "ok" = lyckad live-anrop, "error" =
konfigurerad men onåbar (se detail), "not_configured" = env-variabel ej
satt. queue kan bara vara "not_configured"/"not_integrated" — ingen
Redis-klient finns i repot, så en satt REDIS_URL rapporteras ärligt i stället
för att låtsas testa en anslutning som aldrig görs. ok är true endast när
ingen konfigurerad dependency ligger i "error". HTTP-statusen är fortsatt
alltid 200 (liveness — processen svarar) oavsett dependency-läge; en extern
beroendekrasch ska inte omstarta instansen. Ny GET /api/ready failar stängt
(503) när databasen (den enda lastbärande dependencyn idag) inte både är
konfigurerad och nåbar — peka en Coolify/lastbalanserar-readiness-probe dit,
inte mot /api/health. Engine har ett separat GET /health som checkEngine()
nu faktiskt anropar (tidigare kollades bara ENGINE_URL-närvaro).
Loggar
Ingen strukturerad loggning. console.warn i src/auth/context.ts är den enda
avsiktliga loggpunkten. Ingen korrelation-id-propagering till logg (ett
requestId skapas i readRequestContext men loggas aldrig). Ingen
Sentry/GlitchTip/OTel-koppling.
Backup + senast testad restore
Ingen backup. Det finns ingen databas i produktion att säkerhetskopiera (§4.2), inget backupskript, ingen dumproutine. Restore har aldrig testats. Om/när Postgres provisioneras måste detta lösas separat — jfr estate-blockeraren "ingen offsite backup".
Kända fällor
pnpm installkräver--ignore-workspace.pnpm-workspace.yamlär en v0-exportartefakt med baraallowBuilds:och ingetpackages:-fält, vilket får pnpm att avbryta.Dockerfileskickar flaggan;.gitea/workflows/ci.ymlgör det inte — CI:spnpm install --frozen-lockfilekan alltså falla på samma sak som Dockerfilen redan fått ett workaround för. Ej verifierat vilken pnpm-version som räddar CI idag.- Node-versionsglapp: CI kör Node 22, Dockerfile bygger på Node 24.
- In-memory-state (§4.2) gör att varje kallstart och varje ny Vercel-lambda nollställer capability-lagret. Testresultat i CI säger ingenting om detta.
next.config.mjssätterimages.unoptimized: true— inte en bugg, men förklarar varför bildoptimering saknas.services/scrapers/output/*.jsoninnehåller committad skrapdata (brreg-sidor) i repot.__pycache__/*.pycär committade iservices/.
8. Säkerhet
Vad som faktiskt finns
- Riktig kryptografisk tokenverifiering.
src/auth/zitadel.tsgör äkta JWKS-baseradjwtVerifymed issuer-kontroll ochexp/nbfviajose. Tenanthärledning har dokumenterad precedens (urn:zitadel:iam:org:id→urn:zitadel:iam:user:resourceowner:id→tenant_id→tenant), och ett token utan tenantclaim underkänns. - Fail-closed default i koden.
authEnforced()returnerartrueför allt utom literalen"false", ochassertAuthConfigSafe()kastar vidNODE_ENV=production+ auth av. Designen är korrekt. - Stripe-webhooken verifierar signatur mot
STRIPE_WEBHOOK_SECRET. Om Stripe-konfiguration saknas svarar den nu503i stället för att kvittera ett obearbetat event med200; saknad signatur ger400. Deposit-checkout bär nu order-id/kind i sessionsmetadata. Webhooken flyttar bara en order efter verifierad signatur,payment_status=paid, matchande belopp och valuta. En skapad slutbetalnings-Checkout markerar inte längre orderndeliveredinnan betalt event har verifierats. Deposit-sidans demo-fallback kan inte längre märka en misslyckad betalning som lyckad. Klient-PATCH kräver nu verifierad auth, följer tillåtna statusövergångar och kan aldrig skrivain_production/delivered; concurrent writes använder jämför-och-sätt. Slutbetalningsrouten kräver också verifierad auth. Ny order-POST kräver nu en verifierad Zitadel-principal; dennes tenant och subject lagras iorders.tenant_id/buyer_subjectviasql/011. Buyer GET/list/PATCH och deposit/release använder tenant-predikat i SQL; legacy-ordrar utan owner är osynliga och obetalbara. Fortfarande öppet före live-läge: migrationen har bara smoke-testats på engångs-Postgres och måste appliceras i målmiljön före deploy. Browser-UI saknar produktions-SSO/Bearer och stagingköp/refund är ännu inte verifierat. Beslut och nästa steg finns iADR-001-order-ownership.md. SECURITY.mdfinns med rapporteringsväg (security@siax.io).
KÄNDA LUCKOR
S1 — /api/0pt/* svarar anonymt i produktion, trots "fail-closed". 🔴
(live-verifierat 2026-08-07) GET https://0pt.app/api/0pt returnerar 200
utan Authorization-header. Samma sak med ett medvetet ogiltigt token
(Bearer aaa.bbb.ccc) → 200. GET /api/0pt/lakehouse/mappings och
/api/0pt/sources/rate-policies returnerar 200 + [] anonymt. Eftersom
lakehouse-routen bara finns i nyare kod är deployen inte gammal — middlewaren
kör helt enkelt inte. README.md rad 49–52 och merge-commit 6531384
("make the AUTH_ENFORCE default fail-closed — 0pt.app was answering anonymously")
påstår motsatsen. Orsaken är OKÄND. Ej uteslutna hypoteser, ingen verifierad:
(a) middleware.ts plockas inte upp av Next 16 i den här filplaceringen när ett
src/-träd finns, (b) Vercel-projektet har ingen middleware i sin
build-output, (c) något annat. Detta måste rotorsaksanalyseras innan repot
beskrivs som skyddat någonstans.
S2 — Ingen handler läser den verifierade tenanten. 🔴
middleware.ts:56-57 sätter x-0pt-verified-sub / x-0pt-verified-tenant på
den vidarebefordrade requesten. En grep över hela repot visar att ingen fil
utanför middleware.ts läser de headrarna. Samtliga /api/0pt/*-handlers tar
i stället tenant_id från request-body eller querysträng
(app/api/0pt/sources/[sourceId]/query/route.ts:49,
documents/capture/route.ts:27, lakehouse/mappings/route.ts:27,49,
sources/rate-policies/route.ts:26, m.fl.). Även med fungerande middleware kan
alltså en innehavare av ett giltigt token för tenant A skicka
{"tenant_id": "t_b"} och arbeta i tenant B. Middlewarens 403 vid
X-Tenant-Id-konflikt hjälper inte — den kontrollerar bara headern, inte
bodyn. Tenantisolering är inte implementerad.
S3 — Historiskt anonymt icke-/api/0pt-API; delvis åtgärdat i source.
Matchern täcker bara /api/0pt/:path*. Orderkö/adminmetrik, enskild
order-GET, deposit- och release-checkout samt order-PATCH har nu explicita
route-auth-grindar i source. De äldre live-observationerna av anonymt 200
beskriver tidigare deploy, inte verifierat nuläge. Övriga icke-prefixade
API:er kräver egen inventering. Buyer-order-API:t har nu tenant-predikat i
source, men migration, UI-SSO och liveverifiering återstår enligt ADR-001.
S4 — Historisk escrow-bugg åtgärdad i source; liveköp återstår.
POST /api/payments/{order_id}/release kräver nu route-auth och skapar
Checkout först efter accepted. En skapad session sätter inte delivered;
endast en signerad, betald och matchande Stripe-webhook får göra det.
Cross-tenant-skydd av buyer-ordern finns nu i SQL-predikat, men är inte
produktionsbevisat innan sql/011 har applicerats, UI-SSO är kopplat och
stagingköp/refund är verifierat. Livebetalning får därför inte aktiveras.
S5 — CL0UD-policyklienten är fail-open utan produktionsspärr.
src/clients/cl0ud-client.ts:45 — saknas CL0UD_URL returneras alltid
{decision: "allow"} utan nätanrop. Till skillnad från auth finns ingen
assertAuthConfigSafe-motsvarighet, ingen NODE_ENV-kontroll och ingen varning.
En produktionsdeploy som tappar variabeln godkänner tyst varje
sidoeffektsbärande capability, inklusive Iceberg-writes (L4).
S6 — AUD0-audit är no-op utan produktionsspärr.
src/clients/aud0-client.ts — saknas AUD0_URL skrivs inga auditevent och inga
lineage-event, tyst. Kombinerat med S5 innebär det: inga beslut fattas, inga
spår lämnas. sql/001_init.sql har en audit_log-tabell som TypeScript-lagret
aldrig skriver till.
S7 — db-grant-routen påstår kryptering som inte finns.
app/api/connections/db-grant/route.ts kommenterar att anslutningssträngen
"lagras KRYPTERAT (AES-GCM via GRANT_ENC_KEY), aldrig i klartext". Koden gör
ingenting av detta: utan GRANT_ENC_KEY returneras 501, och med nyckeln
returneras ett slumpat grant_id — ingen validering av read-only, ingen
kryptering, ingen lagring, ingen audit. Kommentaren beskriver en oimplementerad
funktion i presens.
S8 — FastAPI-engine har ingen autentisering.
services/api/orchestrator/main.py (12 endpoints, inkl. POST /orders,
PATCH /orders/{oid}, POST /orders/{oid}/run) saknar varje form av
auth-dependency. lib/engine.ts skickar X-Internal-Token, men servern läser
den aldrig. Var engine än exponeras är den öppen för alla som når porten.
S9 — Compliance-koden motsäger produktlöftet. 🔴
services/scrapers/waf_bypass.py implementerar FlareSolverr, Tor-rotation,
cloudscraper, TLS-fingerprintbyte, gratis proxypool, "undetected browser"
(nodriver) och "fake identity generation".
services/infra/docker-compose.data-stack.yml startar flaresolverr och
tor-proxy. services/api/workers/adaptive_router.py inleds med
"""Adaptive router — all methods, no compliance restrictions.""".
Detta står i direkt konflikt med CLAUDE.md §1, README.md ("Bypass/stealth/
proxy-rotation/fake-account paths are excluded by design") och med hela den
juridiska positionering som säljs på 0pt.app. Det är samtidigt en
affärsrisk: att marknadsföra laglig sourcing medan bypass-kod ligger i repot
är svårt att försvara vid en granskning.
S10 — Godtycklig anslutningskonfiguration från request-body.
POST /api/0pt/sources/{sourceId}/query tar connection_config direkt ur bodyn
och skickar den till adapterbygget (src/services/source-query-service.ts:95).
Utan fungerande auth (S1) innebär det att en anonym anropare kan få tjänsten att
öppna utgående anslutningar mot en angiven värd — SSRF-liknande primitiv, och en
väg att låta 0pt-instansen agera proxy.
S11 — Typkontrollen täcker inte auth-koden.
next.config.mjs sätter typescript.ignoreBuildErrors: true, och
tsconfig.check.json inkluderar endast src/**/*.ts + tests/**/*.ts.
middleware.ts ligger i roten och typkontrolleras därför i praktiken inte i
CI — trots att det är hela authseamen. eslint är dessutom rådgivande
(continue-on-error: true).
S12 — Sessions-scaffoldet är svagt.
lib/auth.ts signerar {email, iat} med HMAC-SHA256 mot AUTH_SECRET, utan
exp-kontroll vid verifiering (bara cookiens maxAge), utan rotation och utan
bindning till en användarpost. Kommentaren erkänner att det är ett scaffold; det
är ändå den mekanism GET /api/auth/me litar på.
9. Öppna frågor
- Varför kör inte middlewaren i produktion (S1)? Måste rotorsaksanalyseras.
Ingen dokumentation får påstå att
/api/0pt/*är skyddat innan en anonym request faktiskt får 401 från0pt.app. - Ska
waf_bypass.py, FlareSolverr och tor-proxy raderas (S9)?CLAUDE.mdsäger uttryckligen "remove or disable it". Commit4a164c5gick åt motsatt håll. Detta är ett Simon-beslut med både juridisk och marknadsföringsmässig innebörd — inte något en session ska avgöra ensidigt. - Vercel eller flottan? (config-delen löst 2026-08-09, trafik-delen kvarstår.)
0pt:s enda körande instans ligger fortfarande på Vercel — den ändringen är
medvetet INTE gjord här (skulle kräva en egen cutover-plan: verifiera att inget
riktigt kunddata/trafik ligger bakom
0pt.app— vilket är fallet idag, se §6/§7:/api/healthvisardb/queue: not_configured, ordrar lever bara in-memory — provisionera en Coolify-app, peka DNS om, och avveckla Vercel-projektet, i den ordningen). Det som ÄR gjort:siax.config.jsondeklarerar nutarget: "coolify"(matchar varje annat SIAX Cloud OS-repo) och.gitea/workflows/deploy.ymlfinns med en medveten platshållar-UUID som failar tills en riktig Coolify-app skapats. Ingen nod är tilldelad (node: null, matchar st0re/aud0:s mönster). Se README "Deploy" för samma sammanfattning. - Ska capability-lagret vara i drift alls? Det är exponerat publikt men helt in-memory och utan delad state mellan routemoduler (§4.2). Antingen backas det av Postgres, eller så bör routes stängas tills dess.
- Var är
sql/002_*ochsql/003_*? Tre filer refererar dem. Fanns de någonsin, eller är referenserna aspirationella?opt_sourceshar ingen migration här. - Ingen databas i produktion. Är
0pt.appavsedd att vara en publik marknadsföringssajt tills vidare, eller är det ett driftfel attPOSTGRES_URLaldrig satts? Ordrar som skapas idag försvinner. package.jsonheter fortfarande"my-v0-project"— v0-exportartefakt. Byte påverkar inget bygge men bör göras för spårbarhet.- Designsystem: repot har ett eget dokumenterat mono/editorial-system
(
docs/05-design-system.md) + shadcn/ui och konsumerar inte@siax/ui. Ska 0pt konvergera mot estate-standarden eller är avvikelsen medveten?siax.config.jsonsätter därfördesignSystem: nullsnarare än att påstå något osant. - Tenantmodell: är en 0pt-tenant 1:1 med en Zitadel-org? Koden antar det i
deriveTenantIdmen det är inte bekräftat mot CL0UD:s tenantregister. - Infisical: hemligheter ligger i Vercel-env, inte i credential-SSOT. Behöver bootstrappas.
GET /api/screenproxar mot OpenSanctions utan avtal — kommentaren i koden säger själv "VERIFY commercial terms before production volume".- Konsumentledet är odokumenterat.
CLAUDE.mdsäger att 0pt matar Protosell/FiveCRM och VehIQ. Inget i repot visar en sådan integration.