Rozszerzenie rdzenia z model-danych o warstwy wymagane przez audyt (§4.2 brakujące moduły, §4.3 nowe tabele). Domyka luki: tożsamość i uprawnienia, ślad audytowy, retencję, podstawę prawną przetwarzania, wielodostępność, notyfikacje, prowadzenie sprawy, wersjonowanie dokumentów, akceptację ryzyka oraz ścieżkę odwoławczą. To specyfikacja docelowego schematu — nie działający backend.
Tabele incidents, evidence, classifications, playbook_runs pochodzą z rdzenia. Ta strona dodaje warstwy okołordzeniowe i dopina je kluczami obcymi (FK) do incidents(id) i evidence(id). Zasada nadrzędna pozostaje: claim ≤ proof — żaden rekord twierdzenia nie istnieje bez powiązanego dowodu.
Audyt wskazał dziesięć modułów, których rdzeń nie pokrywa. Każdy jest warunkiem dojrzałości GRC/SOC i zgodności (NIS2, RODO, AI Act). Wszystkie mają status implementacji ROADMAP.
| Moduł | Po co (uzasadnienie) | Główne tabele | Status |
|---|---|---|---|
| IAM — tożsamość i dostęp | Kto może czytać/zmieniać incydent i dowód. Uwierzytelnianie, role, uprawnienia, MFA. Warunek rozliczalności każdego wpisu. | users, roles_permissions, organizations | ROADMAP |
| Audit Log — ślad audytowy | Niezmienialny zapis „kto, co, kiedy, na jakim rekordzie". Dowód dla KNF/CSIRT, wykrywanie manipulacji statusem. | audit_events | ROADMAP |
| Data Retention — retencja | Polityki przechowywania i usuwania (RODO art. 5 ust. 1 lit. e, minimalizacja). Automatyczne wygaszanie danych po terminie. | data_processing_records, pola retention_until | ROADMAP |
| Consent / Legal Basis — podstawa prawna | Podstawa przetwarzania każdego zbioru danych osobowych (RODO art. 6/9). Rejestr zgód i podstaw dla zgłaszających i poszkodowanych. | data_processing_records, legal_assessments | ROADMAP |
| Tenant / Organizations — wielodostępność | Izolacja danych między podmiotami (bank A nie widzi banku B). Warunek wdrożenia SaaS dla wielu instytucji. | organizations | ROADMAP |
| Notification Engine — notyfikacje | Powiadomienia o zegarach raportowych (24h/72h NIS2), eskalacjach P0/P1, wygaśnięciu SLA. Kanały: e-mail, webhook. | notifications | ROADMAP |
| Case Management — prowadzenie sprawy | Grupowanie powiązanych incydentów w sprawę, przypisania, statusy postępowania, kolejka analityka. | disputes, rozszerzenia incidents | ROADMAP |
| Document Versioning — wersjonowanie | Historia zmian raportów, playbooków, ocen prawnych. Kto i kiedy zmienił, poprzednia wersja, hash. Integralność dowodowa. | documents | ROADMAP |
| Risk Acceptance — akceptacja ryzyka | Świadoma decyzja o zaakceptowaniu ryzyka rezydualnego (kto zaakceptował, uzasadnienie, termin przeglądu). Ślad odpowiedzialności. | legal_assessments, pola risk_accepted_by | ROADMAP |
| Appeals / Dispute Path — ścieżka odwoławcza | Kwestionowanie klasyfikacji lub statusu dowodu. Rejestr sporów, rozstrzygnięcia, zgodność z zasadą claim ≤ proof. | disputes, truth_claims | ROADMAP |
Poniżej szkielety CREATE TABLE z kluczowymi kolumnami i kluczami obcymi do rdzenia (incidents(id), evidence(id)). Typy poglądowe (PostgreSQL). To projekt schematu — nie migracja uruchomiona na bazie produkcyjnej.
CREATE TABLE organizations ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), public_id TEXT UNIQUE NOT NULL, -- np. ORG-BANK-001 name TEXT NOT NULL, kind TEXT NOT NULL, -- bank | podmiot_kluczowy | regulator | csirt tenant_scope TEXT NOT NULL DEFAULT 'isolated', created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), org_id UUID NOT NULL REFERENCES organizations(id), email TEXT UNIQUE NOT NULL, display_name TEXT, status TEXT NOT NULL DEFAULT 'active', -- active|suspended|revoked mfa_enabled BOOLEAN NOT NULL DEFAULT false, last_login_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE roles_permissions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id), org_id UUID NOT NULL REFERENCES organizations(id), role TEXT NOT NULL, -- analyst|soc_lead|dpo|auditor|admin|reporter permission TEXT NOT NULL, -- incident.read|incident.write|evidence.verify|legal.assess... granted_by UUID REFERENCES users(id), granted_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (user_id, role, permission) );
CREATE TABLE audit_events ( id BIGSERIAL PRIMARY KEY, actor_user_id UUID REFERENCES users(id), org_id UUID REFERENCES organizations(id), action TEXT NOT NULL, -- create|update|status_change|verify|close|export entity_type TEXT NOT NULL, -- incident|evidence|classification|legal_assessment entity_id UUID NOT NULL, -- FK logiczny; dla incydentu -> incidents(id) incident_id UUID REFERENCES incidents(id), -- kontekst nadrzędny, gdy dotyczy before_hash TEXT, -- SHA-256 stanu sprzed zmiany after_hash TEXT, -- SHA-256 stanu po zmianie ip_addr INET, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- append-only: brak UPDATE/DELETE (egzekwowane politykami i rolą DB)
CREATE TABLE legal_assessments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), incident_id UUID NOT NULL REFERENCES incidents(id), framework TEXT NOT NULL, -- ai_act|nis2|ksc|rodo obligation TEXT, -- art.73|24h|72h|art.33|art.34 duty_triggered BOOLEAN NOT NULL DEFAULT false, deadline_at TIMESTAMPTZ, risk_accepted BOOLEAN NOT NULL DEFAULT false, risk_accepted_by UUID REFERENCES users(id), rationale TEXT, assessed_by UUID REFERENCES users(id), created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE data_processing_records ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), incident_id UUID REFERENCES incidents(id), data_category TEXT NOT NULL, -- osobowe|wrazliwe|techniczne|ioc legal_basis TEXT NOT NULL, -- rodo_6_1_c|rodo_6_1_f|rodo_9_2_g|zgoda consent_ref TEXT, -- odnośnik do zgody, gdy podstawa=zgoda purpose TEXT NOT NULL, retention_until TIMESTAMPTZ, -- termin usunięcia (retencja) controller_org_id UUID REFERENCES organizations(id), created_at TIMESTAMPTZ NOT NULL DEFAULT now() );
CREATE TABLE notifications ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), incident_id UUID REFERENCES incidents(id), recipient_id UUID REFERENCES users(id), channel TEXT NOT NULL, -- email|webhook|inapp kind TEXT NOT NULL, -- sla_breach|clock_24h|clock_72h|escalation_p0 payload JSONB, status TEXT NOT NULL DEFAULT 'queued', -- queued|sent|failed sent_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now() );
CREATE TABLE truth_claims ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), incident_id UUID NOT NULL REFERENCES incidents(id), claim_text TEXT NOT NULL, proof_evidence_id UUID REFERENCES evidence(id), -- claim <= proof: brak dowodu => status GAP confidence INT CHECK (confidence BETWEEN 0 AND 100), status TEXT NOT NULL DEFAULT 'gap', -- gap|confirmed|disputed created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE disputes ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), incident_id UUID NOT NULL REFERENCES incidents(id), claim_id UUID REFERENCES truth_claims(id), raised_by UUID REFERENCES users(id), reason TEXT NOT NULL, -- klasyfikacja|status_dowodu|priorytet resolution TEXT, resolved_by UUID REFERENCES users(id), status TEXT NOT NULL DEFAULT 'open', -- open|upheld|rejected created_at TIMESTAMPTZ NOT NULL DEFAULT now() );
CREATE TABLE documents ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), incident_id UUID REFERENCES incidents(id), kind TEXT NOT NULL, -- raport|ocena_prawna|playbook_snapshot version INT NOT NULL DEFAULT 1, parent_doc_id UUID REFERENCES documents(id), -- poprzednia wersja content_hash TEXT NOT NULL, -- SHA-256 treści author_id UUID REFERENCES users(id), created_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (incident_id, kind, version) );
CREATE TABLE victims ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), incident_id UUID NOT NULL REFERENCES incidents(id), kind TEXT NOT NULL, -- osoba|organizacja|system sector TEXT, -- finanse|zdrowie|energetyka... impact_note TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE damages ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), incident_id UUID NOT NULL REFERENCES incidents(id), victim_id UUID REFERENCES victims(id), damage_type TEXT NOT NULL, -- finansowy|dane|reputacja|ciaglosc estimate_pln NUMERIC(14,2), is_confirmed BOOLEAN NOT NULL DEFAULT false, evidence_id UUID REFERENCES evidence(id), created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE actors ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), incident_id UUID NOT NULL REFERENCES incidents(id), actor_type TEXT NOT NULL, -- apt|insider|automat|nieznany alias TEXT, attribution_confidence INT CHECK (attribution_confidence BETWEEN 0 AND 100), evidence_id UUID REFERENCES evidence(id), created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE sources ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), incident_id UUID NOT NULL REFERENCES incidents(id), source_type TEXT NOT NULL, -- osint|siem|cert|zgloszenie|media url TEXT, reliability TEXT, -- A-F (skala NATO admiralty) captured_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now() );
CREATE TABLE training_records ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id), module TEXT NOT NULL, -- evidence-first|playbook|ai-risk passed BOOLEAN NOT NULL DEFAULT false, score INT CHECK (score BETWEEN 0 AND 100), completed_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE vulnerabilities ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), incident_id UUID REFERENCES incidents(id), cve_id TEXT, -- np. CVE-2026-XXXX cvss NUMERIC(3,1), affected_asset TEXT, status TEXT NOT NULL DEFAULT 'open', -- open|patched|mitigated evidence_id UUID REFERENCES evidence(id), created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE researchers ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), handle TEXT UNIQUE NOT NULL, org_id UUID REFERENCES organizations(id), vdp_accepted BOOLEAN NOT NULL DEFAULT false, -- akceptacja VDP/RoE reputation INT DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE third_party_dependencies ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), incident_id UUID REFERENCES incidents(id), vendor TEXT NOT NULL, component TEXT, -- biblioteka|usluga|dostawca risk_level TEXT, -- low|medium|high|critical sbom_ref TEXT, -- odnośnik do SBOM created_at TIMESTAMPTZ NOT NULL DEFAULT now() );
truth_claims egzekwuje doktrynę claim ≤ proof na poziomie danych: rekord bez proof_evidence_id ma status gap i nie może zostać oznaczony jako confirmed. audit_events jest append-only — status incydentu nie może zmienić się bez śladu. To nie jest jeszcze wdrożone (GAP), to wymóg projektowy dla implementacji.
incidents, evidence). Zero payloadów ofensywnych — to schemat danych GRC, nie narzędzie ataku.