Relational schema (PostgreSQL) for the five core tables: incidents, evidence, response_actions, assets, agents. The evidence-first doctrine is encoded in the column types โ evidentiary status, confidence level and chain of custody are fields, not conventions.
evidence.incident_id โ incidents.id ยท response_actions.incident_id โ incidents.id ยท incidents.asset_id โ assets.id ยท incidents.agent_id โ agents.id. Deleting an incident cascades to its evidence and actions (ON DELETE CASCADE); links to assets/agents are restrictive (ON DELETE RESTRICT).
incidents โ event coreThe central table. It carries the public identifier, type and level (L1โL4), severity, evidentiary status, priority and a full set of legal flags that drive reporting obligations.
CREATE TYPE evidence_status AS ENUM
('confirmed','media_signal','public_claim','gap','disputed','simulation','internal');
CREATE TYPE incident_priority AS ENUM ('P0','P1','P2','P3');
CREATE TYPE incident_level AS ENUM ('L1_cyber','L2_ai','L3_legal','L4_resilience');
CREATE TABLE incidents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
public_id TEXT UNIQUE NOT NULL, -- e.g. INC-2026-0042
title TEXT NOT NULL,
summary TEXT,
source_type TEXT NOT NULL, -- form | osint | siem_log | cert | vendor | api
level incident_level NOT NULL,
category TEXT NOT NULL, -- phishing | ransomware | prompt_injection ...
evidence_status evidence_status NOT NULL DEFAULT 'gap',
severity SMALLINT NOT NULL DEFAULT 0 CHECK (severity BETWEEN 0 AND 10),
priority incident_priority,
-- legal flags (drive the Legal/Compliance Engine)
ai_act_relevant BOOLEAN NOT NULL DEFAULT false,
ai_high_risk BOOLEAN NOT NULL DEFAULT false,
ai_serious_incident BOOLEAN NOT NULL DEFAULT false, -- AI Act art. 73
gdpr_personal_data BOOLEAN NOT NULL DEFAULT false,
gdpr_breach BOOLEAN NOT NULL DEFAULT false, -- GDPR art. 33/34
nis2_relevant BOOLEAN NOT NULL DEFAULT false, -- 24h/72h/final
ksc_relevant BOOLEAN NOT NULL DEFAULT false, -- national cybersecurity act (PL KSC)
critical_infra BOOLEAN NOT NULL DEFAULT false,
law_enforcement BOOLEAN NOT NULL DEFAULT false,
-- impact assessment
impact_confidentiality SMALLINT DEFAULT 0 CHECK (impact_confidentiality BETWEEN 0 AND 3),
impact_integrity SMALLINT DEFAULT 0 CHECK (impact_integrity BETWEEN 0 AND 3),
impact_availability SMALLINT DEFAULT 0 CHECK (impact_availability BETWEEN 0 AND 3),
impact_scope TEXT, -- scope description
affected_users INTEGER,
-- links and lifecycle
asset_id UUID REFERENCES assets(id) ON DELETE RESTRICT,
agent_id UUID REFERENCES agents(id) ON DELETE RESTRICT,
status_lifecycle TEXT NOT NULL DEFAULT 'open', -- open | triage | contained | resolved | closed
detected_at TIMESTAMPTZ,
reported_at TIMESTAMPTZ NOT NULL DEFAULT now(),
sla_due_at TIMESTAMPTZ, -- derived from priority
closed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_incidents_priority ON incidents(priority) WHERE status_lifecycle <> 'closed';
CREATE INDEX idx_incidents_flags ON incidents(nis2_relevant, gdpr_breach, ai_serious_incident);
Notes. evidence_status defaults to gap โ a new incident is not a fact until evidence promotes its status. sla_due_at is computed from priority (P0 โ +4h, P1 โ +24h, P2 โ +72h, P3 โ +7โ30 days). Legal flags are inputs to the Legal Board and the reporting clocks. These reporting windows are illustrative of the NIS2 / GDPR / AI Act frameworks, not legal advice.
evidence โ evidence layerMany pieces of evidence per incident. SHA-256 hash, confidence level 0โ100, chain of custody as JSONB, visibility control.
CREATE TYPE evidence_kind AS ENUM
('url','screenshot','file_hash','log','ioc','cve','cert_bulletin','enisa_report',
'vendor_advisory','upo_edelivery','model_prompt','model_output','agent_trace','hitl_decision');
CREATE TABLE evidence (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
incident_id UUID NOT NULL REFERENCES incidents(id) ON DELETE CASCADE,
kind evidence_kind NOT NULL,
status evidence_status NOT NULL DEFAULT 'gap',
source TEXT, -- url / system / author
content_ref TEXT, -- pointer to artifact / blob
hash_sha256 CHAR(64), -- material integrity
confidence SMALLINT NOT NULL DEFAULT 0 CHECK (confidence BETWEEN 0 AND 100),
chain_of_custody JSONB NOT NULL DEFAULT '[]', -- [{ts,actor,action,hash_before,hash_after,note}]
visibility TEXT NOT NULL DEFAULT 'internal', -- public | restricted | internal
collected_by UUID, -- users.id
collected_at TIMESTAMPTZ NOT NULL DEFAULT now(),
verified_by UUID,
verified_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_evidence_incident ON evidence(incident_id);
CREATE INDEX idx_evidence_hash ON evidence(hash_sha256);
Notes. The aggregate of evidence statuses determines incidents.evidence_status (the strongest confirmed piece of evidence raises the incident to confirmed). A hash_sha256 mismatch at verification moves the evidence to disputed. visibility enforces role-based exposure โ internal never reaches the public view.
response_actions โ response stepsPlaybook steps executed against an incident. They record the performer, the human-in-the-loop approval and the validation outcome.
CREATE TABLE response_actions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), incident_id UUID NOT NULL REFERENCES incidents(id) ON DELETE CASCADE, playbook_type TEXT NOT NULL, -- phishing | ransomware | prompt_injection ... step_no SMALLINT NOT NULL, action TEXT NOT NULL, -- step description action_type TEXT NOT NULL, -- contain | eradicate | recover | notify | validate status TEXT NOT NULL DEFAULT 'pending', -- pending | in_progress | done | failed | skipped requires_hitl BOOLEAN NOT NULL DEFAULT false, -- requires human approval (P0/P1) approved_by UUID, -- users.id (human-in-the-loop) approved_at TIMESTAMPTZ, performed_by UUID, performed_at TIMESTAMPTZ, validation_ref UUID REFERENCES evidence(id), -- evidence validating the step result_note TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_actions_incident ON response_actions(incident_id, step_no);
Notes. requires_hitl=true blocks execution of a destructive step without approved_by (the human-in-the-loop rule for P0/P1 โ see Roles & priorities). validation_ref binds the step to evidence of remediation โ without it the incident cannot transition to closed.
assets โ protected resourcesSystems, services and data under protection. Criticality classification and membership in critical infrastructure.
CREATE TABLE assets ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, asset_type TEXT NOT NULL, -- app | service | db | endpoint | network | data_store owner TEXT, -- business owner environment TEXT NOT NULL DEFAULT 'prod', -- prod | staging | sandbox | simulation criticality SMALLINT NOT NULL DEFAULT 1 CHECK (criticality BETWEEN 1 AND 5), critical_infra BOOLEAN NOT NULL DEFAULT false, handles_personal_data BOOLEAN NOT NULL DEFAULT false, cvss_exposure NUMERIC(3,1), -- highest exploitable CVSS segment TEXT, -- network segment created_at TIMESTAMPTZ NOT NULL DEFAULT now() );
Notes. environment enforces segregation (prod / staging / sandbox / simulation). An incident on an asset with critical_infra=true automatically proposes the nis2_relevant and ksc_relevant flags. handles_personal_data โ proposes gdpr_personal_data. These proposals are decision support, not an automated legal determination.
agents โ AI agents and reputationA registry of AI agents with DID identity and a reputation model (baseline / current / delta) plus an allowlist of tools โ the basis for quarantine on takeover.
CREATE TABLE agents ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, did TEXT UNIQUE, -- decentralized agent identifier model_ref TEXT, -- model / version owner TEXT, baseline_score SMALLINT NOT NULL DEFAULT 100 CHECK (baseline_score BETWEEN 0 AND 100), current_score SMALLINT NOT NULL DEFAULT 100 CHECK (current_score BETWEEN 0 AND 100), trust_delta SMALLINT GENERATED ALWAYS AS (current_score - baseline_score) STORED, allowed_tools JSONB NOT NULL DEFAULT '[]', -- list of allowed tools/permissions status TEXT NOT NULL DEFAULT 'active', -- active | quarantined | revoked quarantined_at TIMESTAMPTZ, last_seen_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_agents_status ON agents(status);
Notes. trust_delta is a computed column (STORED): a drop in reputation relative to baseline signals an anomaly. Detection of prompt injection or agent hijack sets status='quarantined' and drops effective permissions regardless of allowed_tools. did binds the agent's identity to the trust layer (cf. the agent hijack playbook). Detection scenarios are documented as SIMULATION โ methodology mapped to MITRE ATT&CK, no payloads.
| From table | FK column | To table | ON DELETE |
|---|---|---|---|
| evidence | incident_id | incidents | CASCADE |
| response_actions | incident_id | incidents | CASCADE |
| response_actions | validation_ref | evidence | SET NULL |
| incidents | asset_id | assets | RESTRICT |
| incidents | agent_id | agents | RESTRICT |
gap, a required validation_ref to close, an append-only chain_of_custody. The database does not let you pretend a fact exists without proof. The API contract is described on the API page.