Skip to content

Data Model

The key tables are the following:

  • attributes
  • domains
  • frameworks
  • controls
  • measures
  • documents
  • risks
  • actions
  • exceptions

controls = security measures (requirements to implement).
measures = audit instances (periodic verifications of those requirements).
risks = information security risks (ISO 27001 §6.1.2 register).
actions = action plans (remediation of failed measurements and risks).
exceptions = exceptions (temporary waivers to a security measure).

Table Dependencies

Overview: who uses what.

flowchart LR
    controls --> |"domain_id (0:1)"| domains
    domains -.->|"framework = code"| frameworks
    controls <-->|"control_measure (N:N)"| measures
    attributes -.-o |"optional"| controls
    attributes -.-o |"optional"| measures
    measures -->|"measure_id (0:1)"| measures
    documents -->|"document_id (1:1)"| measures
    risks -->|"owner_id (0:1)"| users
    risks <-->|"control_risk (N:N)"| controls
    actions -->|"measure_id (0:1)"| measures
    actions <-->|"action_control (N:N)"| controls
    actions <-->|"action_risk (N:N)"| risks
    actions <-->|"action_user (N:N)"| users
    exceptions -->|"control_id (0:1)"| controls
    exceptions -->|"created_by, submitted_by, approved_by (0:1)"| users

The detailed schema below describes the fields of each table.

erDiagram
    domains ||--o{ controls : "domain_id"
    frameworks |o--o{ domains : "code = framework"
    controls }o--o{ measures : "many-to-many"
    attributes }o--o{ controls : "optional"
    attributes }o--o{ measures : "optional"
    measures o|--o| measures : "next_id"
    documents }o--o| measures : "optional"
    risks }o--o{ controls : "control_risk"
    users ||--o{ risks : "owner_id"
    measures |o--o{ actions : "measure_id"
    actions }o--o{ controls : "action_control"
    actions }o--o{ risks : "action_risk"
    actions }o--o{ users : "action_user"
    controls |o--o{ exceptions : "control_id"
    users |o--o{ exceptions : "created_by, approved_by"

    domains {
        int id PK
        string framework
        string title
        string description
    }
    frameworks {
        int id PK
        string code UK
        string name
        string version
        string publisher
    }
    attributes {
        int id PK
        string name
        string values
    }
    controls {
        int id PK
        int domain_id FK
        string name
        string clause
        string objective
        array measures
        array attributes
        array risks
    }
    measures {
        int id PK
        int next_id FK
        string name
        int periodicity
        date plan_date
        date realisation_date
        int status
        array controls
        array attributes
    }
    documents {
        int id PK
        int measure_id FK
    }
    risks {
        int id PK
        int owner_id FK
        string name
        string status
        int probability
        int impact
        int review_frequency
        date next_review_at
        array controls
    }
    actions {
        int id PK
        int measure_id FK
        string reference
        int type
        int status
        int progress
        date due_date
        date close_date
    }
    exceptions {
        int id PK
        int control_id FK
        string name
        int status
        date start_date
        date end_date
        int created_by FK
        int approved_by FK
    }

The relationships are as follows:

Link Type Description
domains → controls Foreign key (1:N) Each security measure references its domain via domain_id
frameworks → domains Optional (1:N) via the code A domain's framework field holds the code of its framework; the frameworks row adds its metadata
controls ↔ measures Many-to-many (bidirectional) Each security measure lists its audit instances in measures[]; each audit instance lists its security measures in controls[]
attributes → controls Optional The attributes field of a security measure may contain a list of attribute IDs
attributes → measures Optional Same for audit instances
measures → measures Self-reference via next_id Allows chaining successive campaigns of the same audit
documents → measures Optional (1:N) Documents and evidence are attached to audit instances via measure_id
risks ↔ controls Many-to-many via control_risk A mitigated risk must be linked to one or more security controls; the list is stored in controls[] on the risk
users → risks Optional (1:N) Each risk can be assigned an owner (owner_id) responsible for the periodic review
measures → actions Optional (1:N) An action plan created when making a measurement references it via measure_id
actions ↔ controls Many-to-many via action_control Security measures (clauses) concerned by the action plan
actions ↔ risks Many-to-many via action_risk Action plans treating a risk; required for a Not accepted risk
actions ↔ users Many-to-many via action_user Owners of the action plan
controls → exceptions Optional (1:N) Each exception references the security measure it waives via control_id
users → exceptions Optional (1:N) Author (created_by), submitter (submitted_by) and reviewer (approved_by) of the exception

Note: There is no exposed join table for the controls/measures relationship.
IDs are directly embedded in each object on both sides.
The control_risk pivot table links risks to security controls.
The action_control, action_risk and action_user pivot tables link action plans to security measures, risks and owners.


attributes

Attributes are multi-value classification reference sets.
Each attribute defines a set of tags (prefixed with #) that can be associated with security measures and audit instances.

Field Type Description
id integer Unique identifier (PK)
name string Name of the taxonomy (e.g., Security measures, Risk_Level)
values string List of possible values separated by spaces, each prefixed with # (e.g., #Preventive #Detective #Corrective)
created_at datetime Creation date (ISO 8601, UTC)
updated_at datetime Last modification date

Example:

{
  "id": 1,
  "name": "Security measures",
  "values": "#Preventive #Detective #Corrective",
  "created_at": "2026-05-17T20:35:52.000000Z",
  "updated_at": "2026-05-17T20:35:52.000000Z"
}

domains

Domains group security measures by thematic area.
Each domain belongs to a regulatory or methodological framework (framework).

Field Type Description
id integer Unique identifier (PK)
framework string Reference framework (e.g., NIS2, Vulnerability Management)
title string Domain name (e.g., NIS2 Governance and Steering)
description string Description of the scope covered, often with reference to an article or standard
created_at datetime Creation date
updated_at datetime Last modification date

Example:

{
  "id": 1,
  "framework": "NIS2",
  "title": "NIS2 Governance and Steering",
  "description": "Strategic and operational steering according to Art. 21.1 and 21.2.a",
  "created_at": "2026-05-17T20:35:52.000000Z",
  "updated_at": "2026-05-17T20:35:52.000000Z"
}

frameworks

The frameworks table adds metadata to the framework codes used in domains.framework. It does not replace that field: a framework is linked to its domains, and through them to its security measures, by its code. A row is created automatically, with the code as its name, when a domain is saved with a new framework code. The crosswalks use these codes to select the source and target frameworks.

Field Type Description
id integer Unique identifier (PK)
code string Framework code, identical to domains.framework (unique, e.g., 27001:2022)
name string Name of the framework (the code by default)
version string Version of the framework (empty by default)
publisher string Publisher of the framework (empty by default)
jurisdiction string | null Jurisdiction in which the framework applies
source_url string | null URL of the official source
status string | null Status of the framework (free text)
publication_date date | null Publication date
effective_date date | null Effective date
notes string | null Free notes
created_at datetime Creation date
updated_at datetime Last modification date

Example:

{
  "id": 2,
  "code": "NIS2-ReCyF-2.5-FR",
  "name": "ReCyF 2.5",
  "version": "2.5",
  "publisher": "ANSSI",
  "jurisdiction": "France",
  "source_url": "https://messervices.cyber.gouv.fr/nis2#exigences",
  "status": null,
  "publication_date": null,
  "effective_date": null,
  "notes": null
}

controls

Security measures describe the requirements to be implemented.
Each security measure belongs to a domain and is verified by one or more audit instances.

Field Type Description
id integer Unique identifier (PK)
domain_id integer Reference to domains.id (FK, required)
name string Name of the security measure, often with article number (e.g., Art.21.2.a – Risk Analysis)
clause string Short identifier of the normative clause (e.g., NIS2-Art.21.2.a)
objective string Expected objective of this security measure
input string | null Data or resources required for implementation
model string | null Recommended operational model or method
indicator string | null Structured performance indicator (Target, Frequency, Owner)
action_plan string | null Associated action or treatment plan
attributes array | null List of associated attribute IDs; null if none
measures array List of audit instance IDs verifying this security measure
created_at datetime Creation date
updated_at datetime Last modification date

Example:

{
  "id": 1,
  "domain_id": 1,
  "name": "Art.21.2.a - Risk Analysis",
  "clause": "NIS2-Art.21.2.a",
  "objective": "Assessment of threats to critical assets using EBIOS RM or equivalent methodology",
  "input": "List of critical assets, EBIOS RM methodology",
  "model": "Annual analysis according to ISO 27005 or EBIOS RM",
  "indicator": "Target: Residual score ≤ acceptable | Frequency: Annual | Owner: CISO",
  "action_plan": "Risk treatment plan approved by Management",
  "attributes": null,
  "measures": [1]
}

measures

Audit instances describe periodic operational verifications.
An audit instance checks whether one or more security measures are properly applied.
It contains planning, execution, and result data.

Field Type Description
id integer Unique identifier (PK)
name string Title of the verification
objective string | null Specific objective of this audit instance
input string | null Data or evidence required for execution
model string | null Operating procedure
action_plan string | null Corrective actions if the audit fails
periodicity integer | null Frequency in months (e.g., 12 = annual, 3 = quarterly)
plan_date date | null Planned execution date (YYYY-MM-DD)
realisation_date date | null Actual execution date
observations string | null Free comments on the result
score number | null Numeric score from the evaluation; null if not yet performed
note number | null Additional qualitative note
status integer Current status of the audit instance (see below)
next_id integer | null ID of the next audit instance in the historical chain (self FK)
attributes array | null List of associated attribute IDs; null if none
scope string | null Scope of application (entity, site, system)
controls array List of security measure IDs verified by this audit instance
created_at datetime Creation date
updated_at datetime Last modification date

Values of the status field

Value Meaning
0 To do / Not yet performed (realisation_date is null)
1 Proposed (auditee submitted a result, pending validation)
2 Done / Completed (realisation_date is set)

Example:

{
  "id": 1,
  "name": "Formal review and signature of the risk analysis",
  "objective": "Management validation of the risk treatment strategy",
  "model": "Executive Committee presentation + formal signature",
  "periodicity": 12,
  "plan_date": "2026-07-31",
  "realisation_date": "2025-03-25",
  "score": null,
  "status": 2,
  "next_id": null,
  "attributes": null,
  "scope": null,
  "controls": [1]
}

documents

The documents table stores attachments and documentary evidence associated with audit instances.
Each document is linked to a measures record via measure_id.


risks

The risk register records information security risks in accordance with ISO 27001:2022 §6.1.2 and §8.2.
Each risk is assessed using a configurable scoring method, optionally assigned to an owner, and subject to a periodic review cycle.
A risk with Mitigated status must be linked to at least one security control via the control_risk pivot table.

Field Type Description
id integer Unique identifier (PK)
name string Short, identifiable label for the risk (required)
description string | null Detailed description of the risk
owner_id integer | null Reference to users.id — person responsible for the periodic review
probability integer Likelihood level (1 to N, standard formulas), or threat level with the MONARC formula
probability_comment string | null Free comment on the probability assessment
impact integer Severity level if the risk materialises (1 to N)
impact_comment string | null Free comment on the impact assessment
exposure integer | null System accessibility (BSI 200-3 formula only: 0 = off-network, 1 = internal, 2 = Internet)
vulnerability integer | null Exploitability of known weaknesses (BSI 200-3 formula), or vulnerability level (MONARC formula)
residual_risk integer | null Residual risk entered manually (MONARC formula only)
status enum Treatment decision (see values below)
status_comment string | null Free comment on the treatment decision
review_frequency integer Interval in months between two reviews (default: 12)
next_review_at date | null Scheduled date of the next review
controls array List of security control IDs linked to this risk (via control_risk)
created_at datetime Creation date
updated_at datetime Last modification date
deleted_at datetime | null Soft-deletion date

Values of the status field

Value Meaning
not_evaluated Not yet assessed
not_accepted Not accepted — a linked action plan is required
temporarily_accepted Temporarily accepted
accepted Accepted
mitigated Mitigated — at least one linked security control is required
transferred Transferred (insurance, third party)
avoided Avoided

Example:

{
  "id": 1,
  "name": "Unauthorised access to sensitive data",
  "description": "Risk of data exfiltration via compromised credentials.",
  "owner_id": 3,
  "probability": 3,
  "impact": 4,
  "exposure": null,
  "vulnerability": null,
  "residual_risk": null,
  "status": "mitigated",
  "status_comment": "Access control policy deployed in Q1.",
  "review_frequency": 12,
  "next_review_at": "2027-06-01",
  "controls": [12, 15],
  "created_at": "2026-06-09T10:00:00.000000Z",
  "updated_at": "2026-06-09T10:00:00.000000Z",
  "deleted_at": null
}

actions

The actions table contains the action plans put in place to remedy a failed measurement, a non-conformity or a risk. An action plan can be created manually or when making a measurement; it is then linked to this measurement via measure_id. The security measures, risks and owners of an action plan are stored in the action_control, action_risk and action_user pivot tables.

Field Type Description
id integer Unique identifier (PK)
reference string | null Reference of the action plan (e.g., ACT-2026-001, 32 characters max)
type integer Type of action plan (see values below)
status integer Status of the action plan (see values below)
progress integer | null Progress in percent (0 to 100)
criticity integer Not used by the application
name string | null Name of the action plan
scope string | null Scope (32 characters max)
cause string | null Cause of the action plan (observations of the failed measurement)
remediation string | null Remediation to carry out
measure_id integer | null Reference to measures.id — measurement at the origin of the action plan
creation_date date | null Creation date of the action plan
due_date date | null Due date
close_date date | null Close date
justification string | null Justification of the closure or rejection
created_at datetime Creation date
updated_at datetime Last modification date

Values of the type field

Value Meaning
1 Major non-conformity
2 Minor non-conformity
3 Observation
4 Opportunity for improvement

Values of the status field

Value Meaning
0 Open
1 Closed
2 Rejected

Example:

{
  "id": 1,
  "reference": "ACT-2026-001",
  "type": 1,
  "status": 0,
  "progress": 60,
  "criticity": 0,
  "name": "Rollout of multi-factor authentication on privileged accounts",
  "scope": "Information Systems Department",
  "cause": "Administrator accounts are only protected by a password, without a second authentication factor.",
  "remediation": "Deploy an MFA solution (TOTP or hardware key) on all privileged accounts.",
  "measure_id": null,
  "creation_date": "2026-04-01",
  "due_date": "2026-09-30",
  "close_date": null,
  "justification": null
}

exceptions

The exceptions table records the exceptions (waivers) granted when a security measure cannot be applied: justification, compensating controls and validity period. An exception follows an approval workflow: it is created as a draft, submitted, then approved or rejected by an administrator.

Field Type Description
id integer Unique identifier (PK)
control_id integer | null Reference to controls.id — security measure concerned by the exception
name string Name of the exception (required)
description string | null Description of the exception
justification string | null Business justification (why the security measure cannot be applied)
compensating_controls string | null Compensating controls reducing the residual risk
start_date date | null Start of the validity period
end_date date | null End of the validity period; null if no end date
status integer Status of the exception (see values below)
created_by integer | null Reference to users.id — author of the exception
submitted_by integer | null Reference to users.id — user who submitted the exception
submitted_at datetime | null Submission date
approved_by integer | null Reference to users.id — administrator who approved or rejected the exception
approved_at datetime | null Approval or rejection date
approval_comment string | null Comment of the approval, or reason for the rejection
created_at datetime Creation date
updated_at datetime Last modification date

Values of the status field

Value Meaning
0 Draft
1 Submitted (pending approval)
2 Approved
3 Rejected
4 Expired

An approved exception whose end date has passed is also shown as expired.

Example:

{
  "id": 4,
  "control_id": 68,
  "name": "Temporary shared VPN access for a contractor",
  "description": "A shared VPN account is still used by the application maintenance contractor.",
  "justification": "The migration to named accounts has been delayed by the contractor.",
  "compensating_controls": "Enhanced connection logging and restricted access hours.",
  "start_date": "2026-03-01",
  "end_date": "2026-06-01",
  "status": 3,
  "created_by": 2,
  "submitted_by": 2,
  "submitted_at": "2026-02-20T09:00:00.000000Z",
  "approved_by": 1,
  "approved_at": "2026-02-25T14:00:00.000000Z",
  "approval_comment": "Named accounts must be in place before the end date."
}