24 August 2026 · v2 — încorporează review-ul Prodi (24 Aug) + reîncadrarea proiectului · BONO × SOLO · Confidențial Autor: BGN + Claude · Complementar cu prd-iris-mvp-manual.md (spec-ul funcțional al Fazei 1)
Iris este și un test de organizație: un modul dezvoltat de BGN + Claude Code, cu ajutorul echipei IT pe arhitectură (acest document e dovada — review-ul Prodi a schimbat v1 în bine), dar fără implicare de implementare sau mentenanță din partea echipei.
Criteriile de decizie derivate din acest obiectiv, aplicate consecvent în tot documentul:
Ținta de scalare rămâne neschimbată din v1: zeci de mii de clienți finali, sute de mii de documente/lună (~750k/lună ≈ 2–3 doc/sec la vârf), cu plafonul arhitecturii la 10–15× peste țintă.
Iris nu vorbește cu shell-urile (Dublin, Vertigo, Falcon). Per organizație există un singur sistem-partener:
Clientul încarcă în Dublin ──┐
SPV / eFactură ──────────────┤
Facturare (emise) ───────────┼──▶ NUCLEU (BONO) ──▶ ProcessingRequest ──▶ IRIS
Extrase (upload/OpenBanking)─┘ system of record: │
stochează documentele, │
registrul firmelor, ◀── note ──────┘
jurnalul de evenimente
│
Dublin și Vertigo citesc din Nucleu
| Subiect | v1 | v2 | Sursa deciziei |
|---|---|---|---|
| Stack backend | de decis (propus .NET) | .NET 10, stil minimal modern | Prodi confirmă; dezbaterea TS vs .NET tranșată pe merite (vezi §1) |
| Baza de date | Postgres (nespecificat) | Neon — Postgres gestionat, Frankfurt | criteriul zero-ops; capabilități native (RLS) |
| Compute | de decis | Railway (containere Linux, UE) | criteriul zero-ops; deploy prin CLI |
| Storage documente | S3-compatibil | Cloudflare R2 (UE), swap path AWS S3 documentat | simplitate + cont existent; API S3-compatibil |
| Autentificare tenant | chei API în Iris | OAuth2 M2M via iam.bono.ro; Iris = resource server | Prodi — serviciu existent |
| ANAF / BNR | cache construit în Iris | directory.bono.ro + forex.bono.ro (existente, cache-uite) | Prodi — servicii existente |
| Criptare documente | envelope + crypto-shredding | doar SSE la storage; fără criptare aplicativă în v1 | Prodi — cost/simplitate; condiție: fereastra de backup în DPA |
| Nr. afișare BN-YYYY-NNNNN | schiță | mecanica Prodi + decizii: NU gapless; anul = data ingestului | Prodi (mecanică) + BGN (decizii) |
| HA / failover DB | primary + replică | gestionat de Neon (PITR din bifă); nimic de operat | dizolvat de alegerea managed |
| Bulk import | de decis | manifest NDJSON + presigned URLs (de validat cu SOLO) | propunere BGN, aliniată la reformularea Prodi |
Backend: .NET 10 — decizie luată după o dezbatere reală TS vs. .NET, câștigată de .NET pe argumente, nu pe conformism:
Stilul: .NET modern minimal, nu convențiile istorice ale platformei — minimal APIs + EF Core + records + Guid.CreateVersion7(); explicit NU NHibernate/hbm.xml/CQRS ceremonios. Luăm limbajul echipei, nu tot bagajul — zona de maxim corpus pentru Claude Code.
Invariant de dependențe (răspuns la supply chain, valabil și în .NET): lista de pachete externe rămâne scurtă (<15), fixată în versiuni, auditată în CI la fiecare PR.
Frontend: React 19 + TypeScript (Workbench-ul operatorului), cu clientul de API generat din OpenAPI — modelul canonic are o singură sursă de adevăr (C#), tipurile frontend derivă mecanic.
Forma aplicației: monolit modular cu porturi — neschimbat din v1:
Iris (.NET 10, un serviciu + workers) ├── Ingest — API tenant, validare, dedup hash, triaj ├── Storage — fișiere (R2), retenție, presigned URLs ├── Workbench API — coada de lucru, conspectare, interpretare, dubii ├── Engines — porturile IExtraction / IInterpretation / IRules + routerul de cascadă ├── Rules — taxonomia + tabelul Adam, versionate ├── Memory — istoricul per (tenant, client, furnizor, descriere) ├── Export — outbox, webhooks, GET note ├── Identity — RBAC operatori umani (tenants/chei = iam.bono.ro, NU aici) └── Telemetry — evenimente, metrici, cost/doc, SLO
Porturile motoarelor (contracte care rulează din ziua 1, cu implementări manuale în Faza 1) și cele 5 contracte cu regulile lor (schema_version, doar aditiv, confidence nullable, trace_id, fixtures de aur) — neschimbate din v1.
Jurnalul de evenimente = sursa adevărului; tabelele de stare = proiecții. Transactional outbox pentru tot ce iese (webhooks semnate, retry + backoff, consum idempotent). Corecțiile post-export = evenimente noi (nota.corrected), notele emise sunt imutabile. Audit record-ul imutabil = evenimente. *(Detalii în v1 — secțiunea rămâne integral valabilă.)*
De ce Postgres gestionat, după ce review-ul propusese MariaDB: argumentele MariaDB („echipa o operează", „standardul platformei") nu se aplică unui modul pe care echipa nu îl operează. Sub criteriile §0 câștigă Postgres:
tenant_id, plus test de izolare în CI) → două straturi independente de apărare.events, documents, lines) de la prima migrare; coada în Postgres (FOR UPDATE SKIP LOCKED) cu fairness per tenant; JSONB pe evenimente; UUIDv7 ca PK intern.Anexă de compatibilitate MariaDB (din maparea Prodi, păstrată ca plan de portare): mecanica secvențelor, partiționarea RANGE, SKIP LOCKED (10.6+), UUID — toate există și în MariaDB; singura piesă fără echivalent e RLS, compensată exact de stratul aplicativ pe care îl păstrăm oricum. Dacă Iris e predat vreodată echipei și se dorește alinierea la platformă, portarea pierde doar stratul redundant, nu modelul de securitate.
Numărul de afișare BN-2026-00003 — mecanica din review, adoptată integral: rând de secvență (tenant, an) cu SELECT … FOR UPDATE în aceeași tranzacție cu insertul; tranzacție minimă (zero procesare sub lock); alocare după dedup; ordinea lock-urilor consecventă. Deciziile cerute: (1) NU gapless — e handle de afișare/tracking, numerele fiscale reale rămân pe documentele originale; (2) anul = data ingestului — importurile istorice primesc numere în anul curent, în ordinea intrării.
Memoria = tabele normale cu exact-match pe (tenant, client, furnizor_cui, descriere_normalizată) — fără vector DB în v1. Retenție: 12 luni rolling, automată (politici în DB + lifecycle în R2).
API-ul + workerii (coadă, outbox, cron) rulează ca containere .NET pe Railway: deploy dintr-o comandă, loguri/restart/secrete prin CLI — integral operabil de Claude Code. Workbench-ul (SPA React) pe Vercel (fluxul și domeniul există deja). Preview environments: branch de cod (Railway/Vercel) + branch de bază (Neon) = un Iris complet de test per schimbare.
Fluxul, cozile și eșecurile — neschimbate din v1: totul asincron (202 + id instant), idempotență peste tot, fairness per tenant, dead-letter pentru poison documents, circuit breakers. Dependențele externe devin serviciile interne BONO (per review): directory.bono.ro (ANAF, cache-uit) și forex.bono.ro (BNR) — consumate prin porturile ICompanyDirectory / IForexProvider, endpoint-uri în config; indisponibilitate → flag „neverificat", nu blocare. SLO-uri per tenant, măsurate din evenimente, calibrate onest la modelul de operare (natura asincronă iartă: dacă Iris e jos o oră, tenantul retrimite, nimic nu se pierde).
Observabilitate: loguri structurate cu trace_id propagat cap-coadă + un serviciu gestionat de logging (Axiom / Better Stack) — Claude citește logurile prin API când depanează.
iam.bono.ro (per review): tenantul schimbă (client key, secret) pe JWT; Iris = resource server — validează token-ul (JWKS local + TTL scurt), nu stochează nicio credențială de tenant; tenant_id vine exclusiv din claim-urile validate; rate limiting pe claim.Piramida: determinist (parsare + istoric + reguli, cost 0) → Haiku → Sonnet → om (ordinele de mărime: omul e 100× AI-ul scump). Zero-Touch-ul ieftin vine din memorie, nu din inteligență. Lansare cu un singur model (Sonnet); routerul de cascadă există din ziua 1; cascada se activează pe felii, pe datele eval dataset-ului. Shadow mode + feature flags per tenant × categorie = mecanismul de conectare a motoarelor (niciun motor nu primește frâiele direct). Cost/doc = metrică de gradul 1 în Telemetry.
Pasul 0, înaintea tuturor (concluzia review-ului, adoptată integral): modelul canonic de date — nucleul comun + extensiile per cele 6 tipuri de documente (extrasul de cont = testul polimorfismului), modelul de context (master data), contractele derivate din el. Draft: BGN + Claude; sesiune de shaping cu Prodi + Cristi pe strawman.
Închise în v2: stack (.NET 10 minimal) · DB (Neon Postgres) · compute (Railway) · storage (R2 + swap path) · auth (iam) · ANAF/BNR (directory/forex) · criptare v1 (SSE only) · numere de afișare (mecanică + nu-gapless + an ingest) · contract Tenant-out (Iris definește, destinatarii implementează) · HA (gestionat).
Rămase deschise: formatul final bulk import — manifest NDJSON + presigned ca propunere principală, de validat cu SOLO · numărul concret al ferestrei de backup pentru DPA (propunere: 30 zile) · security review-ul punctual pe perimetru (cerere către Prodi) · modelul canonic de date (pasul 0, în lucru).