The security graph
What the graph holds, how each scan updates it, and what the explorer and the Schema page let you do.
This page describes the graph in which Ctadel stores everything it reads, the way each scan updates it, and the two pages that let you look at it: the explorer and the schema.
What the graph holds
The graph is a database of typed nodes linked by typed relationships. A node stands for a resource, a finding or a scope object; a relationship stands for a link, observed or computed, between two nodes. Ctadel keeps the list of these types; it is this list that the Schema page displays and that the explorer offers.
Node families
| Family | Example types |
|---|---|
| Infrastructure | Instance, BaremetalServer, Volume, Snapshot, MachineImage, FlexibleIP, MessageQueue, BackupVault |
| Network | VPC, PrivateNetwork, Subnet, SecurityGroup, SecurityGroupRule, PublicGateway, LoadBalancer, LoadBalancerBackend, DNSZone, DNSRecord, CDN, APIGateway, PrivateEndpoint, VpcPeering |
| Storage and data | Bucket, Database, FileStorage, BigQueryDataset, BigQueryTable, AthenaWorkgroup, DataFactory, DataprocCluster, EMRCluster |
| Kubernetes and containers | KubernetesCluster, NodePool, KubernetesNode, Namespace, KubernetesWorkload, K8sService, RBACBinding, PersistentVolumeClaim, ContainerImage, ContainerRegistry, RegistryImage, ContainerService |
| Serverless and applications | ServerlessFunction, ServerlessContainer, WebApp, WorkflowLogicApp |
| Identities and access | User, IamApplication, IamGroup, IamRole, IamPolicy, IAMStatement, APIKey, ServicePrincipal, RoleAssignment, Account, AzureTenant, GCPOrganization, GCPProject, CognitoUserPool |
| Security | Secret, EncryptionKey, Certificate, CloudCertificate, AuditLog, WAF, MonitoringConfig, SecurityService |
| AI | CognitiveAccount |
| Observed permissions | ActionUsage, PrivilegeRecommendation |
| Events | CloudEvent |
| Findings | SecurityFinding, CVE, SecretFinding, Data, HcrFinding, IacFinding, AttackPath |
| Source code | Repository, IacResource, CIService |
| Scope | Project |
Every cloud resource carries the same base properties: id, name, state, cloud, project_id, region, created_at, tags. Resources with a network presence also carry public_ips, private_ips, vpc_id and security_group_ids. Each type then adds its own properties, and Ctadel normalises some of them across clouds so that a rule can target a single property name whatever the provider.
The deep reads (disks, buckets, databases, clusters, repositories) write extra properties on the resources they read: has_sensitive_data and data_classification on what holds data, has_secrets on what holds a secret, has_sbom and package_count on what has a known package inventory, os_family on a host whose disk was read, has_hcr_findings on a host whose configuration was reviewed.
Relationship families
| Family | Example relationships |
|---|---|
| Structure | BELONGS_TO_PROJECT, HAS_VOLUME, HAS_SUBNET, HAS_RECORD, HAS_RULE, HAS_NODEPOOL, HAS_IMAGE, IN_NETWORK, IN_VPC, IN_NAMESPACE, ATTACHED_TO, RUNS_ON, USES_IMAGE, USES_VOLUME, USES_PVC |
| Exposure and routing | ROUTES_TO, RESOLVES_TO, LOAD_BALANCES, HAS_BACKEND, PROTECTED_BY |
| Identities and access | CAN_ACCESS, HAS_ACCESS_TO, GRANTS, HAS_POLICY, MEMBER_OF, HAS_API_KEY, HAS_ROLE_ASSIGNMENT, USES_SA, SAME_IDENTITY, CLUSTER_ADMIN_OF, BOUND_TO, BINDING_IN, PERFORMED_ACTION, HAS_RECOMMENDATION |
| Data and encryption | CONTAINS_DATA, ENCRYPTED_WITH, STORED_IN, WRITES_TO, LOGS_TO, REPLICATES_TO, MIRRORS |
| Findings | HAS_FINDING, HAS_CVE, HAS_VULNERABILITY, HAS_SECRET, HAS_HCR_FINDING, HAS_IAC_FINDING, PART_OF_ATTACK_PATH, ATTACK_STEP, TRIGGERED |
| Code to cloud | DEPLOYED_AS, PROVISIONED_BY, CREATED_FROM, USES_TOOL, MANAGES, PART_OF |
The CAN_ACCESS relationship is not read as such from a cloud: Ctadel computes it from the policies, roles and IAM bindings it has read, to express that an identity can reach a resource. It is the relationship most attack paths follow.
How each scan updates it
At each inventory, Ctadel updates the graph with what it has just read: a resource already known is updated, a new resource is added, and their relationships are rewritten. No history is kept in the graph itself; the history of findings is kept separately.
Resources that have disappeared are then cleaned up, with three precautions.
- The cleanup covers one type, in one account (a cloud and project pair): the nodes of that type and account that are missing from the received inventory are deleted, with their relationships.
- Only the types that were read successfully are cleaned. If reading a type failed, its nodes are kept as they are until the next successful read.
- Refreshing a single resource cleans nothing.
Child nodes (DNS records, subnets, certificates, node pools, registry images, API keys, security group rules, load balancer backends, IAM policy statements) follow the same principle under their parent: update, then deletion of those not seen again.
The deep reads then write their results on top of the inventory: Data nodes and classification properties for sensitive data, SecretFinding and HAS_SECRET for secrets, CVE and HAS_CVE for vulnerabilities, SecurityFinding and HAS_FINDING for configuration findings, HcrFinding for the host review, IacFinding and DEPLOYED_AS for infrastructure code, AttackPath and PART_OF_ATTACK_PATH for attack paths.
The explorer
The "Explorer" page (titled "Security Graph") lets you query the graph without writing a query.

A query is built in three steps.
- "Find": the starting node type, chosen from the list of types.
- "Where": filters on its properties. The available operators are equals, not equals, contains, not contains, starts with, ends with, greater than, less than, is empty, is not empty and in a list; filters combine with AND or OR.
- "That": connected entities, by relationship, with their own filters and sub-branches.
The "Templates" button opens a library of ready-made queries in six categories: exposure and attack surface, identity and access, encryption and secrets, Kubernetes and containers, data security, attack paths. Each template carries an indicative severity and loads into the builder, where you can change it.
The "Ask AI" bar takes a sentence ("public instances with open SSH") and turns it into a query in the builder; a second sentence refines the previous one.
Results are shown in "Graph" or "Table" view, with CSV export. They respect the scope selected in the header: a user limited to certain projects reads only their part of the graph, and a user with no project reads nothing.
A useful query can become a rule: "Save as CSPM Policy" converts it into a custom policy, evaluated at every scan like the catalogue rules.
The Schema page
The user menu in the header offers "Security Graph", which opens the "Security Graph Schema" page. It shows the list of types: node types by category, their properties with their type, the incoming and outgoing relationships of each type, and the number of instances of each type in your graph. If the graph is empty, the page invites you to run a scan.
Limits
- Some relationships are not modelled, and the catalogue chains that would need them are disabled:
CAN_ACCESSalways ends on a resource, never on a role; no relationship links a cloud identity to a Git repository; Kubernetes workload identity (IRSA, pod identity, federation) is not modelled; no relationship links a load balancer to the Kubernetes workload behind it; aVpcPeeringnode is linked to nothing. - A managed database is not scanned for vulnerabilities, and a machine image is not scanned for secrets.
- Cognito accounts are read as user pools; identity pools, which carry the roles, are not collected.
- A few properties shown on the Schema page are never populated:
nameon aCVE(the package name sits on theHAS_CVErelationship),image_nameon aContainerImage,stateandlast_usedon anAPIKey. - A type whose collection failed keeps its previous nodes: for that type, the graph may show the state of the last successful read.
- The explorer limits a query to 10 chained relationships, 100 results by default and 500 at most, and 15 seconds of execution.
- Query template names and their categories are available in English only.
- The instance counts on the Schema page are recomputed at most once an hour.