Compliance as Code
Automatically Enforcing Governance and Security Policies
3. Compliance Engine: Enforcing Policy on Your Graph
After rescile builds the foundational graph from your assets and models, it enters the compliance phase. In this phase, the Compliance Engine processes declarative rules from TOML files in data/compliance/ to automatically mutate the graph, ensuring it adheres to your organization’s security and governance policies.
This “compliance-as-code” approach makes your security posture auditable, version-controlled, and consistently applied across your entire hybrid estate.
Core Structure of a Compliance File
A compliance file defines an audit_id for the framework it represents and contains one or more [[control]] blocks. Each control has an id, a name, and a [control.config] table to store parameters. The [[control.target]] blocks define what to change in the graph.
data/compliance/bafin.toml
audit_id = "BAFIN-VAIT"
audit_name = "Supervisory Requirements for IT in Financial Institutions"
[[control]]
id = "VAIT-7.2"
name = "Database Encryption in Transit"
# A key-value store for control parameters.
[control.config]
min_tls_version = "1.2"
status = "mandatory"
# Defines where and how to apply the control.
[[control.target]]
relation_origin_type = "application"
relation_target_type = "database"
properties_from_config = ["min_tls_version", "status"]
Common Mutation Patterns
1. Enriching Connections
Use Case: Add security requirements or metadata to existing relationships. This pattern finds all relations between two resource types and adds properties to them from the [control.config] block.
- Result: Every relation between an
applicationanddatabasenow has acontrolsproperty containing an object with the control’s details and the specified config values.
Enriched with:
controls: [{ control_id: 'VAIT-7.2', ... }]" --> DB[database]
2. Attaching New Control Resources
Use Case: Model a policy that applies to a resource, such as an MFA requirement. This pattern finds matching resources, creates a new resource representing the policy, and links it to the original resource.
[[control]]
id = "SEC-MFA-01"
name = "MFA for Privileged Identities"
[[control.target]]
origin_resource_type = "identity"
match_on = [ { property = "privileged", value = "true" } ]
[control.target.resource]
type = "security_control"
name = "mfa_for_{{origin_resource.name}}"
[control.target.resource.properties] # Define properties directly
mfa_required = true
mfa_types = ["TOTP", "FIDO2"]
[control.target.relation]
type = "APPLIES_TO"
- Result: A privileged
identitygets a newsecurity_controlresource linked to it.
name: admin
privileged: true] end subgraph After I2[identity
name: admin
privileged: true] -->|APPLIES_TO| SC[security_control
mfa_required: true] end
Advanced: Linking the New Resource to Others
In more complex scenarios, the new control resource you create might itself need to be linked to other existing parts of your architecture. You can achieve this by adding a [[control.target.resource_links]] block.
Use Case: An internal application must have a specific firewall rule. The control should create the firewall_rule resource and then link that new rule to the existing network_zone it applies to.
[[control]]
id = "NET-SEG-01"
name = "Segment Internal Apps"
[[control.target]]
# 1. Find the internal application.
origin_resource_type = "application"
match_on = [ { property = "environment", value = "internal" } ]
# 2. Define the new 'firewall_rule' resource to attach.
[control.target.resource]
type = "firewall_rule"
name = "rule_for_{{origin_resource.name}}"
[control.target.resource.properties]
action = "allow"
# 3. Link the application to the new rule.
[control.target.relation]
type = "HAS_RULE"
# 4. Add a secondary link from the new rule to an existing network zone.
[[control.target.resource_links]]
[control.target.resource_links.relation]
type = "APPLIES_TO_ZONE"
[control.target.resource_links.resource]
type = "network_zone"
match_on = [ { property = "name", value = "internal-zone" } ]
This pattern allows you to insert new control artifacts into your graph and immediately connect them to the relevant architectural components in a single, declarative rule.
3. Linking Existing Resources
Use Case: Create a new relationship between existing resources to enforce a policy, such as forcing all internet-facing applications to be protected by a central Web Application Firewall (WAF).
[[control]]
id = "SEC-WAF-01"
name = "WAF for Edge Applications"
[control.config]
protection_level = "standard"
[[control.target]]
# 1. Find all origin applications on the 'edge' network.
origin_resource_type = "application"
match_on = [ { property = "network", value = "edge" } ]
# 2. Find the single existing target resource to link to.
[control.target.resource]
type = "gateway"
match_on = [ { property = "name", value = "waf-edge-gw" } ]
# 3. Define the new relation to create between them.
[control.target.relation]
type = "PROTECTED_BY"
- Result: Creates a
PROTECTED_BYrelationship from every application on theedgenetwork to thewaf-edge-gwgateway.
4. Convention-Based Responsibility Mapping ([control.target.map])
Use Case: Model ownership and responsibility across the entire graph with a single, declarative rule. Instead of writing dozens of individual rules, this advanced pattern discovers roles from the graph itself (e.g., from a role.csv file) and automatically creates the appropriate HAS_RESPONSIBILITY relationships between assets and providers.
This powerful pattern, which is key to managing organizational responsibility at scale, is covered in detail in the Advanced Modeling documentation.
Verifying Compliance with GraphQL
After the importer runs, you can use GraphQL to audit your compliance posture. To check if the database encryption control was applied to billing-api, you can query its database relation:
query VerifyComplianceEnrichment {
application(filter: {name: "billing-api"}) {
name
database {
properties {
relation
controls {
control_id
min_tls_version
status
}
}
node { name }
}
}
}