CISO Assistant is an open-source GRC (Governance, Risk and Compliance) platform — a different take on cybersecurity posture management, built on a few load-bearing ideas:
Explicitly decoupling compliance from security-operations implementation.
Providing simplified tools for decision-making.
Assessing a program, product, or whole organisation against standard frameworks.
Letting you bring your own framework via a simplified DSL.
Acting as a one-stop shop for the Governance, Risk, and Compliance layers.
Introduction — design philosophy and vocabulary.
Concepts — the central objects you work with: domains, perimeters, applied controls, assets, assessments, risks.
Installation — getting CISO Assistant running.
— recorded demos and walkthroughs.
— subscription tiers and what's included.
— feature-by-feature comparison.
Configuration — organisation setup, IAM, SSO, and customisation.
Features — a catalogue of shipped capabilities.
Guides — task-oriented walkthroughs, both onboarding and ongoing operations.
AI and Integrations — the REST API, the MCP server, and third-party integrations.
Contributing — how to extend CISO Assistant and improve this documentation.
— partner programme for integrators and resellers.
CISO Assistant is built around a small set of design principles. A lot of object boundaries, naming choices, and workflow shapes only make sense once you have these in mind.
Applied controls at the centre
The applied control is the unifying object in CISO Assistant. Everything the organisation does to manage risk and prove compliance is captured as an applied control — a technical safeguard, an organisational process, a documented policy, a tested recovery plan. Once that's in place, the rest of the platform connects to it:
An audit assesses requirements; each requirement assessment links to the applied controls that satisfy it.
A risk scenario lowers its current and residual levels by attaching the applied controls in place and planned.
A task records that an applied control was actually exercised on a given date and produces the evidence to prove it.
Evidence lives on an applied control — and through it, substantiates every requirement and scenario the control supports.
An incident response invokes applied controls; a vulnerability is mitigated by them; a policyis one.
Authoring an applied control once and reusing it across all the places it applies — instead of redoing the same work per audit, per risk study, per framework — is the productivity gain that justifies the whole architecture.
The corollary of putting applied controls at the centre is that everything around them must be decoupled, so that one applied control can serve many consumers without being rewritten for each:
Security controls are decoupled from compliance requirements — a single applied control can satisfy many requirements across many frameworks.
Risk assessments are decoupled from frameworks — the same risk scenario can inform multiple compliance audits.
Assets are decoupled from threat scenarios — assets exist independently of any specific risk study and can be reused across them.
The payoff is reuse, end-to-end. One applied control answers many requirements. One assessment covers many frameworks. One asset participates in many scenarios. One evidence file substantiates everything the underlying control supports.
Decoupling principle
Community vs PRO
How the Community and PRO editions differ, how contributor seats are counted, and where each edition can run
CISO Assistant ships in two editions:
Community — the open-source edition, free to use, with the platform's core GRC capabilities. Self-hosted only.
PRO — the commercial edition, built on top of Community, adding enterprise features (sub-domains, focus mode, advanced insights, custom roles, validation flows, and more) plus official support.
A full feature-by-feature comparison lives on the — we keep the matrix there so it stays in sync with pricing and release cycles. This page is here to explain the commercial concepts that intersect with the platform itself: contributor seats and where PRO runs.
PRO is licensed by contributor seats — the number of users in your instance who can actually make changes.
A user counts as a contributor as soon as they have any create / edit / delete permission anywhere in the platform — that is, any role that grants add_…, change_…, or delete_… rights. Concretely, the typical contributor is someone with the Analyst, Domain Manager, or Administrator role (or a custom role that confers similar write rights). Read-only users — anyone whose role only grants view_… permissions — do not consume a seat.
Two narrow categories of users are explicitly excluded from the seat count, even though they may perform meaningful work in the platform:
Pure approvers — a user whose only write capability is signing off on a . In practice this is the built-in Approver role: its single write permission, change_validationflow, is registered as a non-seat permission. If the same user also holds any other write right (e.g. they're an Analyst who happens to be an approver too), the seat is still counted — being an approver doesn't subtract from the count, it just doesn't add one on its own.
External third-party representatives — vendor-side users who log into the to fill in entity assessments. They are flagged as external (is_third_party) and are systematically excluded from the count regardless of which write permissions their role grants.
The intent behind the two exceptions is narrow: the seat count tracks internal contributors authoring and maintaining your GRC content, while sparing two patterns where counting would feel punitive — pure sign-off workflows, and external vendors who don't belong to your organisation in the first place.
The instance compares the number of contributors against the seat allowance carried by your license. The current count is visible from About CISO Assistant, opened via the three-dot menu next to your name in the sidebar footer — so you can see at any time how many seats are used and how many are available.
PRO is available on both deployment models, and the feature set is identical between them:
On-premises — you host the platform on your own infrastructure (Linux VM, Kubernetes via the , or any of the deployment methods documented under ). Your data stays in your network. This is the right model when sovereignty, air-gapping, or strict residency requirements rule out a managed service.
SaaS — intuitem hosts and operates a managed instance for you. No infrastructure to run, automatic upgrades, backups handled. The right model when you'd rather focus on the GRC programme than on running a Django application.
You can move between the two models — there's no architectural difference, and the data formats (domain exports, library YAML, audit exports) are stable across deployments.
For organisations that don't want to track individual seats — typically large enterprises, MSSPs, public-sector deployments, or any environment where contributor headcount fluctuates often — both the on-premises and SaaS editions are available with an unlimited plan. Under an unlimited plan, the platform doesn't enforce a seat count and you don't need to manage role assignments around license limits.
A SecNumCloud version is also available, with dedicated hosting under the highest available cloud-security qualification. It is offered in unlimited mode. See the for details.
— current plan tiers and what each includes.
— the full feature comparison.
— the governance object whose approvers don't consume a seat.
— the auditee surface used by external representatives, who also don't consume a seat.
Contributor seats
Exceptions — who doesn't count
Internal users helping fill in an audit do consume a seat. This includes anyone in your own organisation who is assigned to answer requirements via the feature — most typically users carrying the built-in Respondent role, but also any internal teammate whose role grants write access to requirement assessments and evidences. The third-party exception above applies only to vendor representatives reaching the platform through the external auditee surface.
A domain is a top-level container in CISO Assistant. It represents an organisational scope — a business unit, a subsidiary, or any boundary you want to manage access and reporting around.
Domains are the platform's primary mechanism for access control and reporting boundaries: a user's roles are granted on a domain, and most reports, dashboards, and audit roll-ups can be filtered by domain.
Building a hierarchy
A domain can have a parent domain (parent_folder internally) — that's how you build a tree of sub-domains beneath a top-level one. The hierarchy lets you mirror the shape of your organisation in the platform: a "Group" domain on top, "Region" or "Subsidiary" domains beneath, "Business unit" or "Programme" domains beneath those, and so on.
When a parent-child relationship is in place, two things follow:
Permissions can flow downward. A user with a role on a parent domain can be configured to see and act on objects in its sub-domains, without re-assigning them at every level.
Reporting can roll up. Dashboards and analytics aggregate across a domain and its descendants, so leadership-level views work the same way the org chart does.
The root is reserved for global, cross-organisation objects (built-in catalogues, the global library) — your own domains always live one level under it, even when no other hierarchy is in place.
Not every domain needs to be an IAM boundary. Sometimes you want a domain purely as an organisational container — a folder in the tree to group related work — without the platform spinning up the per-role user groups that come with a "real" scoping domain.
The Create IAM groups checkbox on the domain form controls this:
On(default for new domains) — the platform auto-provisions one user group per role for the domain. Anyone who needs access to objects in this domain gets placed into one of those groups; the domain is a true IAM scope.
Off — no user groups are created. The domain exists in the tree, can be picked from selectors, and can host objects, but it carries no scoping machinery of its own. Access flows from whatever parent domain it sits under.
Turn the flag off when you want sub-domains that are just structure — for example, breaking a "Subsidiary" domain into a "2025 audits" / "2026 audits" tree purely for organisation, without giving each year its own IAM surface. The setting is shown by the on-form help text: "IAM groups are used to assign roles to users."
The flag is not a one-way choice. You can flip it on a domain at any time — turning it on later provisions the per-role user groups, turning it off later removes them. So a domain you initially created as a pure folder can later become a real IAM scope (or vice versa) without recreating it or moving its content.
The hierarchy is not frozen at creation. Editing a domain lets you change its Parent domain (PRO), which moves the whole sub-tree — the domain itself and every domain beneath it — under the new parent. Use this to:
Reorganise as your business changes (a programme becomes a subsidiary, two business units merge).
Promote a sub-domain to top-level by setting its parent back to the root.
Re-parent for IAM reasons (giving a parent role-holder access to a sub-tree that wasn't previously under them).
The platform prevents cycles — you can't move a domain under one of its own descendants — but everything else is reachable.
Almost every operational object in CISO Assistant is bound to a domain: assessments and audits, applied controls, evidences, risk scenarios, assets, tasks, policies, findings, incidents, exceptions, contracts, entities, and so on. The domain a record lives in is what drives who can see it and how it rolls up in reports.
Because reorganisations happen, the domain assignment is not permanent:
One at a time — edit any object and pick a different Domain in the form. The platform re-evaluates IAM scoping on save, so the object disappears from one domain's views and appears in the other's.
In bulk — for models that opt in to bulk operations, the toolbar exposes a Change folder action: select multiple rows in the table, choose the destination domain, and the move is applied across the selection in one go. Useful when reorganising a subsidiary into its own sub-tree, or pulling a programme's controls into a dedicated domain.
A handful of objects whose domain is forced by a parent (e.g. risk scenarios always inherit their risk assessment's domain) intentionally don't expose the batch Change folder action — moving the parent moves the children.
— how users, teams, and entities get scoped to a domain
Sub-domains are an Enterprise (PRO) feature. The community edition ships with a flat structure — every domain you create sits directly at the root. The Parent domain field on the domain form is only exposed in the Enterprise edition, where you can nest domains to whatever depth you need to mirror your organisation.
Organisational-only domains — the "Create IAM groups" flag
A glossary of the terms used in CISO Assistant. Where a user-facing term differs from the internal model name, both are given.
A
Accreditation — Formal authorisation that a system, environment, or product has met security and compliance requirements. Captured as an object in project-management workflows, often required for go-live.
Actor — The unifying handle for anyone who can own or be assigned work in CISO Assistant. An actor wraps exactly one of three underlying objects: a User, a Team, or an Entity. Auto-created when its underlying object is created — not managed directly. See Actors and teams.
Applied control — The main building block of the action plan: a concrete action your team has implemented or will implement. It can be technical, organisational, a process, a policy, a piece of documentation — anything that materially changes risk or compliance posture. Applied controls are always defined by the organisation and can be attached to the global domain or to a specific domain. They may derive from a reference control for consistency, or be created independently.
Asset — Anything of value worth protecting. Primary assets are core resources directly contributing to the organisation's main objectives (business processes, data, intellectual property). Supporting assets indirectly aid primary functions (IT systems, services, locations, people).
Asset assessment — A per-asset row inside a business impact analysis, capturing recovery posture (documented, tested, targets met), associated controls, evidence, and the escalation thresholds that describe how impact grows over time.
Assessment — Umbrella term covering audits (compliance work), risk assessments, business impact analyses, and entity assessments.
Attack path — In EBIOS RM, the route an attacker may take from a starting point — through stakeholders or supporting assets — to reach a target objective.
Audit — The evaluation of a perimeter against a framework, producing a per-requirement view of status, score, and evidence. Internally a ComplianceAssessment.
Audit log — Append-only record of significant actions taken in the platform (creations, edits, permission changes, logins). PRO feature.
Auditee mode — A read-only UX mode aimed at external assessors who need to inspect an audit without being granted full access to the platform. Gated by the auditee_mode feature flag.
Business impact analysis (BIA) — A structured assessment of the operational, financial, and reputational impact of disruption to specific assets or business processes. Outputs feed into resilience planning.
Campaign — An orchestration object for running many audits in parallel — for example, one audit per perimeter against the same framework. PRO feature.
Catalog object — A reusable building block of CISO Assistant: framework, threat, risk matrix, reference control, mapping, security advisory, CWE. Catalog objects are packaged into libraries.
Compliance assessment — Internal model name for an audit. See Audit.
Dashboard — A configurable view of metrics and progress indicators, scoped to a perimeter or a domain.
Data breach — In a privacy register, an incident affecting personal data, with notification status and response actions.
Data contractor — A third party involved in a processing as data processor, sub-processor, or joint controller. Distinct from a generic supplier entity — captures the privacy-specific role.
EBIOS RM — The French ANSSI risk-management method, supported natively in the platform as its own object graph (studies, feared events, stakeholders, strategic and operational scenarios, kill chains).
Elementary action — In EBIOS RM, an atomic step an attacker can perform. Composed into operating modes.
Entity — Scope of an external review — typically a vendor or third party.
Feared event — In EBIOS RM, the undesirable outcome to be avoided on a primary asset — for example, a confidentiality breach of customer data.
Filtering label — A free-form tag that can be attached to most objects for categorisation, filtering, and reporting.
Findings assessment — A formal record tracking issues raised by an audit, a security review, or an external assessor, used to drive remediation through to closure.
Generic collection — A flexible grouping object in project-management workflows: a "bag" of related items that doesn't fit a more specific schema.
Incident — A security or operational event being investigated or tracked. Distinct from a risk (potential) or a vulnerability (weakness).
Inherent risk — The natural risk level of a scenario without any applied controls. The top tier of CISO Assistant's three-tier model — useful for ranking scenarios by their underlying severity, independently of the mitigation already in place. Surfaced in the UI when the inherent_risk feature flag is on.
Journey — An instance of a applied to a domain. Carries the scaffolded objects created at apply time and a step list with per-step statuses, notes, and completion timestamps. See .
Journey step — One row of work inside a journey: a title, description, optional pointer to a target object or internal route, and a status (not started, in progress, done, skipped).
Kill chain — In EBIOS RM, an ordered sequence of attacker steps culminating in the target objective.
Library — Container object bundling one or more catalog objects (frameworks, matrices, threats, reference controls, mappings, security advisories).
Managed document — A document tracked through a controlled lifecycle (draft, in-review, validated, published, deprecated) and pinned to a parent object such as a policy. Each iteration is a .
Mapping — Based on the . Allows moving an assessment from framework A to framework B while reusing existing requirement assessments.
Metric definition — A reusable specification for a measurable indicator (formula, target, unit, scope). Defined once, instantiated per perimeter.
Operating mode — In EBIOS RM, a specific way an attacker can carry out an operational scenario, composed of elementary actions.
Operational scenario — In EBIOS RM, the detailed "how" of a strategic scenario — the assets touched, the steps taken, and the techniques used.
Organisational issue — A documented context element describing an internal or external problem affecting the organisation.
Perimeter — A scoped subset of a domain that an audit or risk assessment applies to. Unlike a domain, a perimeter does not enforce role-based access control. Perimeters were previously called "Projects".
Personal access token (PAT) — A long-lived authentication token a user can issue from their profile to authenticate API calls. Alternative to session-based login; used by scripts, integrations, the CLI, and the MCP server.
Personal data — In a privacy register, any data referring to an identified or identifiable individual.
Quantitative risk hypothesis — A parameter set (loss-event frequency, magnitude distribution) feeding a quantitative risk scenario.
Quantitative risk scenario — A scenario inside a quantitative risk study, evaluated via Monte-Carlo simulation over loss distributions.
Quantitative risk study — A risk study using quantitative methods rather than a risk matrix. Sibling to qualitative risk assessment and EBIOS RM.
Recap — The roll-up view of a business impact analysis, aggregating asset assessments and their escalation thresholds into a single readout.
Recovery target — A documented commitment for restoring an asset after disruption, typically a Recovery Time Objective (RTO) or Recovery Point Objective (RPO). Tracked on an asset assessment as "documented", "tested", and "targets met" flags.
Reference control — A template for an applied control. Provided by frameworks via libraries, or defined locally. Optional but recommended for keeping applied controls consistent across the organisation.
Security advisory — A catalogued security warning published by a vendor or CERT (e.g. CVE entries). Linked to vulnerabilities and affected assets.
Security exception — A documented, time-bound deviation from a control or policy, approved through a workflow and tracked for review.
Severity — The shared ordinal scale used to qualify vulnerabilities, incidents, and findings: undefined / info / low / medium / high / critical. Drives SLA escalation and visual emphasis in dashboards.
Task definition — A reusable specification of a task: default assignee, owner, recurrence rule, expected evidence. Defining a task creates one or more task occurrences over time. Internally a TaskTemplate.
Task node — Internal model name for a task occurrence. See Task occurrence.
Task occurrence
URN — Uniform Resource Name. A unique identifier used to link to catalog objects across libraries.
User — A person with an account on the platform.
User group — A combination of a role and a domain, on which you place users. Auto-created when a domain is created.
Validation flow — A configurable approval workflow that mirrors an organisation's internal review or management-approval process — peer-review, sign-off by a security lead, formal acceptance by a steering committee. Attached to objects whose state changes warrant such review (e.g. risk acceptance, audit close-out), it captures the human approval step inside the platform rather than enforcing it as a hard technical gate.
Vulnerability — A weakness in a system or process that could be exploited by a threat. Tracked with severity, status, and linked applied controls.
Webhook endpoint — A registered URL CISO Assistant calls when configured events happen (e.g. an audit closed, an applied control updated). Used to notify external systems and trigger downstream automation.
Contract — A third-party agreement attached to a supplier entity or solution, with terms, dates, and renewal information.
Control — Generic term. Disambiguate against applied control (concrete instance) and reference control (template).
Current risk — The risk level given the applied controls already in place — the state of risk today. The middle tier of CISO Assistant's three-tier model: inherent (no controls) → current (existing controls) → residual (existing + planned controls).
Custom field (also custom attribute) — An organisation-defined attribute that can be attached to platform objects (projects, risks, assets, suppliers, contracts, and more) to capture typed, filterable, searchable metadata beyond the built-in fields.
CWE — Common Weakness Enumeration. A catalogued category of software weakness, used to tag vulnerabilities and security advisories.
Data recipient — A party (internal team, external service, public body) that personal data is disclosed to as part of a processing.
Data subject — The category of individuals whose personal data is processed (customers, employees, prospects, etc.). Surfaces in the privacy register and right-request workflows.
Data transfer — A record of cross-border or cross-entity movement of personal data, with destination, legal basis, and safeguards.
Document revision — A single revision of a Managed document. Carries a version number and a lifecycle status (draft → in review → change requested → validated → published → deprecated).
Domain — The top-level container in CISO Assistant: a business unit, subsidiary, project, or any boundary used for organising work and isolating permissions via role-based access control. Sub-domains nest underneath. Internally a Folder. Demo and Starter are reserved for internal features.
Entity assessment — The actual review of an entity. Can trigger or be linked to an audit.
Escalation threshold — A point-in-time / impact pair attached to an asset assessment inside a BIA: "after 4 hours of outage, impact is high". Lets a BIA model how disruption escalates rather than recording a single worst-case impact.
Evidence — A document, screenshot, configuration sample, or any other artifact attached to an applied control or requirement assessment to substantiate compliance.
Evidence revision — A single versioned iteration of an evidence object. Replacing an attachment creates a new revision rather than overwriting the previous one; revisions carry a version number, an SHA-256 integrity hash, optional observation, and a link to the task occurrence that produced them when applicable.
Focus mode — A workspace mode that filters the entire UI to a single domain, hiding objects belonging to other domains. PRO feature.
Folder — Internal model name for a domain. See Domain.
Framework — A set of requirements covering patterns and expectations needed to comply with a regulation, prepare a certification, or establish a foundation. Shipped as a YAML library.
Metric instance — A concrete sample of a metric definition for a given scope at a given point in time.
Organisational objective — A documented strategic or operational goal of the organisation.
Policy — A specific type of applied control: a document describing what is expected from some part of your stakeholders. Lives in CISO Assistant so its lifecycle can be managed alongside the rest of your controls.
Preset — A reusable template describing a guided workflow: a set of starter objects to scaffold (audit, risk assessment, etc.) plus an ordered list of steps to follow. Library-backed or authored locally; applied to a domain to produce a Journey.
Processing — In a privacy register, an activity that operates on personal data (collection, storage, transfer, deletion). Captures purpose, lawful basis, recipients, and retention.
Processing nature — A catalogued type of processing operation (collection, storage, transfer, disclosure, deletion, …) used to characterise a processing.
Project — In the project-management module, a planned initiative with deliverables and milestones. Distinct from the legacy meaning of "project" in older CISO Assistant documentation — see Perimeter.
Purpose — In a privacy register, the lawful reason for which personal data is processed.
Representative — The person responsible for answering the questionnaire or requirements of an entity assessment.
Requirement — A single normative statement inside a framework.
Requirement assessment — The evaluation of one requirement inside an audit (status, score, evidence, applied controls).
Requirement mapping set — Internal model name for the catalog object backing a mapping library. See Mapping.
Residual risk — The risk level expected once all planned applied controls have been implemented — the target state of the action plan. The bottom tier of CISO Assistant's three-tier model (inherent → current → residual), and the figure used as input to risk-acceptance decisions.
Responsibility matrix — An assignment of actors to activities, used in project workflows and accreditation processes. Supports RACI, RASCI, and RAPID conventions.
Responsibility role — The role attached to an actor on an activity inside a responsibility matrix (e.g. R/A/C/I in RACI, or R/A/S/C/I in RASCI). Defined per matrix.
Right request — In a privacy register, a data-subject request under GDPR or equivalent (access, rectification, deletion, portability).
Risk acceptance — Formal record of an organisation's decision to tolerate a residual risk without further treatment. Carries an approval workflow; approval requires the Approver role.
Risk assessment (also risk study) — A scenario-based evaluation of risk over a perimeter.
Risk matrix — A configurable lookup table that derives risk level from probability and impact. Imported from a library. The matrix is fixed per risk assessment once the assessment is created.
Risk scenario — A building block of a risk assessment: combines threats, assets, and existing controls into a story whose probability and impact can be evaluated.
Role — A bundle of permissions. Four built-in roles ship with the platform; PRO editions also support custom roles.
Domain Manager — can set up and access everything on a domain.
Analyst — can input and read data, but cannot change a domain's settings.
Reader — read-only on the domain's items.
Approver — can validate workflows on objects for a domain (e.g. risk acceptance).
Role assignment — The attachment of a user (or user group) to a role within a domain. The unit of access control.
RO/TO couple — In EBIOS RM, the pairing of a Risk Origin (who attacks) with a Target Objective (what they want). The seed for strategic and operational scenarios.
Solution — A product or service provided by an entity.
Stakeholder — In EBIOS RM, an internal or external party with a relationship to the studied system. Evaluated for trust level and dependency.
Strategic scenario — In EBIOS RM, the high-level "what" of an attack: a Risk Origin, a Target Objective, a path through stakeholders, and an outcome.
— A scheduled instance of a task definition, with a due date, a status (pending → in progress → completed/cancelled), and the evidence collected when the task ran. Internally a
TaskNode
.
Task template — Internal model name for a task definition. See Task definition.
Team — A named grouping of users used for collaborative ownership of objects, with a leader, optional deputies, members, and an optional team email for notification routing. Distinct from a User group (which is role-scoped to a domain — see the disambiguation in Actors and teams).
Terminology — An organisation's overrides to the platform's default labels, used to align the UI with internal vocabulary.
Threat — A catalogued source of harm — reusable across scenarios. Optional: assessments can be performed without referencing threats explicitly.
A perimeter is an optional scope refinement an assessment or risk study can be attached to, inside a domain. Where a domain defines who owns the work and what they can see, a perimeter narrows the assessment to exactly what is being assessed — a product, a system, a process, a contract.
Perimeters are optional everywhere they appear: an assessment without a perimeter is scoped to its domain only. Use them when you need to track several distinct scopes inside the same domain, or to roll up assessments by scope rather than by domain.
When to use a domain vs a perimeter
The two concepts solve different problems — and the choice matters because it shapes both who can see what and how reports roll up.
Use a domain (or a sub-domain) when the boundary needs to enforce IAM scoping or anchor reporting. Domains are where roles are granted, where permissions stop, and where dashboards aggregate. Pick a domain when different teams must have different levels of access, or when leadership wants to see one number per business unit / subsidiary / regulated entity.
Use a perimeter when the boundary is purely logical, inside an already-scoped domain. Perimeters split the work into distinct named scopes — a product, a system, a process, a contract — so you can run separate audits or risk studies against each without spinning up extra IAM machinery. Everyone with access to the domain sees every perimeter inside it; there's no per-perimeter access control.
A useful mental check: "Do these two scopes need different people seeing them?" — if yes, they're domains (or sub-domains). "Do these two scopes need separate assessments but the same audience?" — they're perimeters.
How CISO Assistant represents people, groups, and external parties for assignment and responsibility
Almost every object in CISO Assistant has an assignee — the applied control someone is responsible for, the audit a team is running, the contract a supplier signs. The platform represents all these counterparties through a single abstraction: the actor.
Mental model
The actor is a one-to-one wrapper that always points at exactly one of three concrete records — a user, a team, or an entity (the three dashed edges are exclusive: a database check constraint enforces XOR). Every Assigned to / authorship / approver field on the platform (applied control assignee, audit author, task assignee, contract counterparty…) holds an actor, so a single code path resolves notifications and access regardless of the underlying type. Teams aggregate users via leader, deputies, and members — assigning to a team fans out to every user in it.
User-facing
Internal
Notes
An actor is the unifying handle for anyone who can be assigned to work in CISO Assistant. Every actor wraps exactly one of three concrete underlying objects:
A user — a person with a platform account.
A team — a named grouping of users for collaborative responsibility.
An entity — an external party from the third-party register (typical for contracts and entity assessments).
The actor abstraction means the same Assigned to field on an applied control can hold a user, a team, or a supplier without the consuming code caring which it is. Notifications, emails, assignments, and reporting all go through the actor — they fan out to the right addresses regardless of the underlying type.
Actors are created automatically when their underlying object is created. You don't manage actors directly; you manage users, teams, and entities, and the actor records follow.
A team is a named grouping of users used for collaborative assignment — when the responsibility for something belongs to a working group rather than an individual.
A team has:
A leader — a single user accountable for the team.
Deputies — users who can act in the leader's place.
Members — the broader group.
Teams are first-class targets for assignments and notification routing. When work is assigned to a team, anyone with the appropriate role in the team's domain can act on it.
These two are easy to confuse:
A team is a collaborative group with members, a leader, and deputies. It's about who works together. Teams are voluntary, organisational, and can span any domain.
A user group is a (role, domain) pair on which users get placed. It's about what permissions someone has where. User groups are auto-created when a domain is created — one per role per domain.
You join a team for collaboration. You're placed in a user group for access. Both can carry users; they answer different questions.
Applied controls are assigned to one or more actors.
Requirement assessments can be assigned to an actor for completion.
Tasks are assigned to actors and notify them when due.
Because all of these use the same actor handle, an audit log that says "assignee changed from team A to user B" is unambiguous, and the platform can resolve notifications the same way everywhere.
— where role-based access control happens
An optional team email — used as the default notification address; if not set, emails fan out to the leader, deputies, and members individually.
Risk scenarios are assigned to one or more actors.
Entity assessments have a representative (an actor of type user, tied to an entity).
Validation flows route approvals through actors.
Actor
Actor
XOR(User / Team / Entity); auto-created with its target
How IAM, the domain hierarchy, publication, and cross-domain visibility shape what each user sees
This page is the mental model for how access and visibility work in CISO Assistant. The full configuration-side deep-dive (SAML / OIDC / MFA / PATs / accounting) lives in ; this page focuses on the three things every user needs internalised before they can predict what they will and won't see on screen:
Almost everything is bound to a domain.
Permissions are inherited down the domain tree.
Frameworks
A framework is a normative body of requirements that audits are measured against — an industry standard (ISO/IEC 27001, NIST CSF, SOC 2), a regulation (NIS2, DORA, GDPR), a custom internal standard, or any other structured set of requirements.
In CISO Assistant, frameworks are shipped as YAML libraries and are the foundation of every audit.
A framework lives inside a loaded library and is read-only. It comprises a tree of requirement nodes — some assessable, some structural — linked parent-to-child via the self-loop. Creating an audit instantiates the framework: each assessable node becomes a requirement assessment inside the audit. Requirements can optionally suggest reference controls (templates for the applied controls that satisfy them), and a framework can be mapped to other frameworks for cross-walks.
Some objects are published — visible across the tree — and assessments are not.
The combination of these three is what produces the most common "why am I seeing items from another domain here?" moment, which the last two sections explain and resolve.
The platform's primary scoping unit is the domain (see Domains). Almost every operational object you create carries a domain — that's what drives who can see it and how it rolls up in reports.
The list is long on purpose — to make the model concrete:
A small number of objects don't carry a domain because they're either system-wide (the user catalogue, the role catalogue, instance settings) or imported from a library catalogue. Everything else lives inside a domain.
The domain hierarchy is not just for organising the UI — it actively shapes access. A role granted on a domain applies to every domain beneath it. Give a user the Analyst role on a parent domain, and they get analyst-level access to every sub-domain underneath, without re-assigning them at each level.
This is why the tree shape matters as much as the names: putting "France" and "Germany" under a "EMEA" parent isn't decorative — it's the lever that lets EMEA-level managers see across both without granting them individual roles per country.
Permissions only flow downward: a role on a sub-domain does not grant any access to the parent. If you need a role-holder to see across siblings, the role goes on the shared ancestor.
The same inheritance also drives reporting: most dashboards and analytics roll up across a domain and its descendants, so a leadership-level view on the parent domain is automatically the consolidated view across its sub-tree.
Some objects exist to be shared. Frameworks, threats, risk matrices, reference controls, and other catalogue-style items wouldn't be useful if they were trapped in a single domain — every team needs to be able to pull from the same shared library.
CISO Assistant models this through a built-in flag — is_published — that any object can carry. An object marked as published is visible inside every sub-domain of its own domain, as if it had been attached to each one. Publication is a visibility mechanism only; it does not let users in other domains create, update, or delete the object.
By default:
Catalogue-style objects (frameworks, threats, matrices, reference controls, libraries, terminologies, …) are published — they live "above" individual domains and are intended to be reused.
Assessments (audits, risk assessments, BIAs, entity assessments) are not published — they belong to a specific domain and stay there.
The most common surprise this creates is when a user opens the platform and sees a library of frameworks or threats they "shouldn't" have access to. They aren't seeing them through a permissions hole — they're seeing them because the catalogue is published from a domain that sits above theirs.
If you want to keep a specific object out of the published view, the simplest trick is to attach it to a leaf sub-domain (a domain with no children) — nothing inherits from a leaf.
Assessments routinely compose objects across the tree. Risk assessments reference applied controls, threats, and assets; audits reference applied controls and evidences; findings assessments reference applied controls and the requirement assessments they remediate.
When you're working inside one assessment, the platform's selectors and pickers don't just show you what's in the assessment's own domain — they show you everything you have access to. So a risk scenario authored inside the France domain can pull in:
A shared applied control attached to the EMEA parent domain (you can see it because of inheritance).
A threat from the global library (you can see it because it's published).
An asset attached to a sibling Germany domain (if your role gives you access there).
This is by design — composing across the organisation is the whole point of a centralised GRC platform — but it can be disorienting on day one. The rule is consistent: you see what you have access to, regardless of which domain you started on.
When you have access to many domains and you only want to think about one at a time, the platform exposes Focus mode (PRO, default off). Focus mode scopes the entire application — every list, dashboard, count, and search — to a single domain and its sub-tree, hiding everything else for the rest of your session.
It does not grant or revoke permissions; it just filters the view. Use it when:
You operate across many client domains and want to work on one at a time.
Your organisation is large enough that the "all domains" view is overwhelming for day-to-day work.
You're running a demo or an onboarding and want the rest of the workspace out of the way.
When focus is engaged, all the cross-domain composition described above is suppressed — you'll only see assets, controls, and assessments from the focused sub-tree. Clearing the focus restores the full cross-domain view.
Domains — domain hierarchy, IAM groups, restructuring, moving objects.
Publication — why catalogues appear across all domains
Why you sometimes see items from other domains
Managing the noise — focus mode (PRO)
Related
Notes
Framework
Framework
Read-only catalog object
Requirement
RequirementNode
Tree node (assessable or section)
Library
LoadedLibrary
Active library bundle
Audit
A framework is a tree of requirement nodes. Most nodes are assessable — concrete requirements you evaluate one by one — while others act as section or chapter headings that organise the tree. Each assessable node becomes a requirement assessment inside an audit, carrying its own status, score, and evidence.
Frameworks can define a default scoring scale with a minimum score, a maximum score, and optional level descriptions. For example, a CMMI-style framework may use 0..5, while another framework may use 1..4 or 0..100.
An individual requirement node can override that default scale with its own min_score, max_score, and scores_definition_ref. These overrides are useful when a standard mixes different scoring shapes in the same tree: for example, a mostly maturity-based framework that also contains binary pass/fail requirements.
The override is resolved independently for each field:
If a requirement defines min_score, that value is used; otherwise the audit-level minimum is used.
If a requirement defines max_score, that value is used; otherwise the audit-level maximum is used.
If a requirement defines scores_definition_ref, those labels (resolved from the framework's alternatives registry) are used; otherwise the audit-level labels are used when they fit the requirement's effective range.
Per-requirement label overrides go through a named alternatives registry declared on the framework alongside its default scale. Each requirement references an entry by name, keeping shared scales DRY and avoiding duplication on the node:
One-off scales that only apply to a single requirement are added as a new entry in the framework's alternatives registry and referenced by name, just like shared scales. The node always carries a reference, never inlined labels.
The audit copies the framework's scores_definition (default scale + alternatives) at creation, so per-requirement references resolve against the audit's own copy. This keeps the audit self-contained: customising the audit's scale later doesn't break references on its requirements.
Roll-ups keep mixed scales comparable. Average-based aggregation normalises each requirement score against its effective range before computing the parent or global score, then displays the result on the audit scale. Sum-based aggregation remains a raw weighted sum, so each requirement contributes its own effective maximum.
CISO Assistant ships with 100+ built-in frameworks covering most international standards and regulations. When none of them fits your needs, you can build your own — see Designing your own libraries and Getting your custom framework.
A mapping (or crosswalk) is a directed graph linking the requirements of one framework to those of another, using the NIST OLIR convention. Once a mapping is loaded, an existing audit can be projected onto the target framework — reusing requirement assessments where the mapping is strong, surfacing gaps where it isn't.
A library is a bundled set of catalog objects — frameworks, threats, risk matrices, reference controls, mappings, security advisories, CWE entries — distributed as a YAML file.
Libraries are how content gets into CISO Assistant. They make the platform extensible: anything from a regulator's framework to a vendor's threat feed to your organisation's internal control catalogue is just another library.
Mental model
A library starts life as a stored record — its YAML is parsed and registered but contents stay inactive. Loading it materialises whatever catalog objects the YAML declares — any subset of framework, risk matrix, threats, reference controls, or mapping set (all dashed, all optional). A loaded library can also declare dependencies on other loaded libraries — for example, a framework library that ships its companion reference-control catalogue as a separate dependency.
User-facing
Internal
Notes
A library can be in one of two states:
Stored — the library is known to the instance but its content hasn't been activated yet. It's visible in the libraries store, ready to be loaded on demand.
Loaded — the library is active. Its catalog objects show up across the platform: a loaded framework becomes available when creating an audit; a loaded threat appears in the threats list; loaded reference controls power autosuggestion.
A library typically contains a single primary object (for example, one framework) but may bundle related ones — a framework alongside its companion reference-control catalogue and its mapping to a sibling framework.
Library content is referenced by URN (Uniform Resource Name), an immutable identifier that survives renames and re-imports.
Built-in libraries ship with the platform — over 100 frameworks plus the standard threat, matrix, and reference-control catalogues.
Community libraries are contributed by the open-source community; see .
Custom libraries can be built locally and loaded without sharing them, useful for internal frameworks or control sets.
See for the format.
Libraries are versioned. When a newer version is available, you can upgrade in place — your existing audits keep using the version they were created with until you migrate them explicitly. See and .
Directed graphs linking the requirements of one framework to those of another
A mapping (also called a crosswalk) describes how the requirements of one framework relate to those of another. Once a mapping is loaded, an audit performed against the source framework can be projected onto the target framework — reusing the existing requirement assessments where the mapping is strong, surfacing gaps where it isn't.
Mappings are catalog objects: defined once as a YAML library, loaded into the platform, and applied on demand.
Mental model
A mapping set is the unit shipped by a library — it pins exactly one source framework and one target framework. Inside the set, each individual mapping connects one source requirement node to one target requirement node and carries a relationship type (equal / subset / superset / intersect / not_related). Applying the set to an audit projects the existing requirement assessments onto the target framework: full-coverage relationships (equal, superset) copy directly; partial-coverage (intersect, subset) require manual review.
User-facing
Internal
Notes
Most organisations have to demonstrate compliance against multiple frameworks at once — ISO 27001 plus SOC 2 plus a sector-specific regulation, for instance. Without mappings, you re-assess the same control posture against every framework's requirement list, which is busywork. With mappings, you assess once and project.
A mapping is a directed graph linking assessable nodes of a source (SRC) framework to assessable nodes of a target (TGT) framework, using the convention from project.
Each relationship between a SRC node and a TGT node has a type, which is easiest to read as a set relation between what each requirement covers:
No relationship — the two requirements are disjoint; nothing carries over.
Equal — the two requirements are equivalent in scope and intent.
Subset — the SRC requirement is contained within (narrower than) the TGT requirement.
The directionality matters: a mapping from A → B does not automatically imply B → A. Reverse mappings can be generated, but the relationship type usually inverts (a SRC subset becomes a TGT superset).
Once a mapping library is loaded, it can be applied to an existing audit:
Open the source audit.
Click Apply mapping and pick the target framework.
The platform creates a new audit on the target framework and copies over the requirement assessments where the mapping is strong (typically equal), leaving the rest to be assessed.
The apply-mapping feature can also clone an audit onto the same framework — useful for creating a new revision while keeping the previous one for history.
You don't need a direct mapping between every pair of frameworks. If the platform holds a mapping from A → B and another from B → C, it can chain them automatically to project an A audit onto C, using B as a pivot — even though no one ever authored an A → C crosswalk.
The mapping engine treats the loaded mapping sets as a directed graph of frameworks and searches for a path between your audit's framework and the target you pick. When you open an audit and choose Apply mapping, the list of available targets already includes every framework reachable through the graph — both directly mapped ones and those reachable only through one or more pivots. You select the destination; the chaining happens behind the scenes.
How the chain behaves:
Coverage degrades to the weakest hop. Full-coverage relationships (equal, superset) chain cleanly. If any hop in the path is partial (subset, intersect), the projected result is marked partial and flagged for manual review — a chain is only as strong as its loosest link.
The best path wins. When several pivots connect A to C, the engine keeps the path that successfully maps the most requirements, and records which intermediate framework(s) it went through so the projection is auditable.
This is what makes a modest set of crosswalks go a long way: a hub framework such as ISO 27001 or NIST CSF that is mapped to many others effectively becomes a translation pivot between all of them.
Many cross-walks ship as built-in or community libraries (ISO 27001 ↔ NIST CSF, SOC 2 ↔ ISO 27002, and so on). When none of them fits, you can author your own — see and the prepare_mapping_v2.py tool that scaffolds a mapping skeleton between two loaded frameworks.
Requirement
RequirementNode
Read-only catalog entry from a framework
Superset — the SRC requirement contains (is broader than) the TGT requirement.
Intersect — the two overlap in part but neither contains the other.
Depth is bounded. Chaining is limited by the Mapping max depth setting in General settings (default 3 nodes — i.e. up to one pivot, A → B → C). Raise it (up to 5) to allow longer chains (A → B → C → D…), at the cost of progressively weaker, more indirect inferences and slower computation.
A threat is a catalogued source of potential harm — a phenomenon, agent, or event that could compromise an asset, a business process, or a regulatory obligation.
Threats are reusable building blocks: a single threat (say, "Phishing") may appear in many risk scenarios, EBIOS RM operational scenarios, and applied control rationales without being duplicated.
When threats appear
Risk scenarios — most qualitative risk scenarios name the threat that drives them.
EBIOS RM operational scenarios — threats are mapped to attacker techniques in the kill chain.
Vulnerability tracking — threats can be linked to vulnerabilities to express what could exploit this weakness.
Applied controls — controls can declare which threats they address.
CISO Assistant ships with curated threat libraries based on common sources — MITRE ATT&CK, ENISA, the ISO 27005 illustrative threat catalogue, sector-specific catalogues. You can also create your own threats inside a domain, or contribute a custom threat library.
A risk assessment can be done without referencing threats — the platform doesn't force it. Naming the threat behind a scenario makes the analysis sharper, more reusable, and easier to map back to threat-intelligence feeds.
The lookup tables that derive a risk level from probability and impact
A risk matrix is a configurable lookup table that maps a (probability, impact) pair to a resulting risk level. It's what turns "likely × severe" into "critical" — the encoded judgement that lets a risk assessment be more than a free-form narrative.
Risk matrices are catalog objects: defined once, packaged into libraries, loaded into the platform, and reused across many risk assessments.
Mental model
A risk matrix is a JSON definition (probability axis, impact axis, risk levels, and the grid linking them) bundled in a library. Once a risk assessment is created against a matrix the binding is permanent — the FK uses on_delete=PROTECT — because switching matrices mid-assessment would silently change every risk level, which is what auditors don't want. Each risk scenario in the assessment reads its inherent, current, and residual risk levels from the same matrix's grid using the (probability, impact) pair the assessor sets at each tier.
User-facing
Internal
Notes
A matrix has four pieces:
Probability levels — the ordered scale used for likelihood (e.g. negligible, low, medium, high, very high).
Impact levels — the ordered scale used for severity (financial, reputational, operational, or whatever scale the organisation uses).
Risk levels — the resulting categories (e.g. low / medium / high / critical), usually colour-coded.
The grid is the substance of the matrix; the visual rendering (orientation, colours, layout) is handled by the UI based on the loaded matrix definition.
When a risk assessment is created, its risk matrix is captured and stays fixed for the lifetime of that assessment. Re-evaluating the same scenarios against a different matrix would silently change the risk levels under your feet, which is exactly what auditors don't want.
If you change matrices mid-programme, you create a new risk assessment against the new matrix and migrate the scenarios. The old assessment keeps its history; the new one starts clean against the new scale.
Each scenario in a risk assessment is evaluated three times against the chosen matrix:
Inherent risk — what the risk would be with no controls.
Current risk — what it is today given existing applied controls.
Residual risk — what it will be once planned applied controls are implemented.
The matrix is the same for all three tiers; what changes is the (probability, impact) pair the assessor sets at each tier. See for how the three tiers are used.
Matrices ship as YAML libraries — the same format as frameworks, with _meta and _content sheets defining the probability/impact/risk axes and the grid. They are typically authored in Excel using the templates under tools/excel/matrix/ and converted to YAML.
Designing a matrix correctly — particularly the grid — is non-trivial. Start from one of the existing examples and adapt the levels and grid logic rather than building from scratch. See .
The grid — the lookup from each (probability × impact) cell to a risk level.
Risk matrix
RiskMatrix
json_definition with probability / impact / risk / grid
Guided, step-by-step workflows that bootstrap a domain along a recognised path
A journey is a guided, step-by-step workflow that walks a domain through a recognised process — getting an organisation ready for ISO 27001 certification, running a DORA readiness assessment, building out a privacy register, and so on.
Journeys remove the "what do I do first?" problem. Instead of staring at an empty domain and assembling the right libraries, assessments, and tasks by hand, you pick a journey, apply it to a domain, and start working through the prescribed steps.
Preset vs journey
Journeys come in two flavours of object that mirror the Framework → Audit pattern elsewhere in the platform:
A preset is the template. It bundles a set of starter objects (typically an audit, a risk assessment, sometimes additional scaffolds) plus an ordered list of steps with descriptions and links into the platform. Presets are versioned and shipped via libraries; they can also be authored locally.
A journey is an instance of a preset, materialised in a specific domain. Applying a preset creates the scaffolded objects in the chosen domain and copies the step list onto the journey so that step statuses can be tracked independently from any other journey.
The same preset can be applied to multiple domains and yields a distinct journey each time.
Browse the catalogue. The presets page lists everything available on the instance — library-backed presets shipped with CISO Assistant (ISO 27001 starter, DORA readiness, NIS2, sector-specific bundles, etc.) and any presets authored locally. Filter by region tag to narrow the list.
Apply to a domain. Pick a target domain (or create one on the fly). Optionally let the journey create the scaffolded objects for you and apply any feature flags the preset suggests.
Work the steps. Each step has a title, description, and either a target object (e.g. "open the SOA review for the ISO audit") or a target URL inside the platform. Mark steps as
Scaffolded objects — the assessments and supporting records that get created when the preset is applied. Typical scaffolds include compliance assessments (with framework + implementation-group selection), risk assessments (with a chosen matrix), and occasionally task templates, perimeters, or entities.
Steps — an ordered list of actions to perform. Each step can point at a scaffolded object (via a named reference resolved at apply time) or at a generic internal route such as the SOA results page. Steps carry translations so the workflow speaks the user's language.
Feature flags — some presets enable optional features when applied (reports, sec-intel feeds, etc.), letting the journey come with a fully-configured environment.
Applying a preset requires permission to load libraries on the target domain. Viewing journeys and updating their step statuses follows the standard domain-scoped permission model — see .
in progress
,
done
, or
skipped
as you advance, and add notes.
Track progress. The recently-active journeys panel shows a progress ring per journey; the underlying step counts feed dashboards.
Upgrade when a newer preset ships. When a library-backed preset is updated, journeys derived from it flag an upgrade. Upgrading re-applies the newer template while preserving user-state (statuses and notes) on steps that survived the new version.
Dependencies — required libraries (frameworks, matrices) that must be loaded for the preset to apply cleanly.
Catalogued vulnerabilities, weaknesses, and the feeds that enrich them
The threat intelligence layer holds the catalogued knowledge CISO Assistant uses to prioritise vulnerabilities, qualify incidents, and connect operational findings to the wider security ecosystem. Today it covers security advisories, weakness catalogues, and a small set of public enrichment feeds; the surface is expected to grow significantly in upcoming releases.
Security advisories
A security advisory is a catalogued security warning published by a vendor, CERT, or standards body. CISO Assistant supports four sources today:
CVE — the MITRE/NIST CVE Program identifiers (CVE-YYYY-NNNN).
EUVD — the EU Vulnerability Database (post-NIS2 European equivalent).
GHSA — GitHub Security Advisories.
Other — for sources that don't fit the above.
Each advisory carries the usual identifying metadata (reference ID, published date, references) plus the bits that drive prioritisation:
CVSS base score and CVSS vector — severity per the standard scoring system.
EPSS score and EPSS percentile — probabilistic exploitation likelihood from FIRST.
Active exploitation flag and the KEV date when the advisory landed on CISA's Known Exploited Vulnerabilities list.
Advisories are catalog objects: library-backed, root-folder-published, referenced from vulnerabilities and assets rather than re-created per scope.
The Common Weakness Enumeration is MITRE's catalogue of software-weakness categories — buffer overflows, missing authentication, improper input validation, and so on. Where a security advisory is "this specific vulnerability in this specific product", a CWE is "this class of flaw". CWEs are catalogued separately and tagged onto advisories and vulnerabilities to enable categorical analysis ("how many of our open vulns are credential-handling bugs?").
CWEs ship as their own catalog library; loading the CWE library makes the entries available across the platform.
A vulnerability in CISO Assistant is the organisation-specific record — "we have this exposed in our environment, here's the SLA". It links to:
One or more security advisories — the upstream finding(s) it corresponds to.
One or more CWEs — the weakness categories it belongs to.
This linkage is what lets enrichment feeds work end-to-end: advisories get scores from EPSS, exploitation status from KEV, and category mappings from NVD; vulnerabilities inherit that context via their advisory links and surface it in dashboards and SLA prioritisation. See .
Three public feeds enrich the catalogue when enabled — they keep the threat-intel layer current without manual data entry. Configuration is under :
KEV feed — CISA's Known Exploited Vulnerabilities; flags advisories under active exploitation.
NVD enrichment — pulls extra metadata from the NIST National Vulnerability Database (CWE mappings, affected configurations, references).
Each feed is a separate flag and can be toggled independently. The platform stores feed status, network timeout, and last-update timestamps centrally.
The threat-intelligence surface is being expanded to cover full STIX-style threat intelligence — threat actors, attack patterns, indicators of compromise, campaigns, sightings — along with STIX 2.1 / TAXII / MISP / OpenCTI interop and DORA cyber-threat notification workflows. The objects on this page remain the entry point; new SDOs will sit alongside them.
Aliases — cross-references to other identifiers when an advisory exists under multiple IDs.
A metric definition is the catalog template — qualitative (level) or quantitative (number with unit) — and can be shipped via a library. Each instance scopes one definition to a domain and carries the operational fields: assignee, target value, collection frequency (auto-stale logic kicks in when the latest sample exceeds the cadence + grace period). Samples are timestamped data points against an instance. Dashboards are independent of the metrology pipeline: they comprise widgets, and each widget either reads a custom metric instance, pulls from a builtin sample (a system-computed daily snapshot attached generically to any tracked object — assets, audits, projects, incidents, etc.), or just displays free text.
User-facing
Internal
Notes
Consider metric definition as a template of your metric. It serves as the guideline of the actual metric instance that you will track accross your domains.
You will learn how to import off-the-shelf metrics definitions on CISO Assistant.
Click "Metric definitions".
Click here to proceed to the next menu.
You can also preview the content before importing it.
Click "Metric definitions" to revisit the main metrics page.
Click "Search..." to begin finding specific metrics.
You can now instantiate this metric as you see fit for your domains.
You can now instantiate this metric as needed across your domains. Keep in mind that you can also create your own metric definition directly without going through the library.
Metrics can be quantitative (number with unit) or qualitative (choices):
You can add your options during declaration or afterward:
The "higher is better" setting is used to indicate if the trend is a good thing or not.
The metric instance is the projection of the definition to a specific domain and it's what you'll be tracking.
Parameters:
Metric definition (it will inherit its settings)
Domain: the scope of this metric
Status: lifecycle of the metric. The stale is specially interesting as the application will auto-toggle it according to the data freshness
This is the actual data of the metric instance on a given timestamp.
Keep in mind that you can add the data manually or through all the supported integrations (API, n8n, etc.). Note that data cannot be in the future.
Dashboards are the visual representation of the metrics and support:
custom metrics instance (multiple charts)
built-in metrics (multiple charts)
markdown text widget
In edit mode, you can add different widgets, place and resize them as you see fit:
Once done, you can go back to view mode to see the result:
In addition to the custom metrics for your internal KPI and KRI, you can also include some of the built-in metrics tracked by the platform:
Those are updated on each change of your data and tracked as daily metrics.
Sample
CustomMetricSample
Timestamped data point on an instance
Builtin sample
BuiltinMetricSample
System-computed; daily; ContentType GFK to any object
Dashboard
Dashboard
Container
Widget
DashboardWidget
KPI / donut / pie / bar / line / area / gauge / sparkline / table / text
Collection frequency: expected collection frequency, on which we add a grace period before toggling the metric to stale status
Target value: expected target of this metric for this specific domain. This is handy as you can have different targets of the same metric definition according to the domain.
Assigned to: actor responsible for the metric instance and its updates.
Metric definition
MetricDefinition
Catalog template; optional library FK
Metric instance
MetricInstance
One definition × one domain
Mental model
Metric definitions
Importing definitions
1. Introduction
2. Open Metric Definitions
3. Click import
4. Import the library matching your criteria
5. Return to Metric Definitions
6. Look for the metric definition you want
7. Select The Specific Metric
Creating a definition
Metric instance
Metric sample
Dashboards
Introduction
Open Metric Definitions
Click import
Import the library matching your criteria
Return to Metric Definitions
Look for the metric definition you want
Select The Specific Metric
Quantitative metric
Qualitative metric
Assets and resilience
Operations
Asset classes
A shared taxonomy for classifying assets, extensible per organisation
An asset class answers "what kind of thing is this?" for an asset — a server, a business process, a set of personal data. Classes form a tree, so a class can be a refinement of a broader one, and an asset points at most one class.
CISO Assistant ships with a taxonomy derived from the CIS asset categories (Devices, Software, Data, Users, Network, Facilities, Documentation, Business Process, and their sub-classes). You can extend that tree with your own classes, and hide the shipped ones you don't use.
Why it exists
The class is deliberately separate from the domain an asset lives in and from its primary/supporting type. Those three answer different questions:
Question
Field
Keeping them apart means you can classify a supporting asset in one domain and a primary asset in another under the same class, and then read your whole estate by class regardless of who owns it.
A class refines a parent class, forming the taxonomy tree. An asset is classified by at most one class — the link is optional, so an unclassified asset is valid and stays fully usable. The domain still scopes the asset; classes are organisation-wide and carry no access control of their own.
User-facing
Internal
Notes
The shipped classes are marked Builtin. They are re-created every time the platform starts, which is why they behave differently from classes you create:
They cannot be renamed or deleted — a rename would be undone and a deletion would silently reappear on the next restart. The edit form states this: "Built-in classes are provided with the product: they can be hidden, but not renamed or deleted."
They can be hidden. Clearing Visible removes a class from the picker used when classifying an asset, without touching any asset already classified under it. The form notes: "Hidden classes stay listed here but are no longer offered when classifying an asset."
Classes you create are yours entirely — rename, re-parent, hide, or delete them (see ). Creating a class under a built-in one is the intended way to extend the shipped taxonomy: the child is your own class, and the built-in parent stays untouched.
Hiding a class that still has visible sub-classes does not orphan them. The class remains in the picker as a non-selectable grouping row so its descendants keep a readable path.
In the sidebar under Extra → Asset classes.
The table lists Name, Parent class, Description, Translations, Visible and Builtin. Filters let you narrow to visible or built-in classes.
Use the add button on the Asset classes page. The form asks for:
Name — free text.
Description — optional.
Parent class — optional. Leave it empty to create a top-level class. The picker is a tree, and it excludes the class you are editing along with everything beneath it, so a class can never become its own ancestor.
To add a child directly under an existing class, open that class and use the add button on its children table: the Parent class is filled in for you.
Built-in class names are translated by the interface itself and follow the language you are using. Classes you create are free text, so they need their own translations.
The Translations editor on the class form takes a Name and a Description per language. When you are using a language that has no entry, the class falls back to the name you typed when you created it. That fallback is why a class can look "untranslated": it means no entry exists for your current language, not that translation is unavailable.
Deleting a class also deletes every class beneath it. The confirmation dialog lists exactly what will go, so check it before confirming.
Assets classified under a deleted class are never deleted — they simply become unclassified. They keep every other property, and you can re-classify them afterwards.
On the asset form, the Class field is a searchable tree. Only visible classes are offered. Type to search across the whole taxonomy, or expand the tree to browse it; the selected class shows its full path so you can tell two same-named classes apart.
The Assets by class page turns the taxonomy into a way to navigate your estate. Reach it from the Assets page using the tree icon.
Each class shows a count of the assets beneath it, rolled up through the tree, so a parent class with no assets of its own still shows what its children hold. Expanding a class lists the assets classified directly in it; classes with no assets at all are hidden unless you clear Hide empty classes.
Assets with no class appear in a separate Unclassified group at the bottom — a useful starting point when you are working through a backlog of unclassified assets.
Only assets you are allowed to see are counted or listed, so the totals on this page are yours, not the whole database's.
Asset exports include an asset_class column holding the class's full path, with each level separated by / — for example assetClassDevices/assetClassEnterpriseAssets/assetClassServers.
The path is used rather than a display name because it is unambiguous (two classes may share a name under different parents) and stable across instances and languages. Imports accept the same format in an asset_class column, and also accept a bare class name when only one class in the tree carries it.
If an import names a class that does not exist, the asset is still imported — it simply arrives unclassified, and the row is reported as a warning.
Domain exports carry asset classes the same way. When you import a domain into another instance, any of your own classes that the target does not have are re-created, while built-in classes bind to the ones already there.
— a different organisation-defined override
— confidentiality labelling, not typology
Built-in
builtin
Shipped with the product
Visible
is_visible
Controls whether the class is offered when classifying
Translations — see below.
Visible — on by default.
Who owns and can see it?
Domain
Does it deliver value directly, or support something that does?
Type (primary / supporting)
What kind of thing is it?
Class
Asset class
AssetClass
Self-referencing tree via parent
Class (on an asset)
Asset.asset_class
Optional; a single class per asset
Mental model
Built-in versus your own classes
Managing classes
Creating, editing and deleting asset classes requires administrator rights. Other roles can see the taxonomy and use it to classify assets, but not change it.
Creating a class
Translating class names
Deleting a class
Classifying an asset
Browsing assets by class
Assets load in batches as you expand a class, with a Load more control on large groups. The page is built to stay responsive on estates with tens of thousands of assets.
An asset is anything of value worth protecting. Assets are first-class objects in CISO Assistant, decoupled from any specific risk study or audit, so the same asset can participate in many analyses without being duplicated.
Assets are always defined by the organisation and can be attached to the global domain or to a specific domain.
Mental model
The asset is a hub other surfaces point at: risk scenarios impact it, vulnerabilities affect it, incidents affect it, and a Business Impact Analysis assesses it (through an intermediate AssetAssessment row, one per asset in the BIA). The supports self-loop captures the primary/support hierarchy — a support asset is recorded as a child of its primary parent through parent_assets.
User-facing
Internal
Notes
Primary assets are core resources directly contributing to the organisation's main objectives — business processes, data, intellectual property.
Supporting assets indirectly aid primary functions — IT systems, services, locations, people.
The distinction matters for risk work: scenarios typically express what can happen to a primary asset via which supporting assets are involved.
An applied control is the main building block of the action plan: the actual action your team has implemented or will implement to address a security need. It can be technical, organisational, a process, a policy, a piece of documentation — anything that materially changes risk or compliance posture.
A single applied control can satisfy any number of requirements across any number of frameworks — it's where what the framework asks meets what the organisation actually does.
Mental model
The applied control sits at the centre — anything that asks for action points to it; everything that proves the action took place hangs off it. Compliance work (requirement assessments), risk work (scenarios), follow-up work (findings), and operational maintenance (tasks — periodic reviews, evidence refresh, assignee rotation) all reference the same control, while evidences and protected assets accumulate on the other side. This is the decoupling principle made concrete: one applied control, many demand-side users.
User-facing
Internal
Notes
Applied controls are fundamental for both compliance and remediation. They can derive from a reference control for consistency, or be created independently. They are always defined by the organisation and can be attached to the global domain or to a specific domain.
The status field is what turns an applied control from a static catalogue entry into a live, trackable piece of work. It's the single signal that drives roll-ups across audits, dashboards, action plans, and reporting — so it's worth understanding how it moves.
The target state of an applied control is Active, not Done. This is deliberate. A control is never finished in the way a project task is finished: a backup policy that's been written and approved still has to keep being followed, a firewall rule that's been deployed still has to keep being enforced, an access review that's been run still has to be run again next cycle. Reaching Active doesn't end the work — it starts the maintenance phase. The work shifts to keeping the control in Active, through periodic reviews, evidence refresh, and the that "maintain" it.
This is why the lifecycle has Active, Degraded, and Deprecated rather than Done. A control that's failing partially moves to Degraded (signal to act); a control that's no longer needed moves to Deprecated (retire with history intact). There's no terminal "done" state on purpose — the whole point of cybersecurity controls is that they're a continuous effort to stay in Active, not a checkbox you tick once.
The lifecycle runs through seven states:
Status
Meaning
Counts as "in place"?
Why the lifecycle matters across the platform:
Risk model — Current risk uses controls in active (and partly degraded); residual risk also factors in controls in to_do / in_progress (the planned ones). Moving a control from to_do to active is what closes the gap between residual and current.
The transitions aren't enforced as a strict state machine — you can move a control between any two statuses — but staying within the lifecycle above makes audit trails and analytics meaningful.
Applied controls carry a structured cost field, so the financial weight of your security programme isn't a separate spreadsheet — it's attached to the controls themselves and rolled up wherever they appear (most notably on every ).
The platform models the build vs run distinction that controls actually carry in practice:
People days — a measure of internal effort. The platform converts this to money using the daily rate configured in General settings, so internal time is comparable with external spend.
From these inputs, the platform computes an annual cost for every control:
Symbol
Meaning
That single number is what feeds every roll-up — the action plan's budget overview, the per-assignee and per-domain breakdowns, the by-status / by-priority / by-CSF-function totals. Controls without a cost set contribute zero, so partial adoption of the financial tracking still works — you fill in numbers where you have them and the totals reflect what's known.
The annualised view matters because cybersecurity controls are continuous (see above). A capex-style "we paid €40k once" view doesn't compare well across controls; an "annual cost in steady state" view lets you put a single line per control on the budget and compare them like-for-like.
A reference control is a template for an applied control. Reference controls facilitate the creation of applied controls and help keep them consistent across the organisation.
They can be provided by security frameworks imported from a library, or you can create your own — in the global domain or in a specific domain. Reference controls are optional but recommended.
A policy is a specific type of applied control: a document describing what is expected from some part of your stakeholders. Putting your cybersecurity policies in CISO Assistant makes them readily available for audits, and lets you manage their lifecycle alongside the rest of your controls.
M2M; one evidence can prove several controls
In progress
Being implemented
No
On hold
Started then paused — blocked, deprioritised, or waiting on a dependency
No
Active
Implemented and operating as intended
Yes
Degraded
Was active, now partially failing — gap detected at audit or in operations
Partial
Deprecated
Retired or superseded — no longer in scope
No
Action plan — the Kanban view and the Control Plan grid both swimlane by status. A control sitting in on_hold for too long is the signal to escalate.
ETA enforcement — when ETA passes and the status isn't active, the control is flagged overdue. Reaching active is what stops the clock.
Framework / audit roll-ups — compliance percentages and analytics treat active as "in place" and degraded as a partial signal that wants follow-up. Other statuses don't contribute coverage.
Deprecation — preferred over deletion. A deprecated control keeps its history, evidence, and links to the requirements it once satisfied — useful for past-audit traceability — without polluting current views.
Security and operational events tracked from detection through resolution
An incident is a security or operational event being investigated or responded to. CISO Assistant treats incidents as first-class objects so that detection, response, evidence, and the controls that should prevent recurrence all live in one place.
Incidents are deliberately distinct from related concepts:
A risk scenario is a potential adverse event — the possibility.
A vulnerability is a weakness that could be exploited — the gap.
An incident is something that has actually happened and is being handled.
An incident lives in a domain and aggregates everything about the event: the assets it affected, the threats it's attributed to, the actors handling it, the applied controls invoked during response, and any task templates set up for follow-up work (post-mortem, control review). Timeline entries are the append-only log of what happened and when — detection, mitigation, observation, severity / status changes.
User-facing
Internal
Notes
Identifiers and classification — a name, an optional reference ID, the severity (critical / major / moderate / minor / low / unknown), and the status through its lifecycle.
Timing — when it occurred, when it was reported, when it was resolved.
Detection — internal vs external, optionally with a link to the source signal.
Incidents follow a five-state lifecycle:
new → ongoing → resolved → closed (or dismissed at any point if the event turns out not to be a real incident)
State transitions are recorded in the incident timeline, an append-only log of significant moments: detection, mitigation, free-form observation, severity changes, status changes. The timeline is what an auditor or a post-mortem author will read to reconstruct what happened.
For regulated tenants, CISO Assistant ships a DORA incident report — a structured form aligned with the Digital Operational Resilience Act notification requirements (initial, intermediate, and final reports). It draws from the underlying incident but adds the regulatory fields and timing that DORA prescribes.
Actor
Actor
XOR(User / Team / Entity)
Task template
TaskTemplate
Spawns occurrences
Timeline entry
TimelineEntry
Append-only response log
Scope — the affected assets, the threats believed to be in play, the entities (third parties) involved.
Assignees — the actors handling the response.
Response and qualifications — qualifying terminology, BCP-activation flag, resolution notes.
Linked controls and tasks — the applied controls invoked during response, plus the task definitions that should run as follow-up (e.g. a post-mortem, a control review).
Recurring and one-off operational work tracked against assignees, schedules, and evidence
Tasks are how CISO Assistant tracks the operational work that keeps controls effective: the weekly access review, the monthly backup test, the quarterly policy refresh, the one-off offboarding checklist. They are deliberately separate from applied controls — a control says what is done; a task says that someone has actually done it on a given date.
The task template is the definition — assignee, recurrence rule, expected evidence — and the task occurrence is the actual unit of work scheduled from it. The schedule is a JSON field describing the cadence (DAILY / WEEKLY / MONTHLY / YEARLY with the usual iCalendar refinements). Templates can be wired to many other objects — applied controls being the canonical one (the "maintains" semantics: this work keeps that control healthy) — and when an occurrence is completed, the evidence revision it produces is back-linked through task_node so the audit trail closes the loop.
User-facing
Internal
Notes
Task definition / template
TaskTemplate
One spec, one schedule
Task occurrence
TaskNode
One scheduled run
Schedule
schedule JSON field on the template
iCal-style recurrence rule
Tasks come in two layers, mirroring the template → instance pattern used elsewhere in the platform:
A task definition is the reusable spec — the title, description, assignee, expected evidence, and the recurrence rule (every Monday, the 1st of each month, every 90 days). It says what should happen and how often. Internally a TaskTemplate.
A task occurrence is a single scheduled run produced from a definition — the actual row with a due date, a status (pending → in progress → completed / cancelled), and the evidence collected when the work was done. Internally a TaskNode.
A one-off task is just a definition that produces a single occurrence.
Define. Create a task definition with the assignee, the cadence, and what's expected when the task runs.
Schedule. Occurrences are materialised from the recurrence rule. The platform creates them as their due dates come into view, so the list of upcoming work is always visible.
Work the occurrence. When a due date arrives, the assignee opens the occurrence, records what they did, attaches evidence, and marks it completed.
Iterate. Edit the definition to adjust the cadence, assignee, or expected evidence — existing occurrences keep their state; future occurrences pick up the change.
The point of tracking tasks inside the platform — rather than in a calendar or ticketing system — is so that the evidence of execution lives next to the rest of your compliance record. An auditor asking "show me proof of monthly backup testing" can be answered by pointing at the task and walking through the completed occurrences with their attached evidence.
A business impact analysis (BIA) measures the operational, financial, and reputational consequences of disrupting specific assets or business processes — the input to any serious resilience plan.
Where a risk assessment asks "what could go wrong?", a BIA asks "if it does, how bad is it, and how quickly does it become unacceptable?".
Mental model
A BIA is an assessment scoped to a perimeter. For each asset in scope, the platform captures:
Asset assessment — the impact of disruption along one or more dimensions (financial, operational, regulatory, reputational).
Escalation thresholds — the durations after which disruption becomes severe, critical, or unacceptable — often expressed as RTO (Recovery Time Objective) levels.
The output is a prioritised view of which assets your continuity plan must protect first.
BIAs read from the asset inventory — assets are first-class objects, not BIA-private records.
The criticality output can drive risk scenario prioritisation for the same assets.
For organisations subject to DORA, BIA results feed into incident-reporting workflows.
A policy is a specific type of applied control: a document describing what is expected from some part of your stakeholders — an acceptable-use policy, a password policy, a data-classification policy, an incident-response procedure, anything that defines how things should be done.
Because policies are applied controls under the hood, they inherit the full applied-control machinery: they live in a domain, have an assignee and a status, link to the requirements they satisfy, and carry evidence.
Why give policies their own page
Policies are central to most compliance frameworks — almost every requirement expects a documented policy as part of the substantiating artifacts. Pulling policies onto a dedicated page lets you:
Maintain the catalogue of published documents independently of the broader action plan.
Track policy review and approval cycles separately from operational controls.
Surface policies by domain when responding to audit requests or external reviews.
The Policies page in the platform is a filtered view of applied controls where the type is policy; everything you can do to an applied control, you can do to a policy.
Policies can come from either side of the divide:
Author in CISO Assistant. Each policy can carry one or more managed documents — versioned documents tracked in-platform through a draft → in-review → validated → published → deprecated lifecycle. Useful when you want the policy text to live where the rest of the GRC programme lives, with revision history and approval workflow attached.
Bring an existing document. If a policy already exists as a file, or lives in Confluence, SharePoint, or a DMS, its managed document can be uploaded (the file itself) or linked (a pointer to where it lives) — the same lifecycle, versioning, and links apply, without duplicating the source of truth.
Both paths are first-class — you can mix them across an organisation, or even across policies in the same domain. The applied-control machinery (assignee, status, linked requirements, evidence) is the same either way.
For policies authored in CISO Assistant, every change produces a new revision of the managed document rather than overwriting the previous text. Each revision carries:
A monotonically increasing version number (v1, v2, …).
A revision status (draft / in review / change requested / validated / published / deprecated).
A timestamp, the actor who edited it, and the content at that point in time.
The full revision list is reachable from the version history sidebar on the document page — you can switch between revisions to read any past version exactly as it was published.
Two diff views help reviewers and approvers see what actually changed:
Diff between two revisions — pick a "from" revision and a "to" revision in the history sidebar and the platform renders the textual differences between them. Useful for periodic reviews ("what changed between v3 and v6?") and for the approval workflow ("show me the delta the requester is asking me to validate").
Edit diff (within a revision) — while a draft is being worked on, the platform also tracks the diff between the last loaded state and the current edits in the editor. This lets the author see exactly what they're about to commit before they save the increment.
The diff is computed on the document content itself — it's not a binary file diff, so it works well for the plain-text or Markdown policies typically authored in-platform. For policies attached as external files (PDF, DOCX, etc.), version history is preserved through evidence revisions, but the inline diff view is not available.
Policies have their own lifecycle — drafting, review, approval, publication, periodic review, retirement. The platform tracks these states through the standard applied-control status field and the supporting evidence on each policy entry. When authoring in-platform, the managed-document revision states (draft, in review, validated, published, deprecated) provide a finer-grained workflow on top.
A findings assessment — called Follow-up in the UI — tracks the issues raised by a review and drives their remediation through to closure. Findings can come from a CISO Assistant audit, an internal security review, a penetration test, an external assessor's report, or any other source.
It's the place where the action plan meets reality: each non-compliance, observation, or recommendation gets an assignee, a due date, and a status, and is followed all the way to "fixed".
Mental model
A findings assessment is an assessment scoped to a perimeter, in the same family as audits, risk assessments, and business impact analyses. Inside it sit individual findings, each with:
A severity, often aligned with the assessor's severity scale.
An assignee and a due date.
A status from open → in progress → remediated → closed.
One or more linked applied controls that address it.
The same findings model serves several sources:
Audit findings — non-compliances or partial compliances raised during an audit, especially when extended results are enabled (minor / major nonconformity, observation, opportunity for improvement, good practice).
Internal reviews — self-imposed checks outside the formal audit cycle.
Because the model is uniform, dashboards aggregate across sources: you can see all open findings due this quarter, across all reviews, without preselecting which kind of review they came from.
Findings link to applied controls — closing a finding usually means standing up or updating one or more controls. The link is many-to-many: a single control can close several findings, and a single finding can require several controls.
Document management lets you author, version, and publish documents — policies, procedures, charters, records, meeting minutes — directly inside CISO Assistant, so the text of your governance programme lives where the rest of your GRC data lives, with revision history and an approval workflow attached.
Documents can be authored in-platform (a Markdown editor with a draft → published lifecycle), uploaded (an existing PDF or file carried through the same lifecycle), or linked (a pointer to a document that lives in another system). Whatever the source, a document can be classified, linked to the objects it governs, and referenced by other documents.
Document management is controlled by the document_management (on by default). The Documents reading catalogue, the document list, and templates all live behind it.
Mental model
A document container is the language-independent identity of a document: it holds the document's type, its domain, its classification, and its links. Under it sit one managed document per language, and each managed document is a chain of document revisions — one per saved increment. A new document can be seeded from a template, classified with a level, reference other containers, and be linked to the objects it governs. Solid edges are always present; dashed edges are optional.
In the UI
Internally
Every document has a Document type, used to group the reading catalogue and to scope the template picker. The built-in types are Policy, Procedure, Charter, Record, Meeting minutes, and Other.
A document's content comes from one of three sources, chosen when you create it:
Authored — written in the in-platform Markdown editor (formatting toolbar, live preview, insert links to other documents). The published version is rendered to a PDF snapshot.
Uploaded — an existing file (e.g. a signed PDF) attached to the document. It runs through the same lifecycle and version history, but is served as-is rather than rendered from Markdown.
Linked — a pointer to a document that lives in another system (Confluence, SharePoint, a DMS, …). The document carries the URL through the same lifecycle; readers open it in place rather than reading content inside CISO Assistant.
All three share the same versioning, lifecycle, languages, classification, and links — they differ only in where the content lives.
Each language version moves through its own lifecycle — Draft → In review → Change requested → Validated → Published → Deprecated — and every saved change produces a new revision (v1, v2, …) rather than overwriting the previous text. The Version history sidebar lets you read any past revision and diff two of them.
The lifecycle, revision history, and diff mechanics are shared with policy documents and are described in detail under .
A single document can carry a version per language. One language is the default (used for the catalogue title and status); the others are translations. Each language version has its own lifecycle, so a French translation can still be in draft while the English original is published.
When you link one document to another from inside the editor (Link to document), CISO Assistant records the edge automatically. Each document then shows:
References — the documents this one points to.
Referenced by — the documents that point at this one.
These are computed from the content — there is no separate list to maintain, and they stay accurate as the text changes.
A document can be linked to one or more Policies, Applied controls, Task definitions, or Processings. The link is associative — it never changes the document's domain or publication state — and it is bi-directional: the linked object shows a Documents panel listing the documents attached to it.
This is how a policy or a control points at the document that describes it without duplicating the text. See .
A document can carry a Classification — a level from an scheme such as TLP. Once set, the level shows as a coloured badge on the reading catalogue, in the reader, and in the documents table, and it is stamped on every page of the document's exported PDF (for example TLP:AMBER). Classification is optional and independent of the document's type, domain, and lifecycle.
New authored documents can start from a document template — a reusable Markdown body, chosen in the editor's template picker. The picker only offers templates matching the document's type and language. See the .
The Documents page is a read-oriented catalogue: published documents shown as tiles, grouped by type, with search and type filters. It is the place to browse and read the published corpus, separate from the authoring workflow.
A risk assessment (also called a risk study) is a scenario-based evaluation of risk over a perimeter. CISO Assistant supports qualitative approaches (configurable risk matrices), quantitative approaches (Monte Carlo over loss distributions), and the structured EBIOS RM methodology.
The platform follows the ISO 27005 risk-management workflow.
ISO 27005 risk management workflow
Mental model
A risk assessment always lives inside a domain (the mandatory IAM scope) and is bound to one risk matrix that supplies the probability × impact scale (the matrix can be swapped later; existing scores are clamped to the new scale). A perimeter can optionally narrow the assessment to a specific service or process inside the domain. The assessment is composed of risk scenarios; each scenario links to the assets it impacts, the threats it materialises, and the applied controls that mitigate it (split between existing and planned to drive the three-tier risk model below).
User-facing
Internal
Notes
A risk assessment encompasses three steps:
Risk identification — defining the risk scenarios.
Risk analysis — assessing probability, impact, and strength of knowledge for each scenario.
Risk evaluation — done automatically based on the selected risk matrix.
In CISO Assistant, risk treatment is combined with the risk assessment rather than tracked as a separate phase.
Scenarios can be defined directly from the risk-assessment view or separately via the scenarios view. The same scenario can be reused across multiple studies.
CISO Assistant tracks three risk levels for each scenario, reflecting where the organisation stands along the treatment journey:
Inherent risk — the natural level of the scenario without any controls in place. The starting point. Surfaced in the UI when the inherent_risk feature flag is on.
Current risk — the level given the applied controls already in place. The state of risk today.
Residual risk — the level expected once all planned
Each level has its own probability, impact, and overall level fields. The assessment's consistency check flags a scenario whose residual risk exceeds its current risk (on level, probability, or impact), and also flags a residual lowered below current when no applied control justifies the reduction.
Risk acceptance is when an organisation or individual decides to tolerate a certain level of risk without taking further action to reduce it. CISO Assistant provides a workflow to capture formal approval of risk acceptances by management — the approver must hold the Approver role.
For the formal definition, see .
Risk levels are calculated as a function of the probability and impact of a scenario, using a configurable risk matrix. Matrices are imported from libraries — pick one of the built-in matrices or define your own via a custom library.
Most organisations define an official matrix to be used for all risk assessments, but CISO Assistant lets you choose a different matrix per assessment when needed. The matrix can be changed after the assessment has been created. When you do, each scenario's existing probability and impact values are clamped to the new scale's bounds — a score that falls outside the new range is clipped to the nearest valid value, and unrated scenarios stay unrated. There is no proportional rescaling, so review the scenarios afterwards and correct any score that no longer reflects your intent.
RiskMatrix
Can be changed; existing scenario scores are clamped to the new scale's bounds
Domain
Folder
Required; drives IAM scoping
Threat
Threat
Catalog entry from a library
applied controls have been implemented. The target state, and the figure used in risk-acceptance decisions.
A quantitative risk study evaluates risk in monetary terms — the expected annualised loss from each scenario — using probabilistic methods rather than a qualitative matrix.
It's the sibling of qualitative risk assessment and EBIOS RM: same problem (what could go wrong, how bad would it be), different lens (statistics rather than categories).
Mental model
A study is the container for one quantitative analysis. It comprises scenarios — each one a discrete risk being modelled — and each scenario comprises one or more hypotheses, typically one per risk stage (inherent / current / residual). The hypothesis carries the probability and impact distributions plus the applied controls it assumes are in place — split into existing / added / removed sets so the delta between stages is explicit. Scenarios reference the assets they impact and the vulnerabilities they exploit, mirroring the qualitative side of the platform.
User-facing
Internal
Notes
Each scenario in the study is parametrised by one or more hypotheses:
A loss-event frequency distribution — how often the bad thing happens per year, expressed as a distribution rather than a point estimate.
A loss magnitude distribution — how much it costs when it happens, also as a distribution.
The platform runs Monte-Carlo simulation over those distributions and derives the loss exceedance curve (LEC) plus aggregate metrics: expected loss, value-at-risk, tail loss.
You need to compare risk against budget — "should we spend €X on control Y?" becomes tractable when both sides are in euros.
You need to talk to the board or finance about risk in the language they speak.
You have enough data — or enough informed judgement — to bound the loss distributions.
Qualitative methods stay useful for everything else.
— click walkthrough for running a study.
— the math behind the LEC, VaR, expected shortfall, ROSI, and tolerance overlay.
Study
QuantitativeRiskStudy
Container; carries risk tolerance + loss threshold
EBIOS RM (Expression des Besoins et Identification des Objectifs de Sécurité — Risk Manager) is the structured risk-management method published by the French national cybersecurity agency, ANSSI.
CISO Assistant supports EBIOS RM natively, with a dedicated object graph rather than forcing the method into a generic risk-assessment shape.
The five workshops
EBIOS RM organises a study around five workshops:
Scope and security baseline — define the studied system, its mission, and the regulations it must comply with.
Risk origins and target objectives — identify who might attack and what they want (the RO/TO couples).
Strategic scenarios — model attack paths through stakeholders to reach target objectives.
Operational scenarios — drop into technical detail: kill chains, attacker techniques, supporting assets touched.
Risk treatment — score residual risk and plan the action plan.
An EBIOS RM study lives in a domain (no perimeter — the study itself is the scope envelope). It unfolds through five workshops that produce, in order: feared events (undesirable outcomes on primary assets) and stakeholders in workshop 1; RO/TO couples (Risk Origin × Target Objective) in workshop 2; strategic scenarios showing high-level attack paths through stakeholders in workshop 3; and operational scenarios drilling into kill chains, operating modes, and elementary actions in workshop 4. Workshop 5 reuses the platform's standard objects — applied controls, evidence, residual risk — to treat both strategic and operational scenarios.
User-facing
Internal
Notes
EBIOS RM scenarios sit alongside qualitative risk scenarios in the same risk register: both contribute to the residual-risk picture for a perimeter, and both can be treated with the same applied controls.
Stakeholder
Stakeholder
Workshop 1 — internal/external party with trust + dependency scores
RO/TO couple
RoTo
Workshop 2 — Risk Origin × Target Objective
Strategic scenario
StrategicScenario
Workshop 3 — uses an attack path through stakeholders
Catalogued weaknesses tracked through detection, triage, and remediation
A vulnerability is a weakness in a system, process, or product that could be exploited to compromise confidentiality, integrity, or availability. CISO Assistant tracks vulnerabilities as first-class objects, separately from the incidents they may cause and the risk scenarios they feed into.
The vulnerability surface answers two operational questions: what's exposed and are we treating it fast enough?
Mental model
A vulnerability sits inside a domain and points outward at what it affects (assets) and what's treating it (applied controls). Threat-intel feeds enrich it asynchronously — KEV/NVD/EUVD feeds attach security advisories, NVD enrichment tags it with CWEs. The vulnerability SLA policy is a single platform-wide setting that maps severity to a deadline: when severity changes and no explicit due date has been set, the platform recomputes the due date from the policy.
User-facing
Internal
Notes
Identification — a name, an optional reference ID (typically a CVE), a description.
Severity — the shared scale (undefined / info / low / medium / high / critical).
Status — undefined, potential, exploitable, mitigated, fixed, not exploitable, unaffected.
Vulnerabilities are unusual in that the platform sets a due date automatically based on severity, via the . The flow:
A vulnerability is created (or its severity changes).
If no explicit due date is set, the SLA policy is applied — high severity gets a tighter deadline than medium, and so on.
The bulk "refresh due dates" action re-applies the policy across existing rows when the SLA configuration changes.
Explicit due dates the user (or an import) sets are preserved — the policy only fills in the blank.
Vulnerabilities don't live in isolation. CISO Assistant can pull from external feeds to enrich them automatically — see :
KEV feed flags vulnerabilities confirmed exploited in the wild.
EPSS feed attaches a probabilistic exploitation score.
NVD enrichment pulls CWE mappings, affected configurations, and references.
These enrichments link a vulnerability to its security advisory and CWE entries, making severity triage less guesswork.
The status field captures where each vulnerability sits in that flow; the linked applied controls and security exceptions explain how it's being treated.
Severity → days mapping
Affected scope — the assets exposed, and the entities (third parties) involved when relevant.
Treatment — linked applied controls (the remediations), security exceptions (formal deviations), CWE entries, and one or more security advisories.
Timing — detection date, publication date, ETA, and the SLA due date computed from the severity-driven policy.
An audit is the evaluation of a perimeter against a framework. It produces a per-requirement view of status, score, evidence, and the applied controls that substantiate each requirement.
Because applied controls are decoupled from compliance requirements, a single set of controls can be evaluated against many frameworks in parallel without re-doing the work.
An audit always lives inside a domain (the mandatory IAM scope) and is assessed against one framework. A perimeter can optionally narrow the audit further — e.g. to a specific service or process inside the domain. On creation the platform spawns one requirement assessment per requirement in the framework — those rows are where status, score, and the supporting applied controls and evidences live.
User-facing
Internal
Notes
Audit
ComplianceAssessment
One audit = one framework × one domain (× optional perimeter)
Requirement assessment
RequirementAssessment
Per-requirement row inside the audit
Requirement
RequirementNode
Read-only catalog entry from the framework library
Domain
The fundamental input to an audit is a framework — a published standard such as ISO/IEC 27001:2022 or NIST CSF. Frameworks ship as YAML libraries. If you can't find one that fits your needs, you can build your own and import it.
An audit assesses compliance against the chosen framework. The evaluation of a single requirement inside an audit is called a requirement assessment.
A requirement assessment is not a single value — it captures several dimensions at once, separating the compliance result from how the work got done and from the depth of the implementation. The point is that the same row tracks the auditor's view, the analyst's progress, and the maturity of the underlying implementation without conflating them.
Every audit in the audit tables (and on dashboards and campaigns) shows a Progress percentage. It answers a single question: how much of the audit has been assessed?
What counts as assessed depends on which fields the audit exposes to the auditor. A single rule drives every surface (audit tables, the audit page, journeys, and My assignments), so the number never diverges between views:
Status mode — the default, and active whenever the workflow status field is visible to the auditor. A requirement counts as assessed only once its status is Done. Nothing else moves the needle: a requirement can already be Compliant or scored and still not count until it is explicitly marked Done. This makes progress the analyst's own "I'm finished with this one" signal.
Content mode — active when the status field is hidden. A requirement counts as assessed when:
for questionnaire requirements, the questionnaire is fully answered (every question answered), or it already carries a result or a score;
otherwise, when the result is visible, as soon as it leaves the default Not assessed state (Compliant, Partially compliant, Non compliant, or Not applicable);
otherwise, for scoring-only audits, when the score is strictly above the applicable minimum of the scale.
Enabling scoring pre-fills every requirement at the scale minimum, so a bare minimum score is treated as "not touched yet" and does not count. Only a score genuinely moved above the minimum counts as assessed, which stops empty maturity- or scoring-only audits from showing 100%.
In other words, progress is an auditing-activity signal, not a compliance signal. An audit can be 100% in progress and still be largely non-compliant — the column tells you the team has gone through every requirement and reached a verdict, not that the verdicts are good. The actual compliance picture lives in the donut and score read-outs computed from the compliance result below.
Only assessable requirements count toward the denominator — section titles and headings from the framework are skipped. When the audit is scoped to specific implementation groups, only the requirements inside those groups count, so progress reflects the work you actually committed to rather than the framework's full catalogue.
Several frameworks ship with their requirements pre-tagged into implementation groups (IG) — labels that act as filters over the requirement catalogue. They serve two recurring purposes, depending on how the framework's authors used them:
Maturity tiers — basic / intermediate / advanced (CIS Controls' IG1 / IG2 / IG3, CyFun's Basic / Important / Essential, FedRAMP's Low / Moderate / High). Picking IG1 narrows the audit to the minimum baseline; IG2 adds the next tier; IG3 expands to the full catalogue. This is the "how mature do we want to be?" axis.
Scope or applicability — slices like physical security, SaaS, cloud, or framework-specific selectors like ISO 27001's SoA group. Picking these narrows the audit to the requirements that actually apply to your context, regardless of maturity.
The two patterns are not exclusive — a single framework can mix them, and several IGs can be combined on the same audit. If no IG is selected the audit covers the full catalogue, and you can change the selection later (adding or removing IGs) without losing the work already done on requirements that stay in scope.
Where IGs show up in the platform:
Audit scope — selected at creation, editable afterwards. Anything outside the selection isn't dropped, it's simply hidden from the audit's assessable count.
Progress, score, donut, and analytics — all roll-ups respect the IG selection. The Implementation Groups Breakdown widget in audit analytics shows progress per IG when you want to compare tiers.
Framework report — the report filter lets you re-slice the audit by IG after the fact, so the same audit can produce an IG1-only view and a full-catalogue view without re-running the assessment.
See Multi-level support for the mechanic of selecting IGs at audit creation, and the framework library's own documentation for which IG taxonomy a given standard ships with.
The headline dimension — the actual answer to "does this requirement hold?". Each requirement carries one of:
Compliant
Partially compliant
Non compliant
Not applicable
This is the field that feeds the framework's compliance percentages, the report, and the cross-framework roll-ups.
For questionnaire-driven frameworks (whether authored in the library builder or imported from an Excel source — same vocabulary on both paths), the result is computed from the compute_result tag carried by each question choice and aggregated worst-wins across the requirement's questions, with not_applicable neutral.
Independently of the compliance result, each requirement assessment captures who is working on it and where they are in their process:
An assignee (an actor — user, team, or entity) — who is responsible for assessing this requirement.
A workflow status — To do / In progress / In review / Done. This is the analyst's status, not the auditor's verdict.
The two layers exist because the same requirement can be Compliant but still In review (the analyst has reached a conclusion, but a peer hasn't validated it yet) — and an In progress requirement obviously doesn't have a final compliance result yet. Splitting analyst progress from compliance result lets dashboards and the campaign view show meaningful "still to do" counts without polluting the compliance percentage.
When you enable extended results on the audit, each non-compliant or partially-compliant requirement can carry an additional qualification on a specific scale:
Major nonconformity
Minor nonconformity
Observation
Opportunity for improvement
Good practice
This is the auditor's grading language, useful when the framework requires distinguishing major from minor non-conformities (ISO certification audits being the canonical case). It's an extra layer attached to the result — not a replacement for it.
Beyond the binary compliance result, each requirement assessment can carry a score on the framework's scale. Scoring captures how mature or deep an implementation is, not just whether it exists. There are two ways to score, depending on what the audit needs:
Maturity score(single layer) — one score per requirement, typically used for CMMI-style or NIST-CSF-style maturity assessments.
Implementation + Documentation scores(two layers) — toggle on documentation score to split scoring into is this implemented? and is the implementation documented?. The platform computes the maturity score as the average of the enabled layers.
Each requirement assessment uses an effective scoring scale. At audit runtime the fallback is the audit's own scoring scale (ComplianceAssessment), which is usually initialised from the framework when the audit is created. A requirement can override that audit-level scale with its own min_score, max_score, and level labels. The scoring UI, documentation score, exports, and tree views use that effective scale for the requirement.
When an audit contains mixed scales, average-based roll-ups normalise each requirement against its effective range before aggregating, then display the result on the audit scale. Sum-based roll-ups stay raw: they add score x weight, and their maximum is the sum of each requirement's effective maximum times its weight.
If anchor N/A to target is enabled, not-applicable requirements contribute the audit target projected onto their own effective range. If no target is configured, they contribute their effective maximum.
Together with the compliance result, the analyst dimension, the extended results, and the scoring layers, a single requirement assessment can record: what's the compliance state, who is working on it and where they are, how severe any non-conformity is, and how mature the implementation is — all without conflating them.
Each requirement assessment can carry a thread of comments — short, dated, author-attributed notes used for in-context conversation during the audit. They sit alongside the formal fields and don't change the compliance result or the score; they're where the back-and-forth between the analyst, the reviewer, and the auditee happens (clarifications, follow-up questions, agreed-upon next steps).
Each comment has a body, an author, a creation timestamp, and an active / processed toggle so resolved threads can be filtered out of the default view without losing the history. Comments can be edited (the platform records the edited state), preserving who said what and when. The panel is collapsed by default and shows the comment count, so it stays out of the way until you open it.
Comments are not exclusive to requirement assessments — the same thread is available on risk scenarios, applied controls, and findings.
On audits, Comments is governed by two controls: the commentsfeature flag (the platform-wide master switch, default on) and the per-audit field-visibility editor, which lets you make the thread visible to respondents, auditor-only, or hidden. So you can keep comments enabled everywhere while still hiding the discussion from third-party respondents on a sensitive audit. Authors are also masked for third-party participants who can't see other users.
See Comments for the full feature reference — processed state, edit history, permissions, and author privacy.
Evidence justifies the status of a compliance requirement or proves that an applied control has been implemented. It can be a description, a link, or an uploaded file, and it can be attached to any number of applied controls or requirement assessments.
Formal sign-off workflow that routes assessments, policies, evidence, and other artefacts through one or more approvers
A validation flow (labelled Validations in the sidebar) is the platform's structured sign-off workflow. It takes one or more objects — an audit, a risk assessment, a policy, a security exception, a contract, an accreditation — bundles them into a single request, and routes that request to a designated approver for a formal decision. The decision and its context are preserved as an audit trail.
It's the answer to "who approved this, when, and on what evidence?" — the question that comes up in every certification audit, every internal review, and every regulator visit.
Mental model
A requester submits a validation flow for one or more objects in their domain. The flow carries optional request notes, a validation deadline, and is routed to a designated approver. The approver moves the flow through a status machine ending in one of: Accepted, Rejected, Change requested, Revoked, Expired, or Dropped. The original objects remain unchanged — what's recorded is the validation flow itself, with the link back to whatever was up for approval.
User-facing
Internal
Notes
There are two ways to start a flow, and they cover slightly different objects:
From the Validations list — click +, then expand the More section to attach objects. The picker offers Audits, Risk assessments, Business impact analyses, Findings assessments (follow-ups), Security exceptions, Processings (privacy), Accreditations, and Contracts (third-party). You can mix several types in one flow when they belong to the same approval decision (e.g. "approve this audit and the related exceptions").
From an object's own page — objects that carry a validation section let you submit the open object directly. This is the only way to route a Policy through a flow, and it's also available on audits, risk assessments, BIAs, follow-ups, exceptions, processings, accreditations, and contracts.
So the full set of objects that can go through a validation flow is: audits, risk assessments, business impact analyses, follow-ups, policies, security exceptions, processings, accreditations, and contracts.
The flow moves through a small state machine:
Status
Meaning
The status is the audit-trail field that gets cited in evidence reports — "policy v3 was Accepted by Jane Doe on 2026-03-12" is what makes the decision provable later.
Most ownership/assignment fields across the platform hold an actor (which can resolve to a user, a team, or an entity). The validation-flow approver is the deliberate exception: it's a foreign key to a User directly. Approval is a personal accountability act — "a named individual signed off" — and a team can't approve in the same way it can co-own work.
When the page surfaces validation flows, it wraps the user-only approver field so the team-broadening toggle still works: switching to "include team assignments" adds every team member's user ID into the approver filter, giving you a per-team queue without changing the underlying "one named user approves" semantics.
Each validation flow carries an optional, unique reference ID — useful when you need to cite the approval in external systems (a change-management ticket, a regulator submission, an internal governance record). The platform enforces uniqueness across the instance.
The pattern fits anywhere a decision needs formal, traceable sign-off:
A policy moving from validated to published — the approver records that the document is fit for release.
A security exception being granted — naming the risk-owner who accepted the residual risk.
An audit's results being signed off before publication to stakeholders.
It is not the right tool for casual review or peer feedback — for those, use comments, the requirement-assessment review status, or a . Validation flows are explicitly heavy: they exist so that the approval is preserved as evidence.
— typical objects routed through validation flows for publication
— lighter-weight review/remediation tracking
— formal risk acceptance is one of the most common reasons a validation flow is submitted
Requester
requester FK to User
The platform account that submitted the flow
Approver
approver FK to User
Foreign key to a User (not an actor) — approval is always personal accountability, even if assignment elsewhere can fan out to a team
Request notes
request_notes
Free-text explanation of what's being asked for
Validation deadline
validation_deadline
Optional date used for expiry and dashboards
Rejected
Declined — decision and rationale recorded
Revoked
Approval was rescinded after the fact (e.g. underlying conditions changed)
Expired
Validation deadline passed without a decision
Dropped
Requester withdrew the flow before a decision was reached
A risk assessment's residual-risk acceptance — the formal capture required by ISO 27005 and most internal risk frameworks.
A third-party contract going through procurement sign-off.
My assignments — where approvers see flows queued for them
Actors and teams — explains why approver is a user, not an actor
Validation flow
ValidationFlow
Lives in a domain; carries the request, the bundled objects, the deadline, and the decision
Validations (sidebar)
validationFlows i18n key
The list view label
Submitted
Sent to the approver; awaiting decision
Change requested
Approver wants edits before deciding; the requester reworks and resubmits
This tutorial guides you through managing minor nonconformities and major audit results within your audits
Go to your instance
1. Introduction
You will learn how to add extra attribute to your audit through extended result.
Introduction
2. Open your audit
Click "ISO 27002 SOA" to access the relevant audit for managing audit results.
Open ISO 27002 SOA Assessment
3. Enter Edit Mode
Click "Edit" to enable modifications on the selected audit.
Enter Edit Mode
4. Access More Options
Click "More" to reveal additional settings and options for the assessment.
Access More Options
5. Enable extended result
Click "on" to activate the desired feature or option within the assessment settings.
Click "Save" to apply and store the changes made to the audit.
Click "4.1 - Understanding the organization and its context" to examine specific requirements.
Select the appropriate category such as major nonconformity, minor nonconformity, observation, opportunity for improvement, or good practice.
Click the note stating "Major and minor nonconformities are only applicable when result is non-compliant or partially compliant." to understand criteria.
Click "Save" to store the additional nonconformity or observation details.
Click "Save" to finalize and save the observation or nonconformity information entered.
You have successfully managed minor nonconformities and major audit results by editing, documenting, and saving relevant audit details. This ensures accurate tracking and supports effective audit management for your organization.
7. Save audit Changes
10. Open a requirement
11. Choose the value for your extended result
14. Make sure it's consistent with the audit result
17. Select the value and save
21. Use the Observation field for more Information
Evidence is anything that substantiates a claim in CISO Assistant — that a control has been implemented, that a requirement is met, or that a process is being followed as described.
Evidence is the connective tissue between what the audit asks and what the organisation actually does.
Mental model
Evidence is the shared substantiation surface — the same record can back an applied control, a requirement assessment, and a finding at the same time, which is why a single proof can satisfy many frameworks at once. Each evidence comprises a chain of revisions; the latest revision holds the current attachment (with its SHA-256 hash) or external link. When a recurring task produces evidence, the corresponding task occurrence is recorded on the revision so the audit trail goes both ways.
User-facing
Internal
Notes
An uploaded file — PDF, screenshot, configuration export, signed approval, exported policy.
A link to an external system — a Jira ticket, a Confluence page, a Git commit, a monitoring dashboard, a signed agreement.
A free-form description, when the proof is the assertion itself (rare but allowed).
Evidence attaches to two places:
Applied controls — proof that the control is in place and working.
Requirement assessments — proof that a specific compliance requirement is met.
Because the same applied control can satisfy many requirements across many frameworks, a single piece of evidence often substantiates compliance against several requirements at once — without duplication.
Evidence isn't attached once and forgotten. Each piece carries metadata — description, timestamp, expiry, assignee — and a status. Auditors regularly refresh evidence: a yearly penetration-test report needs to be re-uploaded each year; an exported configuration needs to be re-pulled after each significant change.
Each piece of evidence keeps a versioned history through evidence revisions. Replacing an attachment doesn't overwrite the previous file — it creates a new revision under the same evidence object. Every revision carries:
A monotonically increasing version number.
The attachment (file) or link for this revision.
An SHA-256 hash of the attachment, computed automatically and used for integrity checks.
The current attachment shown on the evidence page is always the latest revision; older revisions remain accessible for audit. This matters when an assessor asks "what proof did you have on date X?" — the historical revision is still there.
An observation field for notes about what changed.
An optional link to the task occurrence that produced it — when an evidence file is generated by completing a recurring task, the task occurrence is recorded on the revision so the audit trail goes both ways.
A technical posture measures a fleet of assets against a technical baseline — a CIS Benchmark, a hardening guide, or any check-shaped framework — and keeps the measurements coming. Where an audit captures a considered, point-in-time evaluation, a technical posture is a living scoreboard: scanners, scripts, and operators keep pushing results, and the platform always shows the latest state of every check on every asset.
Technical postures are gated by the posture_assessments feature flag. Default off. See .
Mental model
A technical posture always lives inside a domain and is measured against one framework, whose assessable requirements act as the check list. It monitors a set of assets, and every ingestion of results — a manual entry session, an API push, a file import — is recorded as a run carrying one check result per (check, asset) it touched. An optional follow-up findings assessment is where remediation of failing checks is tracked.
User-facing
Internal
Notes
Think of a technical posture as a three-dimensional cube of measurements:
Checks — the assessable requirements of the chosen framework. If the framework defines implementation groups (for example Automated vs Manual recommendations in a CIS Benchmark), the Selected implementation groups field narrows the check list to the slice you committed to.
Assets — the fleet under measurement. Assets can be enrolled explicitly, or automatically the first time results arrive for them.
History — for each (check, asset) cell, the platform keeps the last N results, where N is the
The cube is sparse on purpose: nothing is created upfront. A cell only exists once something has measured it, and the Measured checks read-out tells you how much of the cube is filled. The current posture — what the overview, the score, and the action plan are computed from — is simply the latest result in each cell.
Each result carries one of five values: Pass, Fail, Not applicable, Error, or Not checked, along with the observed and expected values and a message when the source provides them. The Pass rate score is computed over Pass and Fail only — errors and unchecked cells don't dilute it.
Technical postures deliberately do less than :
Audit
Technical posture
The two share the same framework catalogue, so a benchmark imported once can back both a formal audit and a running posture. Use an audit when a human needs to weigh evidence and sign off; use a technical posture when a tool can answer the question and you want it answered often.
Every way of getting results in produces a run:
Manual runs — the "New manual run" flow on the Runs tab, or per-asset editing in the tree view, for checks a human performs.
API — scanners push JSON to the assessment's upload-results endpoint; re-using the returned run identifier accumulates or patches the same run. The API tab shows a ready-to-copy example.
File import — the "Import results" button on the Runs tab accepts CSV and XLSX files as well as OCSF Compliance Finding JSON. A mapping dialog lets you bind arbitrary scanner CSV columns and result values, choose how colliding rows aggregate, and target one or several of the scoped assets.
Runs are the ingestion log of the assessment: the Runs tab lists them with their tool, check counts, and pass/fail breakdown, and a run's detail page shows exactly what it recorded per asset. The trend chart on the Overview tab plots the pass rate across runs, which is how posture drift becomes visible.
The Action plan tab lists every check whose current result is anything other than Pass — failures first, then errors, then unchecked and not-applicable cells — so the operational team can decide what to investigate. From there, each line can be turned into a finding with one click.
Findings do not live on the technical posture itself. They are created in the follow-up assessment, a regular findings assessment linked to the posture (the creation form offers to create one automatically via Create a related findings assessment). This separation is deliberate:
Measurements are ephemeral; remediation is not. A check result is overwritten by the next run and eventually pruned by the history depth. A finding needs a stable life of its own — status, owner, ETA, severity, linked applied controls — that survives every re-measurement. Pinning remediation to a measurement row would lose it on the next scan.
Remediation is a shared discipline. Findings assessments are the platform's common remediation ledger — the same object used for pentest reports and internal reviews. Routing posture failures into one means they benefit from the full findings workflow (action plans, applied controls, reporting) instead of a parallel, posture-only mechanism.
The posture stays a pure measurement surface.
The link is kept visible in both directions: the action plan shows the finding attached to each failing check with its status and ETA, and re-measuring a remediated check back to Pass is how the fix is verified — on the next run, not by declaration.
Assessable requirement from the framework library
Follow-up assessment
FindingsAssessment
Findings assessment with the "Posture follow-up" category
Domain
Folder
Required; drives IAM scoping
History depth
of the assessment (10 by default). Older results are pruned as new ones arrive.
Result
Compliance result, score, maturity — set by a person
Pass/fail vocabulary — set by whatever measured it
Substantiation
Evidences and applied controls per requirement
Observed vs expected values from the measurement
Cadence
Campaign or review cycle
Continuous — every scan is a new run
The cube tells you
what is true right now
; the follow-up assessment tells you
what you are doing about it
. Either can be read — or rebuilt — without touching the other.
Technical posture
PostureAssessment
One framework × one domain × a set of assets
Run
PostureRun
One ingestion event; keeps the tool name and start time
Check result
PostureResult
One measurement of one check on one asset
Check
Question answered
"Are we compliant, and can we prove it?"
"What is passing right now, and what changed?"
Rows
One requirement assessment per requirement, created upfront
The project-management module brings PMBOK-style planning into CISO Assistant: a structured way to organise complex, multi-stakeholder initiatives — go-lives, accreditations, transformation programmes — alongside the compliance and risk work they drive.
It's the newest concept in the platform, and the object graph will continue to evolve.
Mental model
A project sits in a domain and can stack as a portfolio → programme → project hierarchy (the same model with three values of kind). On creation it's auto-paired with a generic collection — the flexible bag where the audits, risk studies, findings, evidences, and policies tied to the project accumulate. A responsibility matrix can be attached to one or more projects, encoding RACI / RASCI / RAPID assignments of actors to activities (each activity in turn references the work objects it covers). An accreditation is the formal authorisation event: it links its decision evidence and its compliance-assessment checklist back to the project's collection.
User-facing
Internal
Notes
Project objects don't replace — they sit alongside. Use a perimeter to define the scope of assessment; use a project to plan the work needed to bring that scope into compliance or through an accreditation.
A single project typically references many perimeters, audits, applied controls, and findings assessments — it's the cross-cutting view the security organisation works against day-to-day.
Every project carries a Budget and an Actual cost field (with a currency that defaults to the instance setting), so leadership has a place to capture the financial envelope an initiative is operating under and what's been consumed against it.
This is deliberately the high-level view only. The project's budget number is not computed from anywhere — it's the planned envelope a sponsor signed off on, and the actual cost is whatever spend you record against it. Both are decimal money values, not roll-ups.
The detailed financial picture — line items, build vs run split, amortisation, who's spending — lives where the work actually happens: on the referenced by the audits, risk studies, findings assessments, and other objects sitting in the project's . Each applied control carries its own structured cost; the of every assessment in the collection rolls those costs up into a budget overview.
The link between the two layers is indirect: applied controls aren't attached to a project directly. They're attached to the work objects inside the project's collection (a control satisfies an audit's requirement, mitigates a risk scenario, remediates a finding), and through that chain they become part of the project's financial picture.
When the project budget and the rolled-up applied-control costs diverge, that gap is itself a signal — you've either committed more spend than the project envelope foresaw, or your action-plan controls are missing cost data. The platform doesn't flag the gap automatically; the numbers are surfaced on both sides for the project manager to reconcile.
— the detailed build / run cost model that drives the bottom-up view.
— where the rolled-up cost of an assessment's controls is shown.
Row of a matrix; many M2Ms to work objects
Assignment
ResponsibilityAssignment
RACI cell — (activity, actor, role) unique
Accreditation
Accreditation
Linked to a Collection; checklist FK to ComplianceAssessment
Third-party risk management (TPRM) is the discipline of evaluating the security and compliance posture of the vendors, suppliers, and service providers your organisation depends on.
CISO Assistant treats third parties as a first-class concern with their own object graph, separate from internal compliance work.
Mental model
Four interlocking objects model the third-party landscape:
Entity — a vendor, supplier, or service provider. The unit of organisational identity.
Solution — a specific product or service provided by an entity. An entity can have many solutions.
Contract — the formal agreement covering one or more solutions, with dates, renewal terms, and obligations.
Representative — the person on the entity side who answers questionnaires and signs off on assessments.
The actual review of a third party is an entity assessment. It can:
Use a questionnaire — custom, or imported from a library (CAIQ, SIG, …).
Trigger an internal audit that lives in the entity's domain, so the third party can fill it in directly.
Capture the residual risk you accept by working with this entity.
CISO Assistant supports two modes for getting the actual answers back from the vendor, depending on whether the third party is allowed to access your instance.
This is the default workflow. You create a representative on the entity side, the platform issues credentials, and the representative logs into CISO Assistant directly to fill in the questionnaire. Their access is scoped: they only see the entity assessment(s) attached to their entity, and they land in a dedicated third-party / auditee surface — a separate route group with its own auditee dashboard, not the rest of your workspace.
This surface is gated by the auditee_mode , which has to be on for the third-party login flow to be available.
Use this mode when:
Your instance is reachable from the internet (or from the vendor's network).
You want live progress visibility — answers land in the database as they're typed, so the dashboard updates in real time.
You want comments, validation flows, and the full audit trail to apply to the vendor's answers automatically.
Don't confuse this with the feature — that one is for internal users splitting one audit across teammates using the respondent role within your own organisation. The third-party flow described here is for external vendors answering through the auditee surface.
Many organisations don't want to expose their CISO Assistant instance to third parties — for security, network, or contractual reasons. In that case, the platform supports a fully file-based round trip:
Export the questionnaire from the framework page. On the framework backing the entity assessment, use the Export to Excel action. You get an .xlsx file containing every requirement of the framework, structured so the vendor can fill in the answers, observations, and supporting evidence references in dedicated columns.
Send the file to the vendor. Email, secure file share, sneakernet — whatever channel your procurement and security policies allow. The vendor opens the spreadsheet in any tool that can edit Excel.
Use this mode when:
Your instance can't be (or shouldn't be) exposed to the vendor's environment.
The vendor refuses to log into a third-party platform.
You want a tangible artefact — the signed-off Excel — as part of the audit record.
Both modes produce the same internal state at the end: a populated entity assessment with per-requirement answers and observations. You can mix them within an organisation (online for one vendor, offline for another) or switch a single vendor from one mode to the other mid-cycle if the situation changes.
Treating third parties as a parallel surface — rather than just "another perimeter" — matters because:
Permissions differ: third-party representatives need restricted access, not full role-assignment privileges in your domain.
Lifecycle differs: contracts have renewal dates, vendors come and go, but your internal frameworks stay stable.
Reporting differs: TPRM dashboards aggregate across many entities, not down inside one.
Receive the filled-in file back.
The vendor returns the spreadsheet with their answers populated. No platform access was needed at any point.
Re-import via the data import wizard. Use the to upload the filled-in spreadsheet. The wizard maps the rows back to the requirement assessments on the existing entity assessment, populating answers, observations, and any other captured columns.
Organisation-defined labels that override the platform's defaults
CISO Assistant ships with a default vocabulary for the values that appear in dropdowns and badges across the UI — risk origin types in EBIOS RM, project status values, qualifications on incidents, accreditation status labels, metric units, and so on. The terminology layer lets each organisation override those defaults to match its own internal vocabulary, without changing the platform's data model.
Why it exists
Two organisations can use CISO Assistant against the same framework and the same methodology, but speak entirely different internal languages. One team's "Risk Origin: state-actor" is another team's "Adversary: nation-state". The terminology surface lets you reshape the labels without forking the platform.
How it works
A terminology entry binds a label to a field path — the specific UI surface where the label appears. The shipped field paths today cover:
ro_to.risk_origin — Risk Origin types on RO/TO couples in EBIOS RM.
qualifications — qualification tags on incidents, risk scenarios, and BIA escalation thresholds. The word qualification itself can be swapped for risk category across the interface via .
accreditation.status and accreditation.category — labels on accreditation records.
metric_definition.unit — units shown alongside metric values.
project.status and project.health — workflow labels on projects.
processing.nature — the nature of a processing activity (collection, storage, disclosure, erasure, …) in the privacy register.
personal_data.category — categories of personal data (name, email, health data, …) in the privacy register.
For each field path, the platform ships a built-in set of entries (state, organised crime, terrorist, activist, … for risk origins, for example). You can:
Hide built-in entries you don't want to surface — the is_visible flag controls dropdown inclusion.
Add organisation-specific entries alongside the built-ins.
Translate entries through the standard library translation mechanism.
The platform falls back to the built-in default whenever an entry is missing or hidden — terminology is additive on top of a working set, not a replacement.
Terminology entries live in the root folder by default, meaning they apply organisation-wide. Built-in entries cannot be deleted; they can only be hidden.
In the sidebar under Extra → Terminologies. The feature is gated by the terminologies feature flag, which is on by default.
— a different kind of organisation-defined override
Organisation-defined sensitivity schemes — like TLP — with ordered, coloured levels
An object classification is a scheme that marks how sensitive an object is — think TLP (Traffic Light Protocol), or a custom scale aligned to a customer's or an authority's own tiers. Each scheme is an ordered set of levels, and any object that supports classification can be tagged with one of them.
CISO Assistant ships with TLP built in, and lets you tune it or define your own schemes alongside it.
Why it exists
Organisations exchange documents with customers, partners, and authorities that each impose their own sensitivity tiers. One party works in TLP; another in a national CONFIDENTIAL / SECRET scale; a customer in its own bespoke levels. Object classifications let you define those schemes once and apply them consistently, with colour coding people already recognise.
Mental model
A scheme comprises an ordered set of levels. Each level carries a rank (its position in the order, from least to most sensitive), a colour, an abbreviation, and a translatable name and description. A level can then classify an object — today, a document — which surfaces it as a coloured badge and, on published PDFs, a page marking. The dashed edge is deliberate: classifying an object is always optional.
A scheme is a thin container with a name and an ID (TLP, for example). Its meaning lives in its levels, which are ordered by rank:
Rank sets the order and carries semantic weight — a higher rank is more sensitive. TLP runs CLEAR < GREEN < AMBER < AMBER+STRICT < RED.
Colour is the swatch shown wherever the level appears; the platform picks readable text automatically over it.
Abbreviation is the short token on the badge (AMBER
The built-in TLP scheme follows the FIRST TLP 2.0 standard — five levels with their official colours. Its structure is protected: you can hide levels you don't use, but you can't delete or reorder the canonical set.
Because levels are ranked rather than just listed, the order is data, not decoration — "at least AMBER" is a well-defined comparison. Today a classification is a visual marking; the ordinal rank is the foundation for comparing and, in future, gating access by level.
Open a scheme to reach its level editor, where you can:
Add a custom level with Add level — set its abbreviation, name, and colour; it slots in at the end of the order.
Reorder levels with the up/down controls (rank follows the order).
Hide a level with its visibility toggle — it stops being offered without being destroyed.
Built-in levels (and the built-in TLP scheme itself) can only be hidden, never deleted or restructured — so the canonical TLP always stays intact, and a hidden level stays hidden across restarts.
When you create or edit a document, the Classification field lets you pick a level. Once set, the level shows as a coloured badge on the document catalog, in the reader, and in the documents table, and it is stamped on every page of the document's exported PDF (for example TLP:AMBER, in the level's colour). Deleting a level a document points at simply clears the field — the document is never removed.
Documents are the first object type to carry a classification; the same mechanism extends to other objects over time.
Schemes and their levels live in the global (root) folder — they apply organisation-wide. Multiple schemes can coexist, so TLP and a customer-specific scheme are both available at once.
In the sidebar under Extra → Object classifications. The list shows every scheme; open one to manage its levels. Access is governed by the standard permissions — viewing is available to every role, while creating and editing schemes and levels is reserved for administrators.
— the first object type that carries a classification
— a related organisation-defined override layer
,
RED
).
Name and description are translatable, so a level reads in the user's language.
Edit or delete custom levels.
How it works
Ordering carries meaning
Classification is currently cosmetic — a badge and a document marking. The rank is designed so that a later access-control layer can treat a clearance as granting every lower level, without reworking the data.
CISO Assistant is delivered as a set of containers — frontend, backend, database, and reverse proxy — that can run locally via Docker, on a VPS, or on Kubernetes via the Helm chart. The diagram below sketches how the pieces fit together.
This section is being expanded.
Material to come: prerequisites, local Docker setup, virtualisation and remote deployment, deploying on a VPS, the Helm chart, S3 storage, mailer, custom certificates, secrets management, and instance upgrades.
System architecture
Quick start
Docker Compose or Helm for Kubernetes
Config Builder
Customise the local deployment according to your needs:
Make sure to have Docker 27 or above. If you get an error saying the docker compose command is not recognised, your Docker version is too old.
Docker Compose
Make sure Docker and Docker Compose are installed on your system.
clone the repo: git clone https://github.com/intuitem/ciso-assistant-community.git
run the preparation script and follow the instructions: ./docker-compose.sh
You can also find other variants for different setups as a starting point for your specific needs:
Make sure the Helm binary is installed and switch to your cluster context. The chart is published to a GitHub OCI registry — no helm repo add needed.
Pull the default values:
Edit custom.yaml for your environment. At minimum, look at:
global.domain — the hostname your instance will serve on.
See for the full procedure (image tag pinning, values reference, and operational notes).
Privacy register
A privacy register is the catalogue of personal-data processing activities the organisation carries out — required by GDPR and equivalent regulations as the foundation of accountable data handling.
CISO Assistant models the register as a graph of typed objects so it stays queryable, auditable, and reusable across audits.
Processing — an activity that operates on personal data (collect, store, transfer, delete). The central object.
Purpose — the lawful reason a processing exists. Each processing has at least one purpose.
global.tls and ingress.tls.* — enable TLS.
backend.config.djangoSecretKey — rotate from the default.
backend.config.databaseType (sqlite, pgsql, or externalPgsql) and the matching postgresql.* / externalPgsql.* block.
Create a namespace: kubectl create ns ciso-assistant
helm show values oci://ghcr.io/intuitem/helm-charts/ce/ciso-assistant > custom.yaml
Helm chart
This setup assumes Caddy will handle TLS on your behalf. If you experience SSL-related issues, you may need to patch your ingress-nginx-controller to enable the enable-ssl-passthrough flag.
Personal data — what is being processed (name, email, location, health record, biometric, …), with categories that map to GDPR sensitivity classes.
Data subject — the kind of individual the data refers to (employee, customer, prospect, …).
Data recipient — the internal teams or external parties that receive the data.
Data contractor — third parties that process data on your behalf — a TPRM entity surfaced here with a privacy-specific lens.
Data transfer — flows of data to entities outside the original jurisdiction.
Privacy operations also need to record events as they happen:
Right requests — data-subject requests under GDPR (access, rectification, erasure, portability) and the organisation's response.
Data breaches — incidents affecting personal data, with the notification clock and authority correspondence.
A privacy register lives in a domain with its own RBAC scoping — typically a DPO-led folder.
Findings from GDPR audits surface in the register as actions on processings.
Data contractors cross-reference TPRM entities, so a sub-processor's security review feeds both worlds.
A processing's nature and its personal-data categories draw from the layer, so you can align them to your own internal vocabulary.
A processing can be exported as a multi-sheet Excel workbook from its detail page and re-imported — through the data import wizard or the CLI — to recreate it with all its sub-objects. See .
Terminology — customise processing nature and personal-data category labels
Mental model
Personal data entries can be created in batch from a processing's detail page: the Batch create personal data button (layer-group icon on the Personal Data table) opens a picker of categories grouped by theme — drawn from the layer — and applies the same Retention, Deletion Policy, and Is sensitive values to every entry it creates. Categories the processing already records are skipped.
Prerequisites to Install CISO Assistant On-Premises
Hardware Requirements:
CPU: 4 cores
RAM: Minimum 16 GB
Storage: Minimum 10 GB (consider more for evidences)
You can start with lower specs of course for testing.
Software Requirements:
Ubuntu/Debian, CentOS, RHEL: LTS versions recommended when applicable*
Docker 27 or up, with Docker compose, or Kubernetes Cluster 1.31 or up
*most Linux distributions supporting Docker should be compatible but have not been tested. Some distributions are not using the official repositories so make sure to follow the instructions from docker page.
Postgres 16 or up if you are choosing this variant
Any SMTP compatible Mailer
Deployment methods
Docker rootless configuration
Deployment documentation for rootless containers
Since V3.16, CISO Assistant now runs as non-root user 1001:1001 by default across all compose templates.
If you already have a local instance of CISO Assistant, please refer to the migration guide
Fresh install
If you are new from Ciso-Assistant, you will automatically be deployed with a rootless Docker configuration by running pre-built images or local image build
Using pre-built images
./docker-compose.sh
Using image local build
./docker-compose-build.sh
Migration guide
You already have a self-hosted Ciso Assistant (Community version or On-Premise Pro version) and you want to know all the options you have:
I want a rootless Docker and I am currently running CISO Assistant with pre-built images
Then update your docker-compose.yml on the version you want (ex: v3.16) or keep latest tag and then ignore the manual update in your file.
Your new containers should be root-less!
If you have any doubt about updating your custom .yml, do not hesitate to contact us on our Discord or Support portal.
I want a rootless Docker and I am currently running CISO Assistant with local images
Keep root Docker
In the case you do not want to have rootless Docker
Keep the root Docker as it was before
Then manually update your docker-compose.yml on the images version you want (ex: v3.16). Ignore this step if you use latest image
That is it! Since you did not update the github repository, the docker-compose.yml keeps the initial form without root less Docker configuration
Simple rootless check
If you want to verify if you run a rootless Docker container, do:
docker exec -it backend id
It should say :
uid=1001 gid=1001 groups=1001
You are using docker-compose.yml to deploy Ciso Assistant
git pull
docker compose down
sudo chown -R 1001:1001 ./db
docker compose up -d
You are using a custom .yml to deploy Ciso Assistant
git pull
docker compose down
sudo chown -R 1001:1001 ./db
docker compose -f docker-compose-build.yml up -d
In the case you did update the github repository by doing a git pull command, we suggest you to take an older version of the file (like the v3.15.5 docker-compose.yml version)
docker compose down
image: ghcr.io/intuitem/ciso-assistant-community/frontend:v3.15.2
# replace with (for example)
image: ghcr.io/intuitem/ciso-assistant-community/frontend:v3.16
docker compose up -d
Custom certificates
How to add custom certificates for your remote installation
You can configure your own Certificate by replacing the line tls internal in the docker-compose.yml by tls <cert_file> <key_file>. Here is Caddy documentation on this https://caddyserver.com/docs/caddyfile/directives/tls
Before doing this, there is just one step, you need to add the cert_file and the key_file inside caddy container.
You have basically two ways to do it:
Adding the two files inside caddy_data directory, as it is already mounted by default in the volumes, and specify the path to the files:
If you don’t have this volume or you want to add another, create a repository at the same level of your docker compose file for example /certs, add the files inside and moun it:
Local
Basic setup for local deployment and experimentation
The recommended pattern for local deployment is to use Docker Compose. Check the Readme file on the CISO Assistant repo for the latest instructions.
The compose file will manage three containers and set the required variables:
Front
Back
Caddy (proxy)
Make sure to have a recent version of Docker installed
On a Linux distro with a server flavor, make sure to remove older versions and install the latest one using the proper Docker repos to avoid twisted setups. Check out the instructions at
On Windows, Docker Desktop+WSL is recommended
On MacOS, Docker Desktop covers the requirements
Run:
It will clean up previous images and get the latest stable release.
Once the images are downloaded and migration triggered, you should see a prompt asking you to set the first superuser. Follow the instructions to set it, and you should be ready.
In case you are running on an unsupported architecture, you can open a GitHub issue so that we add its support or use the next steps to build the images locally.
Alternatively, if the previous configuration didn't succeed, run:
Given that Caddy is using a self-signed certificate, your browser will mention a warning that you can accept and continue.
Virtual Private Server - Remote internet-facing VM
This setup aims to expose CISO Assistant on a VPS while using automated Let's Encrypt for certificates management.
provision your VPS and make sure it has a public reachable IP - make sure to have the Prerequisites mentioned on that page.
Setup your DNS zone to point to the IP of your VPS (A record). Give it sometime to propagate (depends on the registrar). It's better to start with this once you get the IP to give it enough time for propagation.
on the following I'm using ubuntu 24.04. So adjust the packages installation according to your OS
ssh to your server and perform the following commands:
Follow the instructions and make sure to do the following:
select VM/Remote
Internet facing and ACME ready - yes
Provide the FQDN you've set on your registrar
Port to use: 443
It should look like something like this:
Keep track of the URL mentioned at the end of the config generator. You can review the generated yml file and adapt it if needed.
Wait for the app to initialize and you will get a prompt to enter the first admin user and the password.
You can go back and update the docker-compose.yml according to your needs or restart the interactive guide to create a new one.
You can choose Traefik or BunkerWeb instead of Caddy using the config builder. Please note that BunkerWeb deployment is still experimental at the moment.
The generated file in the config directory will be named docker-compose-custom.yml For subsequent operations with compose, you'll need to specify it with -f
If you're running docker compose without the -f, it could conflict with the default one on the repository root directory.
If you're starting a production environment:
make sure to disable the debug mode,
have your docker-compose-custom renamed and stored out of the repo,
have your db folder outside of the repo.
#update ubuntu repository and OS
sudo apt update
sudo apt upgrade
# install docker
sudo snap install docker
#install python
sudo apt install python3-pip python3.14-venv
#clone the repo
git clone https://github.com/intuitem/ciso-assistant-community.git
#go to the config generator
cd ciso-assistant-community
cd config
# setting up the python project and dependencies
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# run the interactive config generator
python make_config.py
# switch to sudo. This can be avoided depending on your docker setup
sudo su
./docker-compose.sh
cd config
# stop and remove containers
docker compose -f docker-compose-custom.yml rm -fs
# delete the db and proxy config
git clean -fdx .
Notes
Clean up
Post-install setup
Setting up S3
How to connect your S3 block storage for your installation
By default, CISO Assistant stores attachments on the local filesystem.
You can configure it to use an S3-compatible object storage (AWS S3, MinIO, etc.).
Prerequisitories
A running S3-compatible storage
An existing bucket (must be created before starting CISO Assistant)
Use override files to separate dev and production configurations without touching the base file:
Each environment can point to its own .env file:
This lets you commit safe dev defaults while keeping production secrets in a separate file.
2. Per-Environment Compose Overrides
docker-compose.yml # base (references ${VARIABLES}, no secrets)
docker-compose.override.yml # local dev defaults (loaded automatically)
docker-compose.prod.yml # production overrides (git-ignored)
# Dev — override is loaded automatically
docker compose up
# Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
docker compose --env-file .env.prod -f docker-compose.yml -f docker-compose.prod.yml up -d
Structured logging
CISO Assistant can emit its operational logs as line-delimited JSON so a SIEM (Microsoft Sentinel, Azure Data Explorer, Splunk, Elastic) can ingest them without custom parsing. This page explains how
Overview
The three runtime processes each write their operational logs to standard output, where your container runtime or log shipper collects them:
Backend (Django API)
Worker (the Huey background task process)
Frontend (the SvelteKit server-side process)
By default the output is a human-readable, colourised format meant for reading in a terminal. That format is convenient locally but awkward to parse in a log pipeline. Setting a single environment variable switches every stream to one JSON object per line (newline-delimited JSON), which SIEMs parse natively.
Set the following on the backend, worker, and frontend containers:
The backend and the Huey worker share the same logging configuration, so this one variable covers both. Setting it on the frontend container routes the SvelteKit server process — authentication events, request errors — through the same JSON shape.
The log level also applies to all three streams and defaults to INFO, which captures request-level events. Adjust it if you need more or less detail:
A backend request log line (formatted here for readability — on the wire it is a single line):
A frontend authentication event:
Field
Description
Sensitive OAuth2 query parameters (code, token, id_token, access_token) are redacted from logged request URLs.
In addition to standard output, the backend can mirror its logs to a file. Set LOG_OUTFILE to the destination path; the file is always written in JSON, regardless of LOG_FORMAT. Leave it empty (the default) to log to stdout only.
This is useful when a log shipper tails a file rather than the container's stdout.
Once LOG_FORMAT=json is set, point your collector at each container's stdout (or the file above). Because the lines are newline-delimited JSON, Microsoft Sentinel, Azure Data Explorer, Splunk and Elastic all parse them without a custom grok/regex parser — each JSON key becomes a queryable field. Use request_id to correlate all backend lines belonging to a single request, and logger to separate the frontend stream from the backend stream.
event
The log message
request_id
Correlation id shared by all backend log lines from one request
user_id
Authenticated user id, when the request is authenticated
ip
Client IP address
timestamp
ISO-8601 UTC timestamp
level
debug, info, warning, error, critical
logger
Source logger (frontend for the SvelteKit process; a Python module path on the backend)
This page is about the operational log streams (requests, errors, background-task activity, authentication events). It is distinct from the in-app Audit log, which records who changed which object and is read inside the application.
Enabling JSON logs
Set LOG_FORMAT=json on all three containers. If only some are switched, your pipeline receives a mix of JSON and plain-text lines and parsing breaks for the plain ones.
The chart README in the repository carries the full values table (charts/ciso-assistant-next/README.md) — refer to it for every key with its default and description.
Pin the appVersion to a published release if you want predictable upgrades. Set global.image.tag in custom.yaml to the version you tested against (e.g. v3.18.2). Leaving it empty pins to the chart's appVersion, which moves with the chart.
Create a namespace and install:
The backend pod runs migrations on startup; allow it ~30s before checking the frontend.
The in-product AI assistant needs two things wired in the chart: the ENABLE_CHAT flag on the backend and a reachable Qdrant vector database for retrieval-augmented generation. Setting qdrant.enabled: true deploys the official Qdrant chart as a dependency (a StatefulSet with persistent storage and health probes) and injects QDRANT_URL automatically.
Notes:
Any key under qdrant: is passed through to the Qdrant subchart (e.g. qdrant.persistence.size, qdrant.resources). Persistence is on by default; see the subchart values for all options.
The Qdrant subchart is bundled inside the published chart, so installing needs no access to the Qdrant Helm repo. Air-gapped clusters still pull the Qdrant image at runtime: mirror it and override qdrant.image.repository (same as for the backend/frontend images).
To point at an external Qdrant instead of the bundled one, leave qdrant.enabled: false and set QDRANT_URL through backend.env and backend.huey.env.
The LLM provider (Ollama or any OpenAI-compatible endpoint, model, base URL) is configured from the in-app Settings → Chat/AI section, not from the chart. LLM inference is heavy; point it at a GPU-backed endpoint.
The Qdrant collection and the indexes are not created automatically. After the pods are up, run the indexing commands once from the backend pod (init_qdrant creates the collection, index_objects indexes your existing risk/control/asset records, index_libraries indexes the framework libraries):
If your pods need to trust an internal CA (for SMTP, SSO, or any outbound TLS service signed by a private authority), provide just your CA in a secret. Trust is added on top of the default roots, not replaced, so public CAs keep working:
Backend and Huey (Python): an init container concatenates the system CA bundle with your CA into a shared bundle, and SSL_CERT_FILE / REQUESTS_CA_BUNDLE point at it.
Frontend (Node): NODE_EXTRA_CA_CERTS points at your CA, which Node adds to its built-in roots.
The backend init container writes the merged bundle to an emptyDir. If you also harden the pod to run as a non-root user (global.securityContext.runAsNonRoot / per-component runAsUser), set an fsGroup so that user can write the volume:
To restrict pod traffic, enable the bundled NetworkPolicy and pass your own ingress/egress rules. The policy selects all pods of the release by default, so your rules must also allow the internal flows the app relies on: the frontend BFF calls the backend (PUBLIC_BACKEND_API_URL) server-side, and the backend reaches Qdrant. Forgetting these blocks the app even though the ingress controller can reach it.
Replace ciso-assistant-release below with your Helm release name.
Chart upgrades are normally additive: new capabilities ship as new values keys, and existing keys are not removed or renamed within a major chart version, so your current custom.yaml keeps working as-is. New features are opt-in and off by default — if you don't set their keys, nothing changes in that area.
When a breaking change to the values shape is unavoidable, it is released under a new major chart version and the required migration steps are documented in the release notes. Read them before upgrading across a major version.
The main thing to manage on upgrade is the application version. If you did not pin global.image.tag, the images move to the chart's appVersion on the next helm upgrade. Pin the tag to the version you tested and review the release notes for the range you're crossing. Database migrations run automatically on backend startup, so no manual schema step is needed.
PostgreSQL persistent volumes are not deleted automatically when using the bundled subchart. Inspect with kubectl get pvc -n ciso-assistant and remove manually if you want a clean slate.
Note: The old Helm repository (intuitem.github.io/ca-helm-chart) is no longer maintained. The values shape changed substantially between the legacy charts/ciso-assistant/ chart and the current charts/ciso-assistant-next/ chart (frontendOrigin → global.domain, no clientName/clusterDomain shorthand, separate ingress block). New installs should use the OCI registry above.
kubectl get pods -n ciso-assistant
kubectl get ingress -n ciso-assistant
backend:
config:
chat:
enabled: true # sets ENABLE_CHAT on the backend and Huey worker
qdrant:
enabled: true # deploys the Qdrant subchart and injects QDRANT_URL
This means that you will be using a browser from within the VM so localhost settings are applicable. You can simply use the default ./docker-compose.sh at the root of the repository or trigger the config builder with the following settings:
run ./docker-compose.sh and connect from within the VM using https://localhost:8443
setup a FQDN for your VM and make sure it's known by the host you are connecting from. This will vary depending on your OS. For instance, for linux/mac, you can add a line to your /etc/hosts file such as:
192.168.1.87 ca.homelab.local
in this example, the first part is your VM's ip and the second one will be the FQDN you'll be providing to the config builder and that you will use to connect later on.
Run the config builder and provide the following settings:
run ./docker-compose.sh and connect from your host this time using https://ca.homelab.local:8443
Notes:
If you don't want to have a specific port, use the port 443 during the settings, given it's not used by another application on your system.
In the remote setup, if you also want to connect from within the VM, you can add your custom FQDN to the /etc/hosts of your VM but mapped to 127.0.0.1
---
Let's say that you want to setup or experiment with CISO Assistant on a Network or Virtualized environment (eg. Hypervisor) on a remote host, for instance, to use with multiple users:
Install a recent version of Docker on your remote server
Given that we are using TLS with Caddy, we need to have DNS entries and not IPs
The workstations need to be able to reach the remote using an FQDN (DNS entry). If not you can add an entry on your /etc/hosts. Keep track of the remote server DNS as you'll put it on the next step, let's say the remote is cool-vm for instance
Five lines need to be edited. Save the file and move to the next step
If you're getting SSL_ERROR_INTERNAL ERROR_ALERT (Can be different on other browsers) blocking you from continuing, make sure that you've made the 5 changes above.
Thetls internal(equivalent to-iin CLI mode) parameter of Caddy can present some security issues and is not recommended for production and internet exposure. You should consider proper certificates for that.
You're all set, and you can simply run:
Your CISO Assistant can be reached now from https://cool-vm:8443, and you can skip the SSL warning for the self-signed certificate.
Clone the repo, but don't run anything yet. Edit the docker-compose.yml file as follows:
(red is for deletion and green for addition); your diff should look like:
./docker-compose.sh
From the VM
From your network / host OS
Legacy - Kept for reference purposes
Prometheus metrics
CISO Assistant exposes a /metrics endpoint in the Prometheus exposition format. This page explains how to enable it and how to access it safely from Prometheus without exposing it publicly.
Overview
When enabled, the backend exposes a /metrics endpoint that returns instance-level gauges in the Prometheus exposition format:
User and editor counts
Number of loaded libraries, domains, perimeters, assets, threats
Number of compliance assessments, risk assessments, risk scenarios, risk acceptances
Number of applied controls and evidences
License expiration date, instance creation date, last login date
Build information (version, commit, schema)
Set the following environment variable on the backend container:
The endpoint is disabled by default (EXPOSE_METRICS=False).
If your prometheus runs in local you can for example check the /metrics endpoint with this config
If you have multiple instances on different machines, add them to targets:
Start Prometheus with:
The Prometheus UI is available at http://localhost:9090. Search for ciso_assistant_nb_users to confirm that scraping is working.
From a machine that has access to the backend (not from the public internet):
You should see output similar to:
If the endpoint returns a 404, check that EXPOSE_METRICS=True is set and that the backend has restarted.
Enabling the endpoint
Security — never expose /metrics publicly
The /metrics endpoint is unauthenticated. If you enable it, make sure it is never reachable from the public internet — either by restricting access to trusted IP ranges, or by binding the endpoint to an internal interface only.
# HELP ciso_assistant_nb_users Number of users in the CISO Assistant instance
# TYPE ciso_assistant_nb_users gauge
ciso_assistant_nb_users 3.0
...
Updating your local instance
How to update your local instance. All docker images are available on ghcr with the specific versions matching the repo tags. The latest tag points to the most recent release for both back and front.
Hands-free
The easiest way to update your on-prem/local instance (pro or community)
Run the script update-ciso-assistant:
./update-ciso-assistant.sh
Detailled steps
In case of issues (unsupported shell, windows, etc.) here are the steps to consider:
backup your db:
if you're using sqlite, copy the file under a different name
if it's postgresql you can use something like pg_dump
stop and clean the containers, this won't affect your data
docker compose rm -fs
restart the compose and let it handle the migration
docker compose up -d
Force remove the previous docker images to get the new ones
If you have selinux enabled on your distro, you might want to check if it's not preventing the mount volume of the docker compose; you can try something like this:
Recompute assessment results after the semantic compute_result upgrade
Audits produced before the semantic compute_result aggregation may have stored results that were collapsed under the older boolean logic (any non-empty compute_result was treated as truthy, so a "Non-compliant" choice could still aggregate to Compliant). This applies to audits backed by questionnaires from either path: the library builder or Excel-imported frameworks.
The recompute_assessment_results Django management command realigns stored results with the current rule.
Properties:
Scoped to compute_result-driven requirements only. The command targets requirement assessments whose requirement carries at least one question choice with a resolvable compute_result. Score-only or manual-entry audits are left alone, so a manually-set result is never silently reset to not_assessed.
Idempotent. Running twice is a no-op the second time; rows already aligned are reported as unchanged.
Per-batch commits by default. Lock duration stays bounded on large tenants. Use --atomic for all-or-nothing semantics on small datasets.
Touches score, result, is_scored, updated_at on the requirement assessments it rewrites. Other fields are left as-is.
Re-triggers CA-level hooks after writes: ComplianceAssessment.upsert_daily_metrics() and CEL outcome evaluation are explicitly called once per touched audit, because bulk_update bypasses the RequirementAssessment.save() signal chain. If any hook fails, the command exits with a CommandError listing the failures, so you don't silently end up with up-to-date results but stale metrics or CEL outcomes. Pass --skip-post-hooks to opt out when you handle those hooks separately.
If you run the application via Docker Compose, prefix with docker compose exec backend (or the equivalent for your deployment).
chcon -Rt svirt_sandbox_file_t ./db
# Preview impact, no writes
uv run python manage.py recompute_assessment_results --dry-run
# Apply to all audits, one transaction per batch (default)
uv run python manage.py recompute_assessment_results
# Scope to a single audit
uv run python manage.py recompute_assessment_results \
--compliance-assessment <uuid>
# Tune batch size or wrap the full run in one transaction
uv run python manage.py recompute_assessment_results --batch-size 1000 --atomic
# Skip the post-run CA hooks (metrics + CEL outcomes), e.g. when chaining
# with another job that will recompute them
uv run python manage.py recompute_assessment_results --skip-post-hooks
Frequent questions
Stop and restart
docker compose down
docker compose up -d
Getting the logs
All services logs combined:
docker compose logs
Specific service:
docker compose logs backend
Didn't get the prompt for the first user
If you didn't get the prompt to create the first user, or lost the password but you still have access to the infra level, you can trigger the createsuperuser command to fix that.
In your compose file folder, try:
docker compose exec backend uv run python manage.py createsuperuser
Alternatively, in a docker environment:
docker ps -a | grep backend (this will get you the id of the Backend for CISO Assistant container, keep it for the next step)
docker exec -it <the_container_id> uv run python manage.py createsuperuser
and you should get a prompt now 😉
docker compose exec backend uv run python manage.py changepassword <user_email>
You'll get a prompt to change the password
In some rare cases, the migration of database schemas can take longer than expected or fail silently. First thing to check is the backend container logs:
Make sure you share these information if you're reporting an issue on Discord or the Support portal.
If you want to trigger the migration to make sure that all increments have been properly applied:
most likely because the initialization took longer than expected. Make sure you provide the expected specs or tune the docker compose to give the app more time to finish the init phase.
The recommended pattern for a first local setup is to go with ./docker-compose.sh ;
In case you can't:
Run
wait for the init to finish and then trigger the first user creation manually:
By default, the BODY_SIZE_LIMIT environment variable is set to 50 MB in the frontend Dockerfile:
In order to upload larger files, this value must be increased. How to do so depends on you rmode of deployment. Here are relevant docs:
Lost the first user password
Random issues after upgrading
Healthcheck fails during the installation
Don't want / Can't run the init script
"Payload too large" when uploading a file to the frontend
If you use helm, this value is overwritten by the bodySizeLimit variable. Note the camel case here.
CISO Assistant uses SMTP to send transactional emails (password reset, superuser creation, notifications). This page covers configuration and the TLS specifics introduced in 3.16.
Environment variables
EMAIL_HOST=smtp.example.com
EMAIL_PORT=465
EMAIL_HOST_USER=noreply@example.com
EMAIL_HOST_PASSWORD=<secret>
DEFAULT_FROM_EMAIL=noreply@example.com
# Pick ONE of the two (not both):
EMAIL_USE_SSL=True # SMTPS (typically port 465)
EMAIL_USE_TLS=False
# or
EMAIL_USE_SSL=False
EMAIL_USE_TLS=True # STARTTLS (typically port 587)
For local development you can run MailHog and point EMAIL_HOST at it with both flags set to False.
TLS certificate requirements (3.16+)
Since 3.16, the backend image runs rootless and read-only, and ships with a recent Python/OpenSSL stack that enables strict X.509 verification (VERIFY_X509_STRICT + VERIFY_X509_PARTIAL_CHAIN).
This has two consequences:
The old recipe of running update-ca-certificates from command: in docker-compose.yamlno longer works — it requires root and write access to /etc/ssl/certs/, both denied by the new container. See the deprecated section below if you are still on an older image.
Every certificate in the chain presented by your SMTP server must satisfy the strict checks. A single non-compliant intermediate will fail the whole verification.
Your SMTP server certificate and every certificate in its chain must include:
BasicConstraints — CA:FALSE on the leaf, CA:TRUE on intermediates and root
KeyUsage — at minimum digitalSignature, keyEncipherment on the leaf
If you get an error like:
regenerate the offending certificate with the missing extension. Inspect a cert with:
You cannot modify the system trust store at runtime anymore. Instead, mount a PEM file containing the CA(s) to trust and point OpenSSL at it via SSL_CERT_FILE:
How it works:
Django's mailer goes through smtplib, which builds its TLS context with ssl.create_default_context().
That function asks OpenSSL to load its default trust store.
When SSL_CERT_FILE is set, OpenSSL uses
Requirements for the bundle file:
A single PEM file, concatenating your root CA and any intermediates needed to validate the SMTP server's chain.
Each cert in the bundle must itself satisfy the strict X.509 checks listed above (BasicConstraints, KeyUsage, AKI).
From the backend container:
A clean (250, b'2.0.0 OK') means TLS and trust are correctly configured. Any SSL: CERTIFICATE_VERIFY_FAILED will name the missing extension or the failing cert.
You can also trigger a password reset from the UI and watch the backend logs — failures are logged at iam.models with email_host, email_port, and the underlying SSL error.
Older versions ran the container as root with a writable filesystem, which allowed mounting a CA at runtime and refreshing the trust store from command::
When upgrading to 3.16+, remove the command: override and switch to the CA bundle method above.
Authority Key Identifier (AKI) — on every cert in the chain, not only the leaf
a complete fullchain (leaf + intermediates), served in order by the SMTP server
that file instead
of the system bundle (
/etc/ssl/certs/ca-certificates.crt
).
Trusting a private / self-signed CA
REQUESTS_CA_BUNDLE is only honored by the requests/urllib3 libraries (used by outbound HTTP calls such as OIDC/SAML metadata fetch). It has no effect on SMTP, so you don't need to set it for the mailer.
Verifying the setup
Deprecated: rescue mailer
The EMAIL_*_RESCUE variables (a secondary mailer used as fallback) are deprecated and will be removed in a future release. They are not a workaround for TLS issues — fix the certificate instead.
This section is kept for users still on backend images older than 3.16. Do not use it on 3.16+ — the recipe silently fails on the rootless/read-only container. Use the CA bundle method instead.
The Settings page in CISO Assistant is where instance-wide configuration lives — the dials and switches that affect everyone using the platform, regardless of which domain or perimeter they're in.
Settings are grouped into the following categories:
General settings — display preferences, default language, currency and daily rate, AI/LLM provider configuration, retention defaults, and a handful of behavioural toggles (self-validation, MFA enforcement, external-link warnings).
Feature flags — toggles that turn whole product areas on or off. Use these to tailor the UI to what your team actually needs and to keep advanced or experimental capabilities out of sight until you want them.
— the remediation deadlines (in days) that apply to vulnerabilities by severity, and the anchor date used to compute the clock.
— switches for the optional external feeds (KEV, EPSS, NVD enrichment) and the network timeout the platform uses when reaching them.
(PRO) — replace the default logo, favicon, and client name with your organisation's identity.
(PRO) — override the subject and body of system emails, and the .docx templates used for document exports.
Editing settings requires the change_globalsettings permission. In practice, this means a Domain Manager on the global domain or an equivalent custom role.
Settings on this page are global — they apply to the whole instance. Domain-level overrides aren't available at this layer; if you need per-domain behaviour, look at the relevant domain or perimeter's own settings.
Material to come: organisation setup (users, groups, custom roles, teams), the IAM model, single sign-on (SAML, OIDC) with per-provider guides (Entra ID, Okta, Google Workspace, Keycloak), bringing content in through and the , and changing the interface language.
You think it is time to change or try a new database on CISO Assistant? You were using SQLite and now want to switch to PostgreSQL, or the other way around? This guide is for you.
Primordial step: create backups
Do not forget that this kind of operation can be tricky and may impact your data if something goes wrong.
To stay safe, we strongly recommend creating a backup of the volume, disk, or db/ folder used to host your CISO Assistant instance.
We also recommend testing your backup procedure by deploying another CISO Assistant instance and restoring the backup into it. This will help verify that your backups work correctly before you start switching database engines.
Switch from SQLite to PostgreSQL
Here is a complete guide of all the steps you should perform (after backing up your volume).
The new PostgreSQL-backed instance must run the exact same CISO Assistant version as the source SQLite instance. The restore will refuse a backup produced by a different version. Migrate first, then upgrade afterwards if needed.
1. Export your data
To facilitate the backup and restore workflow, we designed a backup-restore page with a button to back up the database.
Go in Extra > Backup & restore, then click on Export database. You will get a .bak file.
You can install whichever PostgreSQL setup you prefer, as you only need the environment variables afterwards to connect it to CISO Assistant. Here are the two main ways:
Depending on your OS, install PostgreSQL — here on Ubuntu:
Configure your PostgreSQL instance with at minimum:
Also check the port on which PostgreSQL is running (normally 5432).
In your docker-compose.yml, add a service like this:
Define these environment variables in your docker-compose.yml (or via export if running without containers):
If PostgreSQL is running on your host (not in a container), set DB_HOST=host.docker.internal and add the following to your backend service:
Bring the stack back up — if you are using Docker, migrations run automatically at startup:
Since the PostgreSQL database is brand new and empty, you also need to create a temporary superuser:
Connect with your new temporary superuser, go to the Backup & restore section, and click the restore button. Select the .bak file you downloaded in step 1.
Your database will be fully restored. Reconnect with one of your original users.
The .bak only contains database rows. The actual evidence files (PDFs, images, …) live on the filesystem under db/attachments/, independent of which database engine you use. Database rows reference them by path, so if the files aren't where the rows expect them, evidence records will appear to exist but their attachments will be broken.
You have two ways to keep them:
Option A — Same host, same volume (typical case). If you're switching the database engine in place on the same machine and you kept the db/ volume mounted on the new stack, you don't need to do anything: db/attachments/ is already there and the restored records will resolve to the existing files.
Option B — Cross-host migration, or you want one atomic snapshot. Use the clica CLI shipped under instead of the UI export. It packages the database and every attachment in a single backup, then restores both in one atomic call:
Requires a Personal Access Token with the backup permission set in .clica.env. The CLI verifies SHA-256 hashes and is resumable. See cli/README.md for setup details.
The logic is the same in reverse:
Export the database from the Extra > Backup & restore page on your PostgreSQL-backed instance (or run clica backup-full if you want attachments included).
Stop the stack (docker compose down).
Remove the PostgreSQL environment variables (POSTGRES_NAME
The same version-match rule applies: the SQLite-backed instance must run the exact same CISO Assistant version as the PostgreSQL-backed source.
,
POSTGRES_USER
,
POSTGRES_PASSWORD
,
DB_HOST
,
DB_PORT
) and the
db
service from your
docker-compose.yml
. With
POSTGRES_NAME
unset, CISO Assistant falls back to SQLite at
db/ciso-assistant.sqlite3
.
Bring the stack back up, create a temporary superuser, and restore the .bak file (or run clica restore-full).
Keep db/attachments/ in place — the same caveat from step 7 above applies.
Evidence files and other uploaded files are not included in this export — they live on disk under db/attachments/ and are handled separately in step 7. If you'd rather get both the database and the attachments in a single bundle, skip this step and use the clica CLI instead — see step 7, Option B.
2. Stop the instance of CISO Assistant
3. Set up PostgreSQL
On your host using a service
In a Docker container
4. Connect CISO Assistant to PostgreSQL
The exact host settings depend on your setup — OS, Docker version, whether you run rootless Docker, or whether you bridge the backend container to the host network. Adjust DB_HOST and extra_hosts accordingly.
5. Restart, migrate and create a superuser
This is a temporary user. The backup restore will delete it while giving you back your original users.
docker exec -it backend uv run python manage.py createsuperuser
# On the source instance — produces backup.json.gz + attachments/ + manifest
uv run clica.py backup-full --dest-dir ./db_backup
# On the new (PostgreSQL-backed) instance, after step 5
uv run clica.py restore-full --src-dir ./db_backup
Feature flags
Feature flags turn whole product areas on or off. They're how you tailor the navigation and the surface area to what your team actually uses, and how you keep experimental or specialised features out of sight until you want them.
Flags affect what's visible in the sidebar, what appears in CRUD pages, and which permissions are even relevant. They do not delete any underlying data — turning a flag off hides the feature; turning it back on restores the UI as it was.
Operations
xrays — the X-rays inconsistency-detection page.
incidents — incident management.
tasks — the task-management module (one-off and recurring tasks).
control_plan — the consolidated control-plan view across applied controls.
risk_acceptances — the risk-acceptance workflow.
exceptions — security-exception tracking.
follow_up — findings assessments (follow-up).
ebiosrm — the EBIOS RM module.
scoring_assistant — the OWASP-based scoring assistant.
vulnerabilities — vulnerability tracking.
compliance — compliance assessments (audits). Effectively master switch for the entire compliance pillar.
auditee_mode — the read-only auditee surface for external assessors. Default off.
campaigns — bulk-orchestration of audits across many perimeters. PRO.
journeys — preset journeys for bootstrapping new organisations or domains.
comments — comments on objects.
object_audit_trail — per-object button on detail pages, gated by the Can view object audit trails permission. PRO.
experimental — feature-gate for the experimental area. Use with caution.
Defaults marked Default off are off in fresh installs. Everything else defaults to on. Restart isn't required when a flag is toggled, but a hard refresh in the browser is.
validation_flows — configurable approval workflows that mirror internal review or management-approval processes, attached to objects whose state changes warrant sign-off. Default off.
organisation_issues — context register: issues affecting the organisation.
policy_documents — the dedicated Policies surface (a filtered view of applied controls).
document_management — the standalone module: the reading catalogue, document list, and templates. Author or upload documents through a draft → published lifecycle, independent of policies.
audit_tree_inheritance — combine an audit's results with parent-domain audits on the same framework. Reveals the Domain inheritance strategy and the Combined view on the . Default off.
posture_assessments — : continuous measurement of assets against technical baselines (CIS Benchmarks, hardening guides) with recurring scan results. Default off.
personal_data — personal-data inventory inside the privacy register.
purposes — purposes register.
right_requests — data-subject right requests.
data_breaches — data-breach tracking.
service_accounts — for machine-to-machine API access via OAuth2 client credentials. PRO.
chat_mode — the in-product chat assistant. Default off; only visible when ENABLE_CHAT is set on the instance.
Instance-wide settings that don't belong to a more specific category. Grouped here by intent — the order in the UI may differ.
Display and interface
Security objective scale — the labels used for ratings of security objectives (e.g. C/I/A).
EBIOS radar configuration — the max radius and the green / yellow / red zone radii used to draw the EBIOS RM radar chart.
Aggregated scenario matrix — toggles the aggregated view of scenarios on the risk-matrix display.
Risk matrix axis options — swap axes, flip vertical, custom axis labels. Cosmetic adjustments to how every risk matrix is rendered.
Display qualifications as risk categories — swaps the term qualification for risk category throughout the interface: field labels on risk scenarios, incidents, feared events, escalation thresholds and quantitative risk scenarios, plus the matching filters and analytics charts. The values themselves (confidentiality, integrity, …) are managed as either way.
Default language — fallback locale for users who haven't picked a preference and for system-generated emails. Must be one of the languages enabled in the instance build.
Currency — the unit used when displaying applied-control costs and quantitative-risk amounts.
Daily rate — default daily cost used when expressing effort in monetary terms.
Conversion rate — write-only field used when changing the currency to convert existing cost amounts in one operation.
When you change the currency with a conversion rate, the platform sweeps every applied control's cost structure and applies the conversion. Without a conversion rate it just relabels — the numbers stay the same.
Allow self-validation — whether a user can validate workflows they themselves created. Off by default for separation-of-duty reasons.
Show warning on external links — interstitial prompt before opening links that leave the platform.
Enforce MFA — make multi-factor authentication mandatory for every user account.
Built-in metrics retention (days) — how long built-in metric samples are kept before being aged out. Minimum 1.
Enable email notifications — master switch for outbound email notifications. See also .
These settings drive the optional AI features (chat mode, agentic workflows, RAG over the knowledge base):
LLM provider — which model server the platform calls: Ollama, or OpenAI-compatible (LM Studio, vLLM, llama.cpp...) for any server speaking the OpenAI chat completions API, hosted or local.
Ollama base URL, Ollama model, Ollama embedding model — used when the provider is Ollama.
To connect a hosted service such as OVHcloud AI Endpoints or OpenRouter, see .
Default custom analytics dashboard — UUID of the dashboard shown by default on the analytics page.
Shown only when the Domain-tree audit inheritance feature flag is on (under the Compliance assessments group).
Domain inheritance strategy — when the same framework is audited at several levels of the domain tree, decides how a child audit combines results with its parent audits. Options: No inheritance (default), Parent always wins, Child always wins, Best case (optimistic), Worst case (prudent). Scores from a different scale are normalised to the top parent's scale. This one org-wide setting drives the Combined view on the and the inheritance panel in . Leaving it on No inheritance keeps the feature dormant even with the flag enabled.
Allow assignments to entities — whether requirements and tasks can be assigned to third-party entities (not just internal users).
Mapping max depth — how many frameworks a chained mapping path may span when projecting one framework onto another, counted as nodes in the path (2–5, default 3). A value of 3 allows one pivot — A → B → C. See .
API base URL, Model name, API key — used when the provider is OpenAI-compatible. The key is write-only and never returned by GET.
Embedding backend — which backend powers semantic search over knowledge. Local sentence transformers or Ollama; hosted providers are not offered here, so selecting one for the LLM leaves embeddings where they are.
System prompt — system prompt prepended to chat-mode conversations.
Send temperature to the model and Temperature — whether to pass a sampling temperature, and its value. Some servers reject the parameter, which is why it can be switched off.
Language
Money
Behaviour
Retention
Notifications
AI / LLM provider
Local or self-hosted LLMs. The Ollama URL and OpenAI API base URL are checked when you save them and must resolve to a public address — private, loopback, and internal IPs are rejected to prevent server-side request forgery (SSRF). To point at a local or in-network model (for example Ollama on localhost, LM Studio, or an in-cluster endpoint), start the backend with the environment variable ALLOW_PRIVATE_NETWORK_REQUESTS=True. (This variable was previously named WEBHOOK_ALLOW_PRIVATE_IPS; the old name is no longer recognized.)
CISO Assistant can optionally enrich its vulnerability and security-advisory catalogues by polling external threat-intelligence feeds. These switches control which feeds are active and how the platform reaches them.
All feeds are off by default — they make outbound network calls, so opt in deliberately.
Available feeds
KEV feed (kev_feed_enabled) — CISA's Known Exploited Vulnerabilities list. Tags vulnerabilities that are confirmed to be exploited in the wild so they can be prioritised.
EPSS feed (epss_feed_enabled) — FIRST's . Attaches a probabilistic exploitation score to each CVE, useful for prioritisation alongside CVSS severity.
NVD enrichment (nvd_enrich_enabled) — pulls extra metadata from the (CWE mappings, affected configurations, references).
Network timeout (network_timeout) — seconds to wait before giving up on a feed call. Default 30, range 5-120. Tune up if you're behind a slow egress proxy; tune down if you want feed failures to surface quickly rather than block other work.
Enabling a feed doesn't backfill the entire history — feeds are consulted from the moment they're enabled. To enrich historical entries, look for a "refresh" action on the relevant catalog (depends on the feed).
Feed calls happen in background jobs (Huey workers), so toggling a feed doesn't block the request that saves the settings.
Outbound HTTPS access to the feed endpoints is required. The platform doesn't ship with mirrored data.
The Vulnerability SLA policy defines, by severity, how many days a vulnerability is allowed to remain open before it breaches the organisation's service-level agreement.
The platform uses this policy to compute remediation deadlines, surface overdue vulnerabilities, and feed SLA dashboards.
Anchor
SLA anchor — the date used as day 0 when computing the deadline. Two options:
detected_at — the date the vulnerability was first recorded in CISO Assistant (default).
published_date — the date the vulnerability was publicly disclosed (relevant when working from CVE feeds).
The choice matters: published_date tightens deadlines for vulnerabilities that were known publicly before you imported them; detected_at gives a fresh clock starting from import.
For each severity level, set the number of days allowed to remediate. Leave a field empty (null) to mean no policy at this severity.
Critical — typically the tightest (e.g. 7-14 days).
High — typically 30 days.
Medium — typically 60-90 days.
Vulnerabilities whose anchor_date + deadline_days is in the past are flagged as SLA breached.
Severity is read from the vulnerability record itself; the policy is a lookup, not a per-vulnerability field.
Changes to the policy apply going forward and retroactively recompute the breach status of existing vulnerabilities.
Low — typically 180+ days, or unset.
Info — usually unset; informational entries aren't on a clock.
Deadlines by severity
Behaviour
Allowed IP whitelist
Control which IP addresses and CIDR ranges are allowed to reach the CISO Assistant backend. Administrators manage the allowlist from the settings UI, and the infrastructure layer picks it up automatic
What it does
The Allowed IPs whitelist lets administrators decide which IP addresses and CIDR ranges are permitted to reach the backend API. It is the simplest way to restrict API access — for example, to let a CI runner, an office network, or a machine running the MCP server talk to your instance while keeping everyone else out.
You manage the list from Settings → Infrastructure:
Settings → Infrastructure → Allowed IPs
Type an IP address (e.g. 203.0.113.4) or a CIDR range (e.g. 198.51.100.0/24) and click Add.
Remove an entry with the ✕ next to it.
Click Save to apply your changes.
Both IPv4 and IPv6 are supported, up to 50 entries.
CISO Assistant SaaS — already enabled. The Infrastructure tab is available to administrators out of the box; just manage your allowed IPs.
On-premises — disabled by default. If you want this self-service allowlist, enable it by setting the following environment variable on the backend and restarting it:
When disabled, the Infrastructure tab is hidden and the API endpoint below is not registered.
Once configured, the allowlist is published on an /infra-config/ endpoint so your reverse proxy, firewall, or security group can read it and apply the rules automatically — instead of you editing infrastructure config by hand.
From a machine that can reach the backend (not from the public internet):
If the endpoint returns a 404, make sure ENABLE_INFRA_CONFIG_MANAGEMENT=True is set and that the backend has been restarted.
Branding
Available on the PRO plan.
Branding
Branding settings let you replace the default CISO Assistant visuals with your organisation's identity — useful when the platform is deployed for end-customers, when a parent company hosts multiple subsidiaries, or simply to align the UI with internal design guidelines.
Settings
Client name — the display name used in headings and email signatures.
Logo — replaces the default product logo in the header. Accepted formats: .png, .jpeg, .jpg, .webp, .svg.
Show images to unauthenticated users — when on (default), the logo and favicon are visible on the login screen and other pre-authentication pages. Turn off to keep branding gated behind authentication.
Logo and favicon are stored as files inside the instance, not as external URLs. Upload them through the Settings UI rather than editing the database directly.
The platform serves a content-hash header alongside each image so browser caches stay coherent when the file is replaced.
Empty list → no access. Saving with no entries blocks all traffic to the backend. The UI asks you to confirm before you do this.
0.0.0.0/0 or ::/0 → any IP. These wildcard ranges open access to everyone, which defeats the purpose of the allowlist.
Make sure your own IP (and anything that needs API access) is in the list before you save.
On SaaS, changes take ~10 minutes to apply. After you save, allow up to 10 minutes for the new rules to take effect — a background job reconciles the allowlist with the infrastructure on a 10-minute cycle. There's nothing to configure; just wait before testing. On-premises, propagation depends on how you wire enforcement (see Availability below).
Availability
On-premises, you have to wire up enforcement yourself. Enabling the flag only exposes the allowlist on /infra-config/ — it does not filter traffic on its own (the automatic 10-minute reconciliation mentioned above is specific to CISO Assistant SaaS). You are responsible for consuming the endpoint and applying the rules in your own infrastructure.
In particular, /infra-config/ is served at the URL root, not under /api/. The default Caddy configuration only reverse-proxies /api/* to the backend and everything else to the frontend, so the endpoint is not reachable through Caddy until you add a route for it. Edit your Caddy configuration to proxy it directly to the backend — for example:
Keep /infra-config/ restricted to your trusted infrastructure (it is unauthenticated — see the warning below).
If you rely on API access — for instance the MCP integration — IP filtering must be in place and your IPs added to the allowlist. SaaS administrators only need to add their IPs; on-premises administrators should enable the feature first.
How the infrastructure layer consumes it
The /infra-config/ endpoint is unauthenticated, exactly like /metrics. Never expose it to the public internet — keep it reachable only from your infrastructure layer (restrict it to trusted networks or bind it to an internal interface).
Custom templates let you replace the default content CISO Assistant uses for outbound emails and for document exports.
Administrators only. Creating, editing and deleting custom templates is restricted to users with the Administrator role. Templates control the wording of system emails and the body of exported documents, so a malicious or careless template could mislead recipients (e.g. a forged password-reset email) or embed inappropriate content in official exports.
Word templates carry additional risk:
Three template types live under this setting:
Email templates — the body and subject of system-generated emails (notifications, invitations, password reset, …).
Word templates — .docx files used as the visual skin for document exports (audit reports, risk-treatment plans, BIA outputs).
Document layout templates — .html files that control the PDF layout of managed documents (cover page, header/footer, branding).
Every template is identified by two fields:
Template key — a stable identifier for what the template represents (e.g. audit_completion_notification, audit_report_export). The platform looks up the active template by key when the relevant action fires.
Language — the locale the template applies to. The platform falls back to the instance's default language if no template for the user's locale is present.
A template is only used when its is_active flag is on, which lets you stage a draft alongside the live version and flip the switch when ready.
Each email template carries a subject and a body. Body content supports the same templating variables as the default emails — use the in-app preview to inspect the variables available for a given key.
Each Word template is an uploaded .docx file containing styled placeholders. The platform substitutes the dynamic content (assessment data, scores, evidence list) when generating the export.
Document layout templates control how a managed document is rendered to PDF — on Export PDF and when a document is published. Each is an uploaded .html file (Django template syntax, with CSS in a <style> block), registered under the document_pdf template key, per language.
When an active template exists for the document's language, the platform renders the document's content into your layout instead of the built-in one — letting you add a cover page, headers and footers, and your organisation's logo (from ). The template receives the document's title, rendered content, version, document_type, date, and the organisation logo.
Download the built-in default first (from the setting) to use as a starting point, then upload your customised version. The built-in layout is used as a fallback when no active template exists for a language, and also if a custom template contains a disallowed tag or fails to render — a broken template never blocks a document from being published or exported.
Editing a template doesn't retroactively change documents that have already been exported.
Inactive templates remain in the database for audit-trail purposes.
Template injection — .docx templates are rendered with a Jinja2-based engine. A crafted template can contain expressions that are evaluated on the server, which an attacker could abuse to read data or run unintended logic during export (server-side template injection).
Malicious macros — .docx files can embed VBA macros that execute on the machine of whoever opens the exported document. A booby-trapped template effectively ships malware to every report recipient.
Document layout templates carry the same template injection consideration: .html templates are rendered server-side with Django's template engine before the PDF is produced, so a crafted template could evaluate unintended expressions.
Keep this permission limited to trusted administrators, only upload templates from sources you control, and review any template before activating it.
You can find here CISO Assistant global organization. All entities will be linked to or contained within these objects.
A folder organization
For Access Control purpose, CISO Assistant data is organized in a tree of folders. Starting from a root folder called Global, it divide into sub-folders called domains. The organization of the tree is not hard-coded, it is entirely determined by configuration. Any object in CISO Assistant is attached to a folder (including folders), either directly or indirectly through a parent object that is attached to a folder.
Organization example
So, what is a domain?
A domain permits to organize your work depending on your use of CISO Assistant. For example, inside a company, you can create a domain for each department for which you need to carry out a variety of perimeters, or if you have different customers, you may as well have a domain for each one in order to delimit your work area.
Utility
A domain is the first thing you create on CISO Assistant. It will bring together all objects you need to complete your different perimeters. Every role/permission a user has on a domain are applicable to all objects/actions across the domain. It's all about organization, the only technical aspect is access control, and this is achieved by adding the user to the relevant user group.
Role assignment
In the first/open source version of CISO Assistant, custom role assignment is not available. So, when you create a domain, user groups concerning this domain are automatically created for each built-in role. All you need to do, is to assign your users to their user groups. To learn more about this, jump to .
Perimeters are fundamental context objects defined by the entity using CISO Assistant. They are grouped in domains. They will contain all your risk and audits. Apart from being able to group your various evaluations across the different domains.
There are two specific fields, internal reference and status. Here are the various status options:
-- (None)
Design
Development
Production
The purpose of a perimeter is at first, it's organizational aspect to solve a problem. But it also makes it possible to improve analytics by breaking them down according to the different assessments, whether for risk or compliance, so as to make your project management more precise and reduce noise.
User groups go hand in hand with domains. they associate permissions with users and define their scope, by being attached to a domain. They follow a simple and consistent RBAC model from a role containing permissions and a domain determining the perimeter. Go to the page for more details.
User groups are built-in objects giving permissions to all users inside of them, with a specific role across a scope.
For now, it is not possible to create custom role assignments so you need to use built-in user groups. They are linking a domain with a role which contains precise permissions, that will be given to users in this group.
Roles
Let's give some details on the 5 built-in roles:
Role
Permissions
Administrator
Once your instance is created, five user groups are already present:
Global - Administrator
Global - Analyst
Global - Reader
Global - Approver
They give corresponding permissions on Global scope so on every object of your instance.
They are created for each domain you add. For example, if you create a domain R&D, there will be:
R&D - Domain Manager
R&D - Analyst
R&D - Reader
R&D - Approver
They give corresponding permissions on the domain scope so on every object inside R&D.
You can manage the members of a user group directly from the group's detail page. This is available to administrators, and to domain managers for the groups of their own domains: membership is governed by the change user group permission on the group's domain, so a domain manager can add or remove members without needing global user-management rights.
Open the user group and click Add members. A picker opens listing the users that are not yet in the group:
type in the search field to filter by email, first name or last name, or switch to Browse for a table view with per-column filters;
tick Include inactive to also list deactivated users;
your selection is kept while you search and change pages, and is summarised at the bottom of the picker;
On the group's Users tab, tick the members to remove, then click Remove from group:
Global - Respondent
R&D - Respondent
click Add members to confirm.
full access (except approval), and specifically management of domains, users and users rights
Domain manager
full access to selected domains (except approval), in particular managing rights for these domains. Read access to global objects
Analyst
read-write access to selected perimeters/domains. Read access to global and domain objects
Reader
read access to selected perimeters/domains
Approver
like reader, but with additional capability to approve risk acceptances
Respondent
see for more details
Django superuser is given administrator rights automatically on startup.
Global user groups
Domain user groups
Managing group members
Adding members
Removing members
Two safeguards apply: the last member of the Global - Administrator group can never be removed, and domain managers cannot remove themselves from a domain administrator group — another administrator has to do it.
Under Organization, click on Users and then Add user:
Set up the email of the new user:
Once created, a new user doesn't have any permissions by default. Click edit and update the user groups:
If you are working on a single domain, or working on solo, you might just set `Global - Administrator`
When the user are added, and if the mailer is set, he/she will receive an email to set up the password. If not, you can set a temporary password as illustrated above.
Disable a user's MFA
If a user has lost access to all their MFA factors (phone wiped, hardware key lost) and has no recovery codes left, an administrator can disable their MFA from the user's edit page. The link only appears when the target user has MFA enabled and you are not editing yourself. See for the full procedure.
Enterprise (PRO) feature — ships in the Enterprise edition and is not reachable on the community edition.
Custom roles allow you to refine access control when very specific permissions need to be granted to a user.
In CISO Assistant, domains are the primary mechanism for restricting access. Custom roles should only be created when domain-based restrictions are insufficient.
Keep in mind that custom roles increase IAM complexity and make access reviews more difficult. In most cases, the default roles are sufficient.
Learn how to create and customize roles such as DPO and OPS within your organization
Sign in as an Admin on your CISO Assistant instance and follow the steps below.
1. Introduction
This guide walks you through setting permissions and assigning user groups to streamline your CISO Assistant's role management.
Introduction
2. Click "Organization"
Navigate to the Organization section to begin managing your team's roles.
Access the Roles tab to view and modify existing roles.
Initiate creating a new role by selecting the option to add one.
Enter the name for the new role, such as 'DPO', to define its identity.
Save the newly created role to confirm its addition to your organization.
Open the permissions settings for the new role to customize access.
Expand the permissions list to view all available options.
Select all permissions to grant comprehensive access to the role.
Save the permission settings to apply them to the role.
Start creating another role by selecting the add role option again.
Name this role, for example 'OPS', to specify its function.
Save the role to add it to your organization's role list.
Access the permissions for the newly created role to tailor its access.
Open the detailed permissions menu to adjust specific controls.
Select the option to edit applied controls for fine-tuning.
Modify the applied control by entering a specific control number, such as '205'.
Confirm the changes made to the applied control settings.
View the applied control details to verify the configuration.
Update the applied control with another control number, like '207', if needed.
Return to the permissions overview to continue adjustments.
Select all permissions to ensure full access for the role.
Save all permission changes to finalize the role's capabilities.
Switch to the User Groups section to manage group memberships.
Navigate to the Users tab to assign roles to individuals.
Select a user to modify their group memberships and roles.
Open the user's group assignment settings to begin editing.
Enter the role name, such as 'OPS', to assign it to the user.
Choose the corresponding user group, like 'ACME - OPS', for the role.
Input another role name, for example 'DPO', for additional assignments.
Select the matching user group, such as 'Global - DPO', for this role.
Review all assigned user groups and roles to ensure accuracy.
Save the user group and role assignments to complete the process.
This guide detailed how to create and configure custom roles like DPO and OPS, assign comprehensive permissions, and manage user group memberships effectively. It ensures your organization’s roles are tailored and users are properly assigned for optimal access control.
3. Click "Roles"
4. Click here
5. Fill "DPO"
6. Click "Save"
7. Click here
8. Click here
9. Click "Select all"
10. Click "Save"
11. Click here
12. Fill "OPS"
13. Click "Save"
14. Click here
15. Click here
16. Click here
17. Click "Edit applied control"
18. You can select an individual permission
19. Click here
20. Click here
21. Click here
22. Or you can click "Select all"
23. Click "Save"
24. Click "User groups" to see your newly added roles
Access security is a foundational aspect of any risk or compliance management platform. In this article, we’ll explore how authentication, authorization, and accounting — the three pillars of the AAA model — are structured and applied within CISO Assistant.
1. Authentication: SAML vs OIDC
CISO Assistant integrates with leading identity providers (IdPs) via SAML and OIDC, enabling secure and seamless single sign-on (SSO).
SAML
legacy protocol based on XML
common in large enterprises, especially for AD
browser-based redirection with signed assertions
modern standard built on OAuth2
uses JWT tokens for identity transport
also browser-based, but more lightweight and versatile
Recommendation: If your IdP supports both, prefer OIDC — it's more modern, flexible, and aligned with today’s security practices.
Multi-Factor Authentication (MFA) is critical for access protection. CISO Assistant supports MFA in two distinct modes, depending on how users authenticate:
MFA is handled entirely by the identity provider (IdP)
the IdP enforces the policy (push notifications, TOTP, biometrics, etc.)
for local accounts, CISO Assistant includes native MFA
based on TOTP (e.g., Google/Microsoft Authenticators) and WebAuthn / passkeys (hardware security keys, platform authenticators)
recovery codes are issued at enrolment so the user can self-recover from a lost factor
CISO Assistant implements a robust Role-Based Access Control (RBAC) model that balances flexibility, clarity, and operational simplicity.
Each object type has granular CRUD permissions (create, read, update, delete). This model applies across all business entities: users, backups, risks, policies, incidents, data processing, and more. There are more than 200 permissions in CISO Assistant.
Permissions are grouped into a small set of standard roles:
Administrator – full access to all objects and settings
Analyst – full access to most objects, but cannot modify access control
Viewer – read-only access
Roles are assigned within a domain — a flexible concept representing any relevant business context.
For example, a domain can represent:
a legal entity
a country or region
a subsidiary
Domains are hierarchical: a role assigned to a parent domain (e.g., "Group") automatically applies to all its subdomains (e.g., subsidiaries, teams).
Access control is defined via explicit assignments:
A role ➡️ on a domain ➡️ for a group of users
Users do not have direct roles. They inherit permissions through membership in one or more groups.
Groups act as the central pivot for managing access:
receive role assignments
grant users permissions via group membership
defined locally
optionally synced with an IdP (via external plugin)
🚀 This simple yet powerful model accommodates the vast majority of real-world access scenarios. And when needed, the system is fully extensible: it supports custom roles, custom role assignments, and custom user groups to fit even the most specific organizational needs.
CISO Assistant doesn’t just secure human access — it also supports secure, auditable access for automated systems and integrations through Personal Access Tokens (PATs).
A Personal Access Token is a time-limited secret that allows a script, CI/CD pipeline, or service to authenticate with the platform's API on behalf of a user or machine identity — without requiring an interactive login.
time-bounded: expiration is mandatory
RBAC-compliant: inherits the creator’s permissions
revocable: can be revoked by user or admin
admins can restrict who may generate PATs
all tokens are auditable and managed via UI or API
This ensures tight control over non-human access, balancing automation flexibility with strict security hygiene.
The following schematic illustrates the fundamental concepts of IAM in CISO Assistant.
All objects of CISO Assistant support a built-in flag called is_published.
Objects with the flag is_published are visible in subdomains as if they were attached to each subdomain of the object's domain. This mechanism only concerns visibility, not creation/udpate/deletion.
All objects are currently published, except assessments (audits, risk analysis, BIA, entitiy assessments)
To avoid an object being published, the simplest solution is to put it in a leaf subdomain.
The plan is to remove this mechanism by Q2 2026 and introduce dynamic groups instead (e.g. the group of all users).
You can attach this object to a subdomain (e.g. named "published"), and add every user in the group corresponding to reader role on the subdomain. This does not rely on the publication mechanism, and is more generic.
CISO Assistant includes native tracking of all key actions:
a searchable audit log accessible via the UI or API
This enables complete accountability over critical operations.
CISO Assistant's AAA model is built on:
Open standards (SAML, OIDC, TOTP, RBAC)
A structured yet manageable authorization system
secure automation through scoped, revocable Personal Access Tokens (PATs)
It supports complex organizations while remaining readable, scalable, and compliant with modern security expectations.
if a user loses every factor and runs out of recovery codes, an administrator can disable their MFA from the user's edit page — see Setting up MFA → Admin recovery. The link is hidden on your own edit page; to disable your own MFA, use the standard MFA settings page on My profile → Settings.
Approver – strictly limited to approving risk acceptance requests
a business unit
any other meaningful organizational structure
Built-in traceability from the ground up
OIDC (OpenID Connect)
2. MFA
SSO-Based Authentication (SAML / federated OIDC)
Local Authentication
3. Authorization: Structured and Hierarchical RBAC
Fine-Grained Permissions
Predefined Roles
Hierarchical Domains
Role Assignments
User Groups
4. Machine Identity: Personal Access Tokens with Expiration & Control
Definition
Key features
Governance controls
5. Illustration
6. Publication mechanism
Can I make an object visible to all users without attaching it to global?
7. Accounting: Full Audit and Traceability
In Summary
SSO
Configure Single Sign-On with different SAML or OpenID Connect providers
By default, logging out of CISO Assistant only ends the local CISO Assistant session. The identity provider session stays open, so clicking Log in with SSO again signs the user straight back in without re-authenticating.
To also close the identity provider session on logout, enable Enable service provider-initiated single logout in the SSO settings. When it is on, logging out of CISO Assistant redirects the browser through the identity provider's logout endpoint.
This is service provider-initiated single logout: CISO Assistant asks the identity provider to end its own session for the user. Whether that in turn signs the user out of other applications federated to the same identity provider depends on the identity provider's single-logout configuration and is not something CISO Assistant controls or can guarantee.
The option is off by default, and each protocol needs a logout endpoint on the identity provider side:
OIDC — the provider must expose an end_session_endpoint, and <frontend_url>/login must be registered as an allowed post-logout redirect URI. See .
SAML — the identity provider Single Logout Service URL must be available (read from the metadata, or set in the SLO URL field). See .
Enabling SSO adds the Log in with SSO button but leaves the email/password form in place, so users can still authenticate locally. To make SSO the only way in, turn on Force SSO Login in the SSO settings.
When Force SSO Login is enabled, local password authentication is disabled for everyone — a user who tries the password form is rejected with "This user is not allowed to use local login."
To keep a few accounts able to log in locally (typically break-glass administrators, or a service account used while the identity provider is being set up), enable the per-user Keep local login flag on their user record. These accounts continue to work through the standard password form even while SSO is forced.
Some accounts get Keep local login enabled by default when they are created:
Superusers created with createsuperuser (or at first boot), so the initial administrator is not locked out.
Third-party users (portal / TPRM accounts).
Note that this is only a default on the flag, not a permanent exemption: a regular user promoted to superuser afterwards does not get it automatically, and unticking Keep local login on any of these accounts removes their local access like anyone else.
SCIM-provisioned users, by contrast, are SSO-only by design.
By default the login page shows the standard email/password form alongside a Log in with SSO button. You can send users straight to your identity provider — skipping the form — by appending ?sso to the login URL:
Opening that link starts the SSO redirect immediately, exactly as if the user had clicked Log in with SSO. It's convenient as a bookmark, or as the link you publish internally when SSO is the expected way in.
To send the user to a specific page after they authenticate, add a next parameter:
Single Logout
Keep CISO_ASSISTANT_URL set to the public frontend URL, otherwise the post-logout redirect will not resolve.
Forcing SSO and local-login exceptions
Turning on Force SSO Login clears the password of every account that does not have Keep local login enabled. Set Keep local login on your exception accounts before you enable Force SSO Login — otherwise their passwords are wiped, and re-enabling the flag afterwards does not restore them (the user has to go through a password reset, which requires a working mailer). Always confirm at least one break-glass account can still log in before forcing SSO.
Direct SSO login link
?sso only triggers the redirect when SSO is enabled. Users who are allowed to keep local login — for example break-glass administrators — can still reach the password form through the plain /login URL.
Teams in CISO Assistant are used to group users who work together on the same security activities, such as risk assessments, compliance programs, or audits.
Each team has a Team Leader, optional Deputies, and Members, making it easy to reflect real-world responsibilities and delegation. A team can also have a dedicated team email address for shared communications. Teams centralize ownership, collaboration, and notifications by allowing CISO Assistant to automatically reach the right people based on their role within the team.
Tasks, assessments, assets, applied controls, etc., can be assigned to teams, in the same way as to users.
OpenID Connect (OIDC)
Configure CISO Assistant with OpenID Connect (OIDC)
Once you've retrieved the Client ID, the Client Secret and the Issuer URL (sometimes called the discovery or .well-known URL) from your provider, the configuration on CISO Assistant is pretty simple.
Log in into CISO Assistant as an administrator > Extra > Settings
Navigate to SSO settings
Enable SSO
Select the OpenID Connect provider
Enter the Client ID
Enter the Client secret
Enter the Server URL
And that's it! Don't forget to click the 'Save' button
You should now be able to see the Login with SSO button
Turn on Enable service provider-initiated single logout in the SSO settings to close the OIDC session at the identity provider when users log out of CISO Assistant (see for the general behavior). It additionally requires:
an end_session_endpoint in the provider's OpenID configuration, and
<frontend_url>/login registered as an allowed post-logout redirect URI on the OIDC client.
CISO Assistant calls the end-session endpoint with client_id, id_token_hint, and that post-logout redirect URI.
Be aware that the user needs to be created on CISO Assistant to be authenticated with SSO.
CISO Assistant automatically sends a standards-compliant state and nonce on every OIDC authorization request (43 characters matching ^[A-Za-z0-9-._~]{43,128}$), and validates the nonce claim returned in the id_token. No configuration is required. This is helpful for identity providers that enforce format or length constraints on these parameters. Per OIDC Core 3.1.3.7, the nonce sent in the authorization request must be present and match in the id_token — a missing or mismatched nonce will reject the login.
Single Logout
With Microsoft Entra ID, OIDC logout still prompts the user to select an account, because Entra ignores the standard id_token_hint. The logout itself works, but the prompt cannot be suppressed without Entra-specific configuration. For a prompt-free single logout with Entra ID, use SAML instead.
You can log into CISO Assistant with any identity provider (IdP), given that it supports either SAML or OpenID Connect (OIDC).
Don't find documentation on how to set up SSO with your identity provider? Feel free to reach out to us on Discord, or contribute to the docs.
SCIM provisioning and IdP groups
Provision users from your identity provider with SCIM 2.0, and map external IdP groups to CISO Assistant user groups so that roles are granted automatically.
SCIM 2.0 lets your identity provider (Okta, Microsoft Entra ID, Keycloak, …) push users into CISO Assistant automatically, so you no longer have to create accounts by hand. IdP groups are the bridge between a group that exists in your identity provider and the CISO Assistant user groups it should grant.
This is a PRO feature. It is gated by the IdP groups feature flag (see ). While the flag is off, the IdP groups menu, the SCIM settings tab and the IdP groups column on the users table stay hidden. The flag is enabled by default on PRO instances.
The IdP groups feature flag in Settings › Feature flags.
How it works
Authentication and provisioning are two separate concerns, handled by two complementary mechanisms:
Your IdP provisions users and their group memberships through SCIM.
The link between the two worlds is the IdP group — a faithful mirror of a group that exists in your identity provider. An administrator maps each IdP group to one or more user groups. A user who belongs to an IdP group then inherits the roles of every user group it is mapped to.
This is a groups of groups model: membership flows from IdP group → user groups → roles, and the effective roles are recomputed on the fly. A user's direct (manually assigned) user groups and the ones inherited through their IdP groups are simply added together — neither overrides the other.
IdP groups live under Organization > IdP groups in the sidebar. The page behaves like any other object table — view, create, edit and delete.
Creating or editing an IdP group asks for two things:
Name — the display name of the group as it exists in your identity provider.
User groups — one or more CISO Assistant this IdP group should grant. Edit this list at any time; every member of the IdP group is re-granted immediately.
The members of an IdP group are managed by SCIM and shown read-only. You will also find an IdP groups column on the Organization > Users table, listing the IdP groups each user belongs to.
Log in as an administrator > Extra > Settings and open the SCIM tab.
Copy the SCIM endpoint URL (for example https://<your-instance>/api/scim/v2/). This is the base URL your identity provider connects to.
Click Generate token and copy the bearer token that is displayed.
You can generate several tokens (one per integration) and revoke any of them at any time from the same screen.
Each IdP group is identified by the UUID that CISO Assistant assigns it. Your identity provider stores that id and reuses it, so renaming a group in the IdP simply updates its name — memberships and the user-group mapping are preserved.
The first time your IdP pushes a group that does not exist yet, CISO Assistant creates the IdP group automatically.
Deleting an IdP group (or removing it from the IdP) removes the access it granted, but never touches a user's manually assigned user groups — those are a separate, direct membership.
SCIM provisions accounts; it does not replace authentication. Provisioned users still sign in through your configured provider.
CISO Assistant implements the inbound SCIM 2.0 Users and Groups resources (RFC 7643/7644) and is identity-provider agnostic.
In your identity provider's provisioning settings, enter the SCIM endpoint URL and authenticate with the token using the Authorization: Bearer <token> scheme.
Assign users and groups to CISO Assistant in your identity provider, and let it provision them.
An IdP group grants nothing until you map it to at least one user group. SCIM can keep pushing memberships into an unmapped IdP group safely; those users gain access only once the mapping exists.
Managing IdP groups
IdP groups are administered globally and are only visible to administrators, just like and roles.
Configuring SCIM
The token is shown only once. Copy it now — for security reasons it cannot be retrieved again. If you lose it, revoke it and generate a new one.
Please note: if OIDC mode has ben configured before, you mustreset the Client ID field to 0 in the OIDC tab and savebefore proceeding.
Configure CISO Assistant with SAML
Once you've retrieved the IdP Entity ID, the Metadata URL and the Entity ID from your provider (see the list of providers for specific details), the configuration on CISO Assistant is pretty simple.
Log in into CISO Assistant as an administrator > Extra > Settings
Enable SSO
Enter the Idp Entity ID
Choose the option 1 or 2 depending of your provider and fill Metadata URL or SSO URL, SLO URL, x509 certificate retrieved from your provider
Check that the SP Entity ID is similar to the Entity/Client ID specified on your provider
And that's it! Don't forget to save changes
You should now be able to see the Login with SSO button
Allow single label domains: This allows you to authenticate through SAML on a single-label domain (e.g. https://ciso-assistant:8443). If this is left unchecked, the only host forms allowed are:
IPv4
IPv6
Turn on Enable service provider-initiated single logout in the SSO settings to close the SAML session at the identity provider when users log out of CISO Assistant (see for the general behavior).
It also needs the identity provider's Single Logout Service URL. When you configure the identity provider by Metadata URL, this is read automatically from the metadata — the SLO URL field is only used when you configure the identity provider manually.
Some identity providers require signed logout requests. In that case, enable Logout request signed in the advanced SAML settings and configure the SP private key and certificate — and register that SP certificate with the identity provider so it can verify the signature. Microsoft Entra ID, for example, rejects unsigned SAML logout requests.
FQDN (e.g. https://www.example.com/)
localhost
Authn request signed: allows the Service Provider (SP) to digitally sign the SAML authentication request sent to the Identity Provider (IdP).
This option should be enabled if your IdP requires signed authentication requests or if you are looking to enforce additional security on SAML authentication flows:
Failure to do so will prevent proper SAML configuration. This behavior is known and will be addressed in future releases.
Single Logout
Be aware that the user needs to be created on CISO Assistant to be authenticated with SSO.
Configure Keycloak as an Identity Provider for CISO Assistant
If Keycloak and CISO Assistant are both deployed locally with docker, you'll need to make sure that both containers can communicate together. You can do this with a.
Go into your Keycloak admin console
Open the sidebar menu > Clients and Create client
Choose SAML client type and name it ciso-assistant or with your custom SP Entity ID
Fill the Home URL with your <base_url> and Valid redirect URIs with <backend_url/*>
If you have some problems to configure these urls you can ask for help on or by emailing us
Go into Keys and disable Signing keys config
Go into Advanced and fill ACS field with <backend_url/api/accounts/saml/0/acs/> (on a cloud instance it is simply <base_url/api/accounts/saml/0/acs/>)
Go to Client scopes and click on ciso-assistant-dedicated
Add a predefined mapper and check all X500 ones
Click on X500 surname and replace SAML Attribute name with http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname
Click on X500 givenName and replace SAML Attribute name with http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname
Go into Realm settings > General, you will find the Metadata URL
You'll find inside the Metadata URL the Entity ID
The same metadata also contains Keycloak's Single Logout Service URL, so it is picked up automatically. To also close the Keycloak session when users log out of CISO Assistant, just turn on Enable service provider-initiated single logout in the SSO settings.
Go into your Keycloak admin console
Open the sidebar menu > Clients and Create client
Choose OpenID Connect client type and give it a Client ID, then click Next
You can now with the parameters you've retrieved.
Enable Client authentication, make sure Standard flow is selected, then click Next
Enter your deployment's Root URL. It is the URL of your frontend.
Set it to <frontend_url>
For cloud deployments, you must set it to <base_url>
Set the Home URL to /
Enter your Valid redirect URIs
Set it to <backend_url>/api/accounts/oidc/openid_connect/login/callback/
For cloud deployments, you must set it to <base_url>/api/accounts/oidc/openid_connect/login/callback/
Enter your Valid post logout redirect URIs
Set it to <frontend_url>/login
For cloud deployments, set it to <base_url>/login
Once your client is created, you can find its Client secret under the Credentials tab. You can copy it from there
Go into Realm settings > General to find the OpenID Endpoint Configuration, which you will have to paste into CISO Assistant's Server URL SSO parameter
Adding a user in your application doesn't automatically create the user on CISO Assistant
Configure Microsoft Entra ID as an Identity Provider for CISO Assistant
Go into your Azure portal home
Open the sidebar menu and click on Microsoft Entra ID
Click on Add button > Entreprise application
Click on Create your own application
Enter a name and then click Integrate any other application you don’t find in the gallery (Non-gallery)
Click on Single sign-on from the sidebar menu or on Set up single sign on bellow Getting Started and choose SAML
In the first box Basic SAML Configuration, specify the Entity ID, it has to be the same than SP Entity ID in CISO Assistant (see next screenshot)
Add the Reply URL: <base_url>/api/accounts/saml/0/acs/ (for example with localhost: https://localhost:8443/api/accounts/saml/0/acs/)
In the third box SAML Certificates, copy the App Federation Metadata Url as it is the Metadata URL in CISO Assistant (see next screenshot)
In the fourth box Set up <App_name>, copy the Microsoft Entra Identifier as it is the IdP Entity ID in CISO Assistant
Make sure you use the same Identifier (Entity ID) that you've set earlier and appear on block 1, on CISO Assistant SP Entity ID:
Click on Users and groups in the sidebar menu, and Add user/group to give them access to CISO Assistant with SSO. The matching key will be the email and you'll be able to grant their permissions on the applications.
Click the App registrations section to add a new application for OIDC configuration. You can also use the search bar if you don't find it in the suggestions.
Navigate to App Registrations
3. Start New Application Registration
Start New Application Registration
4. Name your application
Name your application
5. Select Web Platform in Redirect URI options
Select Web Platform in Redirect URI options
6. Enter the callback URL of your instance
The callback URL is: <ciso_assistant_url>/api/accounts/oidc/openid_connect/login/callback/ for
for instance, for localhost: http://localhost:8000/api/accounts/oidc/openid_connect/login/callback/
Adding a user in your Entra application doesn't automatically create the user on CISO Assistant
7. Complete Application Registration
8. Copy the Application Client ID
9. Past it into the Client ID field
10. Open Certificates & Secrets
11. Create a New Client Secret
12. Add your Client Secret
13. Copy the fresh Client Secret Value
14. Past it into the Secret field
15. Go back to your App Overview
16. Inside Endpoints copy the OpenID Connect metadata URL
17. Paste it into the Server URL field
18. Save your configuration
You have successfully configured OpenID Connect (OIDC) integration with EntraID.
Enter the callback URL of your instance
Complete Application Registration
Copy the Application Client ID
Past it into the Client ID field
Open Certificates & Secrets
Create a New Client Secret
Add your Client Secret
Copy the fresh Client Secret Value
Past it into the Secret field
Go back to your App Overview
Inside Endpoints copy the OpenID Connect metadata URL
Paste it into the Server URL field
Save your configuration
Google Workspace
Configure Google Workspace as an Identity Provider for CISO Assistant
Google Workspace doesn't allow callbacks to urls containinghttporlocalhostso it can be tricky to test it locally. You should deploy CISO Assistant with a FQDN to bypass these restrictions.
Go into Google Workspace Admin console
On the sidebar menu, go to Applications > Web and mobile applications
Click on Add an application > Add a custom SAML Application
Enter ciso-assistant or the name of your choice and click on continue
You can copy the SSO URL, Entity Id and x509 certificate here but you'll be able to retreive them later
Fill ACS URL with <base_url>/api/accounts/saml/0/acs/, enter the Entity ID which has to be the same than SP entity Id in CISO Assistant (ciso-assistant by default) and choose Email in Name ID Format
Add two mappings for First name and Last Name, fill them with those two values: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givennamehttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname
On application home page, you can now find the Entity ID, SSO URL and x509 certificate
You can now with the 3 parameters you've retrieved.
Add a user in your application doesn't automatically create the user on CISO Assistant
Libraries are how content gets into CISO Assistant — frameworks, threats, risk matrices, reference controls, mappings. They ship as YAML files, are versioned, and can be upgraded or removed cleanly.
This section covers everything you need to manage them on your instance:
Configure Okta as an Identity Provider for CISO Assistant
Go into your Okta admin console (it should look like this: https://<your_url>.okta.com/admin/dashboard)
In the sidebar menu, click on Applications > Applications
Click now on Create App Integration
Select SAML 2.0 and click on Next
Choose an App name and click on Next
Add the Single sign-onURL: <base_url>/api/accounts/saml/0/acs/ (for example with localhost: https://localhost:8443/api/accounts/saml/0/acs/) (see screenshot below)
Add the Audience URI (SP Entity ID), it has to be the same than SP Entity ID in CISO Assistant (see screenshot below)
Choose Email as the Application username
Add Attribute Statements
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname for user's first name
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname for user's last name
Click on Next and fill in the Feedback page as you wish then click on Finish
In the Settings box inside SAML 2.0:
Copy the Metadata URL and paste it into the Metadata URL field in CISO Assistant
Copy the Issuer url and paste it into the IdP Entity ID field in CISO Assistant
Go to the Assignments tab
Click on Assign and choose whether you want to assign users or specific groups
You can now with the 3 parameters you've retrieved.
Add a user in your application doesn't automatically create the user on CISO Assistant
Multi-factor authentication adds an extra layer of security to your account. CISO Assistant supports both TOTP authenticator apps and WebAuthn security keys (FIDO2, hardware keys, fingerprint, passkey
CISO Assistant supports two second-factor methods, and both can be enrolled on the same account:
TOTP — time-based one-time passwords generated by an authenticator app on your phone (Google Authenticator, Microsoft Authenticator, 1Password, Authy, Bitwarden, …).
Security keys (WebAuthn) — hardware tokens (YubiKey, Titan, SoloKey, …), platform authenticators (fingerprint readers, Windows Hello, Touch ID, Face ID), and passkeys. Anything that speaks FIDO2 / WebAuthn works.
Recovery codes are always issued alongside the chosen method so you can regain access if you lose both your phone and your security key.
Pick at least one of the following:
A smartphone with an authenticator app installed (for TOTP), or
A WebAuthn-capable device — a hardware security key, or a platform authenticator like a fingerprint reader, Windows Hello, Touch ID, or a passkey-capable browser.
Plus, of course, access to your account settings on CISO Assistant.
Sign in to your account and navigate to My profile.
Select Settings.
Look for the Security section and click Enable 2FA.
Set up your authenticator app:
Sign in and navigate to My profile → Settings.
In the Security section, choose to enrol a security key.
When the browser prompts you, present the authenticator:
You can enrol multiple credentials on the same account — common patterns are a hardware key as the primary and a phone/passkey as the backup, or one key kept at the office and another at home.
After enabling MFA, you'll receive a set of recovery codes. These codes are crucial for regaining access to your account if you:
Lose your phone or security key
Uninstall your authenticator app
Cannot reach any of your enrolled second factors
If a user has lost access to every enrolled second factor (phone wiped, hardware key lost) and has no recovery codes left, an administrator can disable their MFA from the user's edit page:
Sign in as an administrator and navigate to Organization → Users.
Open the affected user and click Edit. In the security section of the edit page, click disable their MFA.
On the confirmation page, type the confirmation word shown on screen (the localized word for "yes") and submit.
All of the user's MFA authenticators (TOTP, WebAuthn credentials, recovery codes) are removed. The user will need to enable MFA again on their next login.
When MFA is enabled, the login flow asks for a second factor after the password. If you've enrolled both a security key and TOTP, the platform prefers the security key prompt by default and offers a "use authenticator app instead" link as a fallback. Either method completes the sign-in.
Test your MFA setup by logging out and back in.
If you enrolled a hardware key, enrol a backup (a second key, a passkey, or TOTP) — losing the only one is the most common lockout scenario.
Reach out for support if you encounter any issues during setup.
Starting v3.13.0 you can enforce MFA for all users by enabling this flag. Users will see a persistent redirect to the MFA configuration page until enrolment is done. The feature doesn't interfere with SSO as long as the user doesn't have both a local account and an SSO one.
Open the app on your smartphone.
Scan the QR code displayed on screen.
Alternatively, enter the provided secret code manually.
Enter the 6-digit verification code shown in your authenticator app.
Click Enable 2FA to complete the setup.
Hardware key — insert it and tap when it blinks.
Platform authenticator — confirm via fingerprint, face recognition, or device PIN.
Passkey — pick the existing passkey from your password manager.
Give the credential a recognisable name (e.g. "YubiKey blue", "MacBook Touch ID") so you can identify it later if you enrol several.
Prerequisites
Enable TOTP (authenticator app)
Enable a security key (WebAuthn)
Important: save your recovery codes
Security Warning:
Store your recovery codes in a secure location, separate from your password
Each recovery code can only be used once
Never share your recovery codes with anyone
Consider storing a copy both digitally (in a password manager) and physically (printed in a secure location)
Admin recovery: disabling another user's MFA
Audit notes:
Every admin MFA disable is logged on the backend (admin id/email, target id/email, number of authenticators removed).
The link is hidden on your own edit page — to disable your own MFA, use the standard MFA settings page on My profile → Settings.
Only users in the built-in Global - administrator group can disable another user's MFA.
Logging in with MFA
Next steps
Enforce MFA for all users
The "Disable MFA" link on a user's edit page (visible only to admins, when the target user has MFA enabled).
The confirmation page requires typing the localized confirmation word shown on screen before MFA can be disabled.
Designing your own libraries
This documentation explains how to create, maintain and evolve custom libraries for CISO Assistant using Excel, YAML, and the official tools provided in the community repository.
A library is a container that bundles one or more governance objects that can be imported into CISO Assistant.
A single library may contain:
Framework
Threats
Reference controls
Risk matrices
Answer sets
Score definitions
Implementation groups
Mappings between frameworks
Libraries are versioned, portable, and reusable across projects.
In practice:
Excel (.xlsx) is the authoring format
YAML (.yaml) is the import/export format
Python tools are used to convert and validate data
Before creating your first library, it is important to understand a few core concepts.
A framework is a hierarchical structure representing a standard, regulation, or internal control model.
Framework _meta tabs define the default scoring scale with min_score, max_score, and scores_definition. A row in the framework _content tab can override that scale for a specific requirement:
min_score — integer minimum score for this requirement.
max_score — integer maximum score for this requirement.
scores_definition_ref — name of a scores object block from the framework's alternatives registry to use as the level labels for this requirement.
Each field cascades independently. Leave the cells blank to inherit the audit-level value at runtime; that audit value is usually initialised from the framework when the audit is created. If you override the range, make sure the referenced alternative covers every integer score from min_score to max_score; otherwise conversion or import validation will reject the library. Also keep choice add_score values within the requirement's effective range.
Threats represent events or situations that may negatively impact an organization.
Typical examples include:
Data breach
Ransomware attack
Insider threat
Loss of availability
Threats are designed to be reusable across multiple frameworks and libraries.
Threats are defined using:
One _meta tab of type threats
One _content tab listing individual threats
Each threat includes:
A ref_id
A name
A description (optional)
Annotations (optional)
Threats are usually referenced from framework requirements using their URN.
Reference controls describe controls or safeguards that can mitigate threats or help fulfill requirements.
They are typically used to:
Document expected controls
Link requirements to best practices
Support evidence collection and audits
Examples:
Access control policy
Backup procedures
Network segmentation
Incident response process
Reference controls are defined using:
One _meta tab of type reference_controls
One _content tab listing individual controls
Each control include:
A ref_id
A name
A description (optional)
A category (policy, process, technical, physical, procedure) (optional)
An CSF function mapping (optional)
Annotations (optional)
Risk matrices are used to model risk evaluation logic, typically combining probability and impact into a resulting risk level.
They define how risk is calculated, not how compliance is assessed.
Risk matrices are usually:
Independent from frameworks
Shared across multiple projects
Maintained separately from compliance libraries
For this reason, they are most often packaged alone in their own library, without any framework.
A risk matrix is defined using:
One _meta tab of type risk_matrix
One _content tab describing:
Probability levels
Impact levels
Risk levels
The risk grid mapping probability × impact to risk
The grid defines the logical relationship between values.
The visual rendering (orientation, colors, layout) is handled by the CISO Assistant interface.
Risk matrices are not trivial to design correctly, especially the grid logic.
For this reason, we strongly recommend starting from one of the existing examples available in tools/excel/matrix.
These examples:
Are already valid and tested
Follow best practices
Can be adapted to your own risk model
Risk matrices can be converted and imported using the same tools as other library objects.
All tools described below are located in the /tools directory of the community repository.
These tools are designed to:
Reduce human errors
Enforce the CISO Assistant data model
Make libraries easier to maintain over time
You do not need to use all tools in every project. Each tool addresses a specific user intent, described below.
Before using any of the conversion or preparation tools, make sure your environment is correctly set up.
You will need:
Python 3.14 or higher and pip (included with Python)
A local copy of the community repository
In the /tools directory, run the following commands:
This installs all required Python dependencies needed to run the tools.
Relationship type (equal, subset, superset, intersect)
Optional rationale and strength
2
Once the Excel file is completed:
The conversion tool will:
Validate the mapping
Generate a YAML mapping library
Once your Excel file has been successfully converted into a YAML file, the final step is to import the library into CISO Assistant.
This operation is performed directly from the CISO Assistant interface and does not require any additional tooling.
Before importing your library, make sure that:
Your Excel file has been successfully converted using convert_library_v2.py
The resulting .yaml file contains no validation errors
1
Open CISO Assistant
2
In the left navigation menu, go to:
Governance → Libraries
3
Click on the button
4
Select your generated .yaml file
5
If the file is valid:
The import will be accepted immediately
The library will be added to your available libraries
Once the library is imported, go to Compliance → Frameworks.
Your custom framework will appear in the list (you can search it in the search bar). It can now be used in audits if it's a framework, or used as mapping of it's a mapping
If the import fails, CISO Assistant will display an explicit error message.
Common causes include:
Missing mandatory fields in library_meta
Invalid or duplicated URNs
Inconsistent framework hierarchy
Invalid references to threats, controls, or IGs
In most cases:
Fix the issue in Excel
Re-run convert_library_v2.py
Re-import the updated YAML file
After importing a framework, it is strongly recommended to:
Create a test audit using the framework
Verify:
Hierarchy rendering
Assessable requirements
Questions and answers
Scores and implementation groups
This validation step ensures that:
The framework behaves as expected
No structural or logical issues remain
Always increment version in the library_meta sheet.
If the version is unchanged, CISO Assistant will ignore the update.
In practice, Risk matrices and Mappings between frameworks have their own library for practical reasons.
2. Key Concepts
2.1 Frameworks and Requirements
2.2 Assessable vs Non-assessable Nodes
2.3 Hierarchy and Depth
2.4 URNs and ref_id
Changing URNs breaks mappings and historical data.
2.5 Implementation Groups (IG)
2.6 Scores and Answers
2.7 Mappings
3. Recommended Workflow
4. Creating a Framework using Excel (v2 format)
This section doesn't explain all the object types yet, neither the type of values to put in the columns. In the meantime, you can refer to the for more up-to-date information.
4.1 Why Excel?
4.2 Excel file structure (v2)
4.3 Example Framework (Strongly Recommended)
Why this example matters
Recommended way to use the example
When to come back to the example framework
Before opening a support ticket related to framework creation, consulting the example framework may sometimes solve your problem.
4.4 The library_meta Tab
Versioning is critical. If the custom framework is already imported into CISO Assistant and the version number has not been incremented, CISO Assistant will not suggest an update.
4.5 Framework definition Tabs
Order matters: the hierarchy is inferred from row order + depth.
Requirement-level scoring overrides
4.6 Threats
Structure
A library may contain only threats, without any framework. This is a common pattern when building shared threat catalogs reused across multiple frameworks.
4.7 Reference Controls
Structure
Reference controls can be defined independently of any framework and packaged in their own library, exactly like threats.
4.8 Risk Matrices
Typical usage
Structure
Important Note: Use Existing Examples
5. Using the Tools
Prerequisites
This setup only needs to be done once.
5.1 Creating a Framework Skeleton (Recommended Starting Point)
Typical use cases
What this tool does
Recommended usage (simple)
Advanced usage
After running this tool, the library_meta tab and the framework _meta tab usually do not need to be modified again, except if you want to:
Add translations
5.2 Converting an Excel file to a Library (Main conversion step)
Typical use cases
Basic usage (recommended for most users)
Verbose mode (recommended during troubleshooting)
Bulk mode (multiple frameworks)
Compatibility modes (advanced users)
If you are unsure, do not use compatibility modes.
5.3 Migrating Legacy Frameworks (v1 → v2)
Typical use cases
Usage
If you use convert_library_v2.py on an Excel file converted into v2, the YAML structure may be completely different from your original YAML file and break your audits if you import it into CISO Assistant!
You can use the convert_v1_to_v2.py script if you created your framework using the v1 format and provided that you HAVE NOT YET imported your framework into CISO Assistant.
You can also use it if you notice that the URNs in the YAML file created with the v2 version of your framework are identical to the URNs in the YAML file of the v1 version of your framework. You can also try using the script's compatibility modes to recover the old structure.
The result must be reviewed manually. Migration fixes Excel structure, not design quality. In some cases, you should still simplify and clean legacy content afterward.
6. Creating Mappings between Frameworks
7. Importing a Custom Library into CISO Assistant
7.1 Prerequisites
7.2 Importing the Library
7.3 After import: Where to find your Framework
7.4 Common import errors
7.5 Testing your Framework after import
8. Updating and Maintaining Libraries
8.1 Version Management
8.2 URN Stability
8.3 Best Practices
9. Common Pitfalls
10. Final Notes
(Old) Videos
IMPORTANT NOTICE: The following sections contain information that is no longer up to date, but some parts are still useful today. Please consult them with caution.
CIS Controls and the Cloud Security Alliance's Cloud Controls Matrix (CCM) ship as Excel spreadsheets that CISO Assistant can convert and load directly — no command-line preparation required.
CIS and CSA have restrictive licence terms on their content, so the spreadsheets are not bundled with CISO Assistant. You have to download the official spreadsheet from CIS or CSA yourself and then upload it to the platform.
Direct import
Download the CIS Controls or CCM spreadsheet from the relevant authority's website.
In CISO Assistant, go to Libraries and click Add your own library.
Select the downloaded spreadsheet and upload it. CISO Assistant converts it to the platform's library format on the fly.
Once the conversion finishes, load the new library like any other.
If you need to adjust the conversion (custom packager name, modified spreadsheet, additional mappings), the conversion logic is available as standalone Python tools in the repository:
The standard flow is to copy the spreadsheet into the tools folder and run convert_cis.sh (Linux/Mac) or convert_cis.bat (Windows). For finer control, run tools/excel/cis/prep_cis.py first to set a custom packager string, then pass the prepared spreadsheet to convert_library_v2.py. The output YAML can be uploaded as a custom library and loaded.