| AuditService | ListSegments | ja | ja | admin | — (**Ingen klient** i `src/grpc.rs`) | — | — | Ej byggd yta | Samma medvetna gap som Query/SentTo |
| JobService | ListJobs (endast `jobs`-byggen; filter `state` = waiting/running/failed/done, holger admit-queue) | ja | ja | admin | Admin-sidan (`/admin`, fliken **Queue**) — BFF:en frågar med konsolens credential på `/api/admin/jobs?state=…`; admin-golv i middleware (`ADMIN_READS`) OCH i handlern, holger frågas aldrig för en lägre session; okänt state-ord nekas med 400 utan att holger frågas. **Ingen klient** i `src/grpc.rs` (wasm) | ja (501 = bygge utan `jobs` sägs ut) | backend-test (`api::admin_queue`: viewer/operator/utan session/förfalskad cookie nekas, 0 anrop till holger; admin får raderna, state når holger) + enhet (`admin_queue`) | Täckt via BFF | Wasm-koden anropar inte RPC:n själv |
| JobService | JobRow-fälten `stage`=12, `attempts`=13, `max_attempts`=14, `next_attempt_micros`=15, `priority`=16 (jobbtyperna `holger_fetch`, `holger_mirror_asset`; status `retry_wait` med reason och nästa försök) | ja | ja | admin | Queue-fliken: status · stage, attempts/max, reason, nästa försök (UTC-tid + "in N s"), prioritetsklass (0 full, 1 reduced, 2 floor, 3 queued), requested_by | ja | enhet (`admin_queue`: tider, avrundning uppåt, klassord) | Täckt via BFF | — |
| JobService | GetJob | ja | ja | admin | — (**Ingen klient** i `src/grpc.rs`; ingen detaljvy för ett jobb — listraden bär alla fält) | — | — | Ej byggd yta | Medvetet gap: ListJobs-raden är samma JobRow |
| AdmissionService | ListUsage (varje konsument i usage-boken: fönster 30 d + i dag, per Work-typ estimerad/faktisk kostnad, klass, planens mening, in flight; dörrens policy och andelar) | ja | ja | admin | Admin-sidan, fliken **Admission** — BFF:en på `/api/admin/admission` (admin-golv i middleware + handler). Konsument: principal / paired peer / anonym dagstoken visad som "anonymous (today)". Under `pass-through` visas used/limit som "N/A (pass-through)", aldrig 0. **Ingen klient** i `src/grpc.rs` (wasm) | ja | backend-test (`api::admin_queue`: nekas under admin med 0 anrop; pass-through = bara ListUsage) + enhet (`admin_queue`: N/A aldrig 0) | Täckt via BFF | Wasm-koden anropar inte RPC:n själv |
| AdmissionService | AdmitPlan (planen för en konsument och ett arbete nu: klass, mening, `budget_left`, andel) | ja | ja | admin | Admission-fliken: BFF:en frågar AdmitPlan (work `download`) en gång per namngiven konsument (`principal:`/`peer:`) + en gång `anonymous` (dagsbudgeten för en färsk token) — limit = used + `budget_left`; en nekad plan visas med holgers ord. Frågas inte alls under `pass-through` | ja | backend-test (1 ListUsage + 1 per namngiven + 1 anonym probe) | Täckt via BFF | Högst 200 konsumenter per sidladdning (`MAX_CONSUMERS`) |
| AdminService | FlowSvg (flödesbilden som SVG, beräknad av holger: RON-grafen + per dörr/repo: anrop i dag och distinkta konsumenter, `no consumer` vid noll) | ja | ja | öppen¹ (samma läsgrind som `/metrics`) | Flow-sidan (`/flow`) — BFF:en frågar med konsolens credential och skickar bilden same-origin på `/api/flow.svg` (session krävs, viewer-golv) | ja | backend-test | Täckt via BFF | Wasm-koden anropar inte RPC:n själv; bilden ritas som `<img>` från BFF:en så `img-src 'self'` håller |
| AdminService | BecomeTwin (bootstrap-dörren som gör en låda till tvilling) | ja | nej | admin | — | — | — | Ej lämplig för webb-UI | En bootstrap-handling från xtask, en gång per låda; ingen knapp i en konsol |
| AdminService | AdminSnapshot (adminvyn: varje disk och volym med statvfs, tvillingen, tillväxten + journalen, klockan, plugins/monetize, audit, installatörens loggdörr) | ja | ja | admin | Admin-sidan (`/admin`, flikarna Disks · Twin · Growth · Clock · Plugins / Monetize · Logs) — BFF:en frågar med konsolens credential på `/api/admin/snapshot`; admin-golv i middleware (`ADMIN_READS`) OCH i handlern, holger frågas aldrig för en lägre session | ja | backend-test (viewer/operator/utan session nekas, holger ej tillfrågad; admin får svaret) | Täckt via BFF | Wasm-koden anropar inte RPC:n själv; länken visas bara för en admin-session enligt `/auth/status`, men skyddet är serverns |
| AdminService | TwinCsr, IssueTwinCertificate, InstallTwinCertificate (tvillingen över mTLS: nyckel, utfärdande, installation) | ja | nej | admin | — | — | — | Ej lämplig för webb-UI | Parningssteg som installatören/estate-ops kör en gång per låda; adminvyn visar resultatet (certifikatens livslängd) men gör dem inte |
| AdminService | Recharge, RechargeStatus, TwinStatus (återladdning av en död main från tvillingen, och lanens två frågor) | ja | nej | admin | — (TwinStatus-uppgifterna syns i adminvyns Twin-flik via AdminSnapshot) | — | — | Ej lämplig för webb-UI | Återladdningen körs av monetize restore-lanen / `holger-server recharge`, inte från en konsolknapp |
| AdminService | FencePrimary (en återladdning stänger den gamla primärens skrivningar) | ja | nej | admin | — | — | — | Ej lämplig för webb-UI | Körs av återladdningslanen (`fence_primary`), inte från en konsolknapp |
| AdminService | ImportIntelBundle, ImportBundle (lådans importdörrar för intel- och uppdaterings-/airgap-bundlar, lane W4: klientströmmar) | ja | nej | Repository Admin eller Security Approver | — (samma kod nås över HTTP `POST /-/intel/import`, `POST /-/bundle/import`, inkorgarna och CLI:n) | — | — | CLI/HTTP-yta | En bundle bärs in av operatören; ingen konsolknapp beslutad |
| AdminService | Topology (estatets tavla: front, main, tvilling och nedströms, ritad av servern i `svg`) | ja | ja | admin-läsning (serverns) | Security-dashboarden överst: serverns SVG som bild (`data:image/svg+xml`, kör inget skript); vägran visas med serverns kod och ord | ja | enhet (mockserver) | Fullt täckt | Konsolen ritar och avgör inget |
| AdminService | SecuritySummary (Security-sidan: gatewayn i holgers ord — kedjan, källor och skannrar, vad som väntar och hålls, vem som läser, synkens signaturer, de senaste säkerhetshändelserna; en sektion per kort) | ja | ja | Admin, Repository Admin, Security Approver eller Auditor (serverns, `holger-auth-2` annars) | Security-sidan (`/security`): ett kort per sektion som servern skickade, raderna som de kom; ingen estate-tavla (den är Översiktens); vägran visas med serverns kod och ord | ja | browser-test (`security_page`) | Fullt täckt | Konsolen ritar och avgör inget (lane HOL-SEC, raden saknades: HHUNT-3) |
# UI ↔ backend capability coverage
> Verifierad 2026-08-13, utökad 2026-08-14 (type-to-confirm, skeletons)
> mot `proto/holger.proto` (vendrad, byte-identisk med holger-huvudrepot),
> runtime-wiringen i `server/lib/src/lib.rs` (`start_grpc_endpoint` registrerar
> **elva** tonic-tjänster (åtta alltid, MigrationService endast i ett `online`-bygge)) och tjänsteimplementationerna + testerna i
> `server/lib/src/grpc/services.rs`. Källordning: tester → runtime-wiring →
> implementation → konfiguration → schema → docs.
>
> **Maskinvaktad:** `tests/contract.rs` failar om proton får en service eller
> RPC utan **tabellrad** i det här dokumentet (en mening i löptext räcker inte),
> om en proto-tjänst saknar klient i `src/grpc.rs`, om två RPC:er delar namn
> (matrisen nycklas på namnet), och om en RPC som **ingen vy anropar** ändå står
> som "Fullt täckt" — det gapet måste redovisas som gap. Underhålls för hand
> därutöver.
>
> "Testad"-nivåer: **live** = körd mot riktig server i webbläsaren (dev och/eller
> prod-kedjan över tailnätet); **backend** = exekverbart backendtest; **enhet** =
> nativ enhetstest i UI-repot; **—** = ej exercerad (skälet står i Åtgärd).
## gRPC-tjänster (runtime-registrerade, nåbara genom gRPC-web-dörren)
| Service | Backendfunktion | Runtime-ansluten | Webbsäker | Krävd roll | Nuvarande UI-yta | UI-state komplett | Testad | Status | Åtgärd |
|---|---|---:|---:|---|---|---:|---:|---|---|
| AdminService | Health (status/version/uptime) | ja | ja | öppen | Topbar-badge + Översiktens serverkort | ja | live | Fullt täckt | — |
| AdminService | ListRepositories | ja | ja | öppen¹ | Repolistan (filter/sortering i URL; en grupp visas som `group · N members` ur `is_group`/`members`), pickers, Översikts-sammanfattning | ja | live | Fullt täckt | — |
| AdminService | ServerProfile (read-only/degraderat läge, writable-antal) | ja | ja | öppen | Översiktens Mode-kort + read-only-varningsbanner | ja | live | Fullt täckt | — |
| AdminService | WhoAmI (subject/metod/effektiv global roll/read-gate) | ja | ja | öppen (introspektion, ekar aldrig credential) | Topbar-identiteten, Översiktens Session-kort, Account-sidan | ja | backend + live | Fullt täckt | Tillagd i denna iteration (proto + server + test + UI) |
| AdminService | Capacity (total/tillgängligt/använt av holger, statvfs över dataroten) | ja | ja | admin | — (ingen admin-sida för lagring finns i konsolen; ingen ny sida skapas utan beslut) | — | — | Ej täckt | Tillagd i holger `4426a12`; visas när en admin-lagringsyta beslutas |
| RepositoryService | FetchArtifact (unär) | ja | ja | öppen¹ | Download-knappen | ja | live | Fullt täckt | — |
| RepositoryService | StreamArtifact (server-streaming) | ja | ja | öppen¹ | — (unär används) | — | — | Delvis täckt | Medvetet: webbläsaren buffrar blobben oavsett; unär räcker |
| RepositoryService | ListArtifacts (limit/page_token/name_filter) | ja | ja | öppen¹ | Browse på repo-detaljen; behåller tidigare data under omladdning | ja | live | Fullt täckt | Tomläget avgörs av `RepositoryInfo.supports_listing` (holger `4426a12`): false ⇒ tom lista ≠ tomt repo; true ⇒ repot är tomt. Ingen gissning per repo-typ |
| RepositoryService | PutArtifact | ja | ja | writer | Upload-panelen, gated på `writable`-flaggan (servern förblir domare) | ja | live | Fullt täckt | Read-only-repon får förklaring i stället för panel |
| RepositoryService | PutArtifactStream (strömmad uppladdning av stora artefakter; stinky 14) | ja | ja | writer | — (grpc-web strömmar inte klient→server) | — | — | Olämplig | Webbläsarens uppladdning går via PutArtifact; paketverktygen och CLI strömmar |
| ArchiveService | ListArchiveFiles (prefix-filter) | ja | ja | öppen¹ | Arkiv-sektionen på repo-detaljen (syns vid `has_archive`) | ja | live | Fullt täckt | Verifierad mot RIKTIGT znippy-arkiv (3 crate-sökvägar, prefix `vetra` → 2/2) |
| ArchiveService | ArchiveInfo (antal/bytes/sökväg) | ja | ja | öppen¹ | Samma sektion | ja | backend + live | Fullt täckt | Verifieringen HITTADE en backend-bugg (writable-läget svarade nollor) — fixad i holger `906e748` + regressionstest |
| SearchService | Search: name/namespace/version/checksum/path | ja | ja | öppen¹ | Sökvyn (checksum-varningen bevaras) | ja | live | Fullt täckt | Stale-svar ignoreras via generationsräknare |
| SearchService | Search: repositories-begränsning | ja | ja | öppen¹ | Repo-dropdown + "search within this repository" | ja | live | Fullt täckt | — |
| SearchService | Search: properties (PropertyMatch) | ja | ja | öppen¹ | Key/value-filterrader, URL-synkade, chips + reset | ja | live | Fullt täckt | — |
| SearchService | format-axel | — | — | — | — | — | — | Ej runtime-ansluten | Finns inte i proton; att hitta på den vore simulering |
| SbomService | FetchSbom | ja | ja | öppen¹ | Detaljvyns SBOM-sektion (NotFound = normalläge) | ja | live | Fullt täckt | — |
| SbomService | AttachSbom | ja | ja | writer | Samma sektion; refetch efter attach; tidsstämplad | ja | live | Fullt täckt | — |
| PropertyService | GetProperties | ja | ja | öppen¹ | Detaljvyns properties-tabell | ja | live | Fullt täckt | — |
| PropertyService | SetProperty (tomma values = remove) | ja | ja | writer | Editor + per-rad Remove; svarets fulla karta är sanningen | ja | live | Fullt täckt | — |
| PromotionService | PromoteArtifact | ja | ja | admin (mål-repo) | Detaljvyn bakom bekräftelsedialog med **type-to-confirm**: målrepots namn måste skrivas ut innan knappen armas (from→to, koordinat, seal-once-konsekvens; fokusfälla + fokusåterställning) | ja | enhet + live | Fullt täckt | Grinden live-verifierad mot kringgång: källrepots namn, nästan-rätt och fel skiftläge armar INTE; återöppning nollställer. Dialogens tre vägar verifierade: konsekvens, cancel, vägran ordagrant |
| PromotionService | ListIntakeArtifacts (säkerhetsdashboarden: serverns fem köer med antal, och den öppna köns rader) | ja (`supply-chain`) | ja | läsplikt per rad (serverns) | Security: köbrickor + tabell, `/security?queue=` | ja | enhet (mockserver) + live i holger | Fullt täckt | Köerna och vilka tillstånd de rymmer är serverns |
| PromotionService | GetArtifactState (tillstånd, historik, röster mot kvorum) | ja (`supply-chain`) | ja | läsplikt (serverns) | Security → artefaktvyn: tillstånd, importör, "1 of 2 approvals", historiktabell | ja | enhet (mockserver) | Fullt täckt | — |
| PromotionService | GetArtifactEvidence (skanningsbeviset: hashar, varje skanners utslag och fynd, SBOM-dokumentet, policyns ord) | ja (`supply-chain`) | ja | läsplikt (serverns) | Security → artefaktvyn: Integrity, Malware, Vulnerabilities, License, Provenance, SBOM (nedladdning + sammanfattning), Policy | ja | enhet (mockserver) + live i holger | Fullt täckt | — |
| PromotionService | AdvanceArtifact (Approve, Quarantine, Release, Block) | ja (`supply-chain`) | ja | plikter (serverns, `holger-duty-<n>`) | Security → artefaktvyn: fyra knappar för varje artefakt; Block bakom bekräftelse; vägran visas med serverns kod och ord | ja | enhet (mockserver) | Fullt täckt | Ingen rollogik i konsolen: knapparna är desamma för alla |
| AuditService | GetArtifactTrail (artefaktens spår i hashkedjan, med kedjans utslag) | ja | ja | Auditor eller admin (serverns, `holger-audit-10`) | Security → artefaktvyn: Trail-tabellen; vägran visas med serverns kod och ord | ja | enhet (mockserver) | Fullt täckt | — |
| AuditService | VerifyAuditChain (hela kedjan, första brottet) | ja | ja | Auditor eller admin | — | — | — | Endast CLI | Kedjans utslag syns i trail-svaret (`chain_intact`, `chain_summary`); en egen knapp får vänta |
| PromotionService | Lineage/withdrawal/bundles/staging | delvis (lokal motor) | nej (ingen gRPC) | filsystem | — | — | — | Endast CLI | `holger-server promote …`; withdrawal är en append-only statusändring — får UI-yta först med ett typat API |
| ReplicationService | Replicate preview/execute + räknare | ja | ja | admin | Operations-vyn: preview-först, Execute låst till samma par, alla räknare separata, incomplete = röd banner, tidsstämplad | ja | live | Fullt täckt | Ok-svar med partiella fel presenteras aldrig som framgång |
| MigrationService | Discover (plan + refusals + delad dry-run-rapport) | ja (endast `online`-bygge) | ja | admin (serverbred, fail-closed) | Operations → Migrate: formuläret + repotabellen med kryssrutor, disposition, assets/bytes; rapporten renderas av SERVERNS `report`-fält (samma renderare som CLI:ns `--dry-run`) | ja | enhet | Fullt täckt | Läser inget artefaktinnehåll och skriver ingenting |
| MigrationService | Start (jobb-id direkt, plan vägrad INNAN jobbet finns) | ja (endast `online`-bygge) | ja | admin (serverbred, fail-closed) | Start-knappen på det valda urvalet: hela registret = ett jobb, delmängd = ett jobb per repo i tur och ordning (wire-kontraktet har "allt" och "detta ena", inte "dessa fem") | ja | enhet | Fullt täckt | `skip_digest_verification` lämnas false — nollvärdet är det säkra |
| MigrationService | Watch (server-streaming progress) | ja (endast `online`-bygge) | ja | admin (serverbred, fail-closed) | Progress-tabellen: per repo done/skipped/failed/total/bytes + procent; job-loggen | ja | enhet | Fullt täckt | En sen watcher får senaste raden per repo först — reconnect är samma väg |
| MigrationService | Cancel | ja (endast `online`-bygge) | ja | admin (serverbred, fail-closed) | Cancel-knappen, armad bara medan ett jobb löper; stoppar även kön av följande jobb | ja | enhet | Fullt täckt | Avbrutet jobb renderas som "cancelled — a partial migration is on disk", aldrig som framgång |
| MigrationService | Skapa målrepo | **nej — finns inte, kan inte finnas** | — | — | Sidan ERBJUDER det inte: en `create`-rad går inte att kryssa i, den visar RON-deklarationen att klistra in (serverns `declaration`-fält, renderat av `traits::migrate::ron_declaration`) | ja | enhet | Ej runtime-ansluten | `FastRoutes` byggs ur konfigurationen vid boot och är immutabel processen ut. Flödet är Discover → deklarera → starta om → Start, och det är sidans synliga mitt |
| AuditService | Query, SentTo | ja | ja | admin | — (**Ingen klient** i `src/grpc.rs`) | — | — | Ej byggd yta | Medvetet gap: revisionsläsning har ingen vy ännu. Raden finns för att gapet ska vara uttalat, inte tyst |
| AuditService | QueryDownloads (vad laddade C ner: per ident, repo och tid; stinky SB #19) | ja | ja | admin | — (**Ingen klient** i `src/grpc.rs`) | — | — | Endast CLI | Samma medvetna gap som Query/SentTo; HTTP `/-/audit/downloads` finns hos holger |
| UsageService | ReadUsage | ja (alla byggen) | ja | läsare på repot | Repodetaljens Disk-panel och **My plan** (`/plan`): `disk_bytes` mot taket (`caps`) som "1.0 GB of 5.0 GB" + mätare | ja | enhet (mockserver, `mine`) | Fullt täckt | Konsolen räknar inget själv |
| RoomService | Room (länken till ONE-butiken för mer utrymme, med biljett för en admin) | ja | ja | läsare på repot | Repodetaljens More room-panel och **My plan** (`/plan`, "More room in the shop"); en vägran visas med serverns ord | ja | enhet (mockserver, `mine`) | Fullt täckt | Privat Holger-beställning och förnyelsesidan är ONE-butikens (lane P1) |
| AccountService | MyRepositories (personens repon: ägaren ser sina, en admin alla utom systemrepon; formaten servern gör; klientuppsättning för cargo, pip, npm, maven, oci, raw; planens läge: paid/grace/suspended/retention/lapsed, paid until) | ja | ja | öppen (anonym = inga repon, "sign in") | **My repositories** (`/mine`, kort per repo med klientuppsättning och kopieringsknapp) och **My plan** (`/plan`, planens läge och ord) | ja | enhet (mockserver, `mine`) + holger-integrationstest | Fullt täckt | Ägare, behörighet och format är serverns; konsolen visar svaren |
| AccountService | CreateRepository (ett privat repo som en admin gör, ägt av admin, läsningar kräver credential från första anropet) | ja | ja | admin | **My repositories**: formuläret (namn + ett av serverns format), ett anrop, serverns ord eller vägran med kod (`holger-account-1..4`) | ja | enhet (mockserver, `mine`) + holger-integrationstest | Fullt täckt | — |
| TokenService | Mint, List, Revoke (repotoken för pakethanterare, lane P3: hemligheten visas en gång) | ja (när lane P3 landat) | ja | inloggad person (Mint: stepped-up passkey) | **My tokens** (`/tokens`): formulär (etikett, repon, scope, dagar), hemligheten en gång med kopieringsknapp, tabell med Revoke för en levande token | ja | enhet (mockserver, `mine`) | Täckt mot P3:s kontrakt (stub tills P3 landar) | `proto/holger-token.proto` är P3:s utkast; `tests/proto_is_holgers.rs` kräver holgers kopia så fort holger committar filen |
¹ "öppen" = airgap-mirror-posturen (läsning utan credential). Med
`require_auth_for_reads` i serverns RON gäller `reader`-golvet — samma anrop,
samma UI; konsolen visar Unauthenticated som "authentication required" med
login-länk (FailBox-klassificeringen).
## Autentisering och säkerhet
| Service | Backendfunktion | Runtime-ansluten | Webbsäker | Krävd roll | Nuvarande UI-yta | UI-state komplett | Testad | Status | Åtgärd |
|---|---|---:|---:|---|---|---:|---:|---|---|
| (transport) | Bearer-token (StaticToken/API-nyckel) | ja | ja | — | Account: token-paste (maskerad), interceptor | ja | live | Fullt täckt | — |
| (transport) | OIDC access-token-validering (JWKS offline/userinfo) | ja (feature-pinnad per tier) | ja | — | Samma tokenlager | ja | live (mock-IdP) | Fullt täckt | — |
| (klient) | OIDC authorization-code + PKCE | ja | ja | — | Account: SSO-formuläret (`oidc.rs`) | ja | live (mock-IdP, inkl. förfalskad callback) | Fullt täckt | — |
| (transport) | mTLS-klientcert | ja (transportnivå) | delvis | — | — | — | — | Ej lämplig för webb-UI | Webbläsaren/OS:et äger certvalet; WhoAmI rapporterar dock en mTLS-identitet korrekt |
| (backend) | WebAuthn | **nej** (medvetet ej dispatch-ansluten) | — | — | — | — | — | Ej runtime-ansluten | Rörs inte (låst av auth_ship_small-länkrevisionen) |
| (policy) | RBAC-avslag/quarantine/policyfel | ja | ja | — | Ordagrant i FailBox/mutationsstatus | ja | live | Fullt täckt | Ingen klient-RBAC; quarantine får inget existensorakel |
## Utan webbsäkert API — ärligt klassificerat
| Backendfunktion | Runtime-ansluten | Åtkomstväg | Status | Motivering |
|---|---:|---|---|---|
| Delete/raw-radering | ja (native-HTTP-dörren) | `DELETE /{repo}/{path}` med SHA-256-verifiering, egen port | Ej lämplig för webb | Ingen gRPC-RPC; native-dörren är en annan origin för stock-klienter. Typat delete-API = medvetet framtida beslut (Kräver backend-API) |
| Retention/GC | ja (CLI-verb) | `holger-server retention …` (gated dry-run) | Endast CLI | Destruktiv operation utan gRPC-yta; den lokala vägen är avsiktlig |
| Audit-läsning/verify | ja (CLI-verb) | `holger-server audit verify`, Arrow-IPC-segment på disk | Endast CLI | Append-only-loggen exponeras inte över nätverket; mutationsresultat i UI bär tidsstämpel för manuell korrelation mot segmenten |
| Secure-update: bundle/scrub/backup/restore | ja (CLI-verb) | `holger-server bundle/scrub/backup …` | Endast CLI | Medvetet offline/lokalt (airgap-designen) |
| Promotion via lokal CLI | ja | `holger-server promote --from --to` | Endast CLI | In-process-vägen, gated av filrättigheter |
| Provisionering/dev-pair/tjänsteinstallation | ja (CLI) | `holger-server provision/service …` | Startup-konfiguration | Serverinstallation är inte en webbläsaråtgärd |
| Agent/airgap-överföring | ja (egen binär, internetsidan) | `holger-agent` | Ej lämplig för webb | Körs på andra sidan airgapet; ingen server-API-yta |
| Quarantine-gate | ja (env vid serve) | `HOLGER_QUARANTINE_PROPERTY` | Startup-konfiguration | UI visar avslag ordagrant utan existensorakel |
| Jobbstatus/job ledger | **nej** | finns inte | Ej runtime-ansluten | Ingen platshållarsida skapas |
| Repository-konfiguration (RON-grafen) | ja (startup) | RON-fil vid boot | Startup-konfiguration | Ingen mutations-API; degraderat läge syns via ServerProfile/ListRepositories. Per-repo "senaste fel" saknar API — Kräver backend-API om det ska visas |
## Väntetillstånd och inaktuella data
Tabellformade väntan renderar layoutbevarande skeletons (`components/skeleton.rs`):
`aria-hidden` på platshållarna, EN visuellt dold live-region som säger vad som
laddas, och animationen bortkopplad under `prefers-reduced-motion`. Data som
laddas om ligger kvar men **dimmas** (`.stale`) i stället för att ersättas av en
spinner — inaktuella rader ska läsas som inaktuella, inte som aktuella. Små
inline-väntan (SBOM, properties, korten på Översikt) behåller textvarianten;
en skeleton där skulle antyda en form som inte kommer.
## Adversariell granskning 2026-08-14
Fem oberoende linser (honesty, state, a11y, security, correctness) granskade
konsolen; varje fynd prövades av en skeptiker med uppgift att motbevisa det.
Sju fynd överlevde och är åtgärdade — samtliga verifierade live efter fix:
| Fynd | Åtgärd | Verifierat |
|---|---|---|
| Misslyckad artefaktlistning lämnade förra frågans rader kvar, odimmade, med "N shown" | Färsk laddning som avslås tömmer listan; FailBox står ensam | 100 rader → 0 + "failed to list: not found" |
| Repo-browse saknade stale-vakt (repo-byte, filter och Load-more kunde landa i fel ordning) | Samma generationsräknare som sökningen | kompilerad + browse oförändrad i drift |
| Artefaktfakta svalde felet: ett avslag renderades som "—" (påstådd frånvaro) | `Result` med tre lägen; avslag visar serverns ordagranna text | okänt repo → "unknown" + serverns mening |
| Sökningens nollställning ogiltigförklarade inte pågående sökning | Generationen bumpas före tidiga returen | 2 träffar → 0, inget hängande "searching…" |
| IdP:ns avslag i OIDC-callbacken sväljs; engångshemligheten levde vidare | Avslaget visas ordagrant; verifier+state konsumeras oavsett utgång | banner + tomma nycklar |
| `--muted` klarade inte 4.5:1 på NÅGON yta (4.05/3.73/3.50/3.23) | `#8b98ad` — 5.26–6.59:1 överallt | egen kontrasträkning + computed style |
| README/quadlet dokumenterade en publiceringsväg UTAN CSP (markören är en kommentar) | `publish.sh` är enda dokumenterade vägen; kedjekontrollen failar utan CSP-header/fel hash | `verify-chain.sh` grön, alla nio kontroller |
Tre fynd motbevisades och är INTE åtgärdade: att artefaktdetaljens övriga anrop
redan visar avslaget (delvis sant — men inte för feltypat repo, därav fixen
ovan), att repo→repo-ometikettering saknar nåbar väg (sant: ingen länk finns
mellan två repo-sidor — fixen behövdes ändå för det nåbara filter-fallet), och
att scrollbara block skulle sakna tangentbordsåtkomst (fel: `<pre>` utan
fokuserbara barn får UA-fokus, och tabellerna innehåller länkar/knappar).
## Adversariell granskning, runda två (2026-08-14, mot holger 826feae)
Samma fem linser plus en sjätte med uppgift att **attackera de sju fixarna**.
Elva fynd överlevde skeptikern, men fem av dem var samma defekt sedd av fem
oberoende linser — sex distinkta fel, alla åtgärdade och live-verifierade:
| Fynd | Åtgärd | Verifierat |
|---|---|---|
| **Fix 1 gjordes bara på halva ytan**: en avslagen SÖKNING lämnade förra frågans tabeller kvar, odimmade, under den nya frågans chips (funnen av 5 linser, en satte HIGH) | `results.set(None)` på felgrenen — samma regel som browse | transportfel injicerat: 2 rader → 0, inga "Artifacts (n)"-rubriker, felet ensamt |
| `.stale`-dimningen upphävde fix 6: group-opacity komponerar TEXTEN → th 2.58:1, länkar 2.91:1 (under både 4.5 och 3.0) | dimningen ersatt av en "refreshing…"-ribba + `aria-busy`; färgerna orörda | egen räkning: 5.71 / 6.98 / 16.19 / 5.99 — allt över AA; opacity mätt till 1 under laddning |
| Fix 3 ofullständig: "—" påstod frånvaro även när koordinaten låg bortom första sidan | fyra lägen (`Found` / `Absent` / `Undetermined` / fel) — "—" bara när sidan var komplett | Found "8 B", Absent "—" utan fel, fel → "unknown" + serverns mening |
| Avslagen property-SKRIVNING skrev över LÄS-tillståndet och etiketterades som misslyckad hämtning; ingen busy-vakt | eget skrivtillstånd; listan står kvar; knappar låsta under skrivning | anonym skrivning: listan `["stage"]` intakt, "property write refused: …", och två extra klick gav ETT anrop |
| `publish.sh` körde `chmod -R a+rX` över hela publiceringskatalogen — datalagret, promote-seglen och audit-segmenten ligger under den | chmod skopad till `dist` + `nginx.conf` (det quadleten faktiskt monterar) | 282 världsläsbara filer → 0; store `drwxr-x---`; nginx serverar fortsatt 200 |
| Trunkeringsvarningarna (sök + replikering) saknade live-region | `role="alert"`, som de övriga banners | attributet i källan; inte live-exercerat (kräver >200 träffar) |
Ett fynd motbevisades (properties-hämtningen skulle sakna generationsvakt).
**Inte live-exercerat, medvetet redovisat:** `Facts::Undetermined` kan inte nås
med nuvarande data — faktafrågan filtrerar på det exakta artefaktnamnet, som
matchar en enda post; läget är kompilatorkontrollerat och kodgranskat, inte
kört. Detsamma gäller trunkeringsbannern (kräver >200 sökträffar).
## Verifieringsnoteringar
- Åtta tjänster registreras i `start_grpc_endpoint` — kontrollerat mot koden;
`tests/contract.rs` låser antalet och kräver en klient per tjänst i
`src/grpc.rs` (gapet var verkligt: ArchiveService saknade klient).
- Tom `ListArtifacts` är INTE bevis på tomt repo (holger-repots ai-context
varnar uttryckligen); tomläget formuleras därefter, och "inga filterträffar"
är ett eget tomläge skiljt från "inga repositories".
- **Arkiv-vyn fullverifierad mot riktigt arkiv:** writable rust-znippy-repo
seedad via konsolens egen upload; badge-övergång, `ArchiveInfo`
(`3 files · 54 B · crates-dev`, exakt), fillistning + prefix-filter — och
verifieringen hittade en riktig backend-bugg (fixad i holger `906e748` med
regressionstestet `writable_repo_archive_info_reports_live_stats`).
- **Nativ enhetstestyta:** kratsen är `cfg(target_arch = "wasm32")` så när som på
`src/util.rs`, så vyernas RENA beslut ligger där och `cargo test` täcker dem utan
webbläsarharness: sökens och repolistans adressbyggen (escaping, chip-borttagning,
sorteringsväxling), marketplace-statusens rubrik/ton/banner, nedladdningsnamnet och
property-värdena på detaljvyn, replikeringens preview-grind, gRPC-dörrens bas-URL
och miljöchipet, FailBox-klassificeringen (total över alla 17 statuskoder) samt
PKCE-delarna: base64url (RFC 4648 §5, opaddad), discovery-URL, redirect-URI,
authorize-parametrarna (S256, och CHALLENGE — aldrig verifiern) och
token-formuläret (verifier, ingen client secret).
- **WhoAmI:** introspektion genom SAMMA `auth::validate_request` som gaterna
(ingen tvilling); ogiltig credential rapporteras som anonym, aldrig fel;
backendtestet `who_am_i_reports_anonymous_and_mapped_roles` låser formen.