Graph-Native Responsibility Architecture
Handling ownership and responsibility in a hybrid environment goes beyond simple string tags on an asset. A string property like owner = "team-alpha" lacks semantic meaning: is “team-alpha” a cost center, a legal entity, or an engineering squad? Can they be audited?
Transitioning to a Graph-Native Responsibility Architecture turns organizational roles and entities into first-class graph citizens. This enables deep queries, such as finding the exact legal entity acting as a data processor for a specific database instance.
The Core Ontology
Instead of placing flat properties on an application or database, we introduce a dedicated set of organizational resources and relations. These organizational concepts are not hardcoded backend magic — they are defined by simply creating standard CSV asset files in your data directory, just like any other resource type.
Resources
These organizational concepts are defined by simply creating standard CSV asset files in your data directory:
party(data/assets/party.csv): The ultimate legal counterparty (e.g., “Acme Corp”, “Cloud Vendor LLC”). It holds canonical legal and corporate information.provider(data/assets/provider.csv): The service offering or operational team (e.g., “team-alpha”, “aws-europe”). Each provider is linked to a legalparty.role(data/assets/role.csv): A formally defined organizational function (e.g.,owner,maintainer,data_processor).
Relations
HAS_RESPONSIBILITY: Links an infrastructure asset (e.g., Application, Server) to the operationalproviderthat manages it. The relation property{ role: "..." }defines the specific function they perform.IS_PERFORMED_BY: Links the logicalroledefinition to theproviderfulfilling it, establishing a clear RACI matrix.OPERATED_BY: Links the operationalproviderto its legally accountableparty.
Architecture Diagram
graph TD
App[Application] -- "HAS_RESPONSIBILITY<br>{role: maintainer}" --> Prov[Provider: team-alpha]
DB[Database] -- "HAS_RESPONSIBILITY<br>{role: data_processor}" --> ProvExt[Provider: external-db-vendor]
Role1[Role: maintainer] -- IS_PERFORMED_BY --> Prov
Role2[Role: data_processor] -- IS_PERFORMED_BY --> ProvExt
Prov -- OPERATED_BY --> Party[Party: internal-org]
ProvExt -- OPERATED_BY --> PartyExt[Party: External Vendor Inc.]
Implementation Example: Using map
For enterprise-scale ownership and responsibility modeling, rescile provides an advanced, convention-based pattern
in compliance files: [control.target.map].
Instead of writing dozens of rules to link assets to owners, maintainers, and data processors, you can define the convention in a single block.
Example Foundation Data
To make this work, subject matter experts simply provide the organizational hierarchy as standard CSV files:
data/assets/party.csv
name,jurisdiction,type
internal-org,EU,Internal
External Vendor Inc.,US,Vendor
data/assets/provider.csv
name,operated_by,service_category
team-alpha,internal-org,Engineering
external-db-vendor,External Vendor Inc.,Managed Services
data/assets/role.csv
name,description
maintainer,Responsible for technical uptime and patching
data_processor,Legal entity processing data on behalf of the controller
Note how provider.csv naturally creates the OPERATED_BY link to the party via the operated_by column. By showing the raw data first, the connection between vendor = "team-alpha" on an application and the provider.csv table becomes immediately clear.
data/compliance/ownership.toml
[[control]]
id = "OWN-1"
name = "Map Asset Roles to Providers"
[[control.target]]
[control.target.map]
# 1. Find all resources of this type to get the list of possible roles (e.g., "owner", "maintainer").
derived_type = "role"
# 2. Scan these asset types for properties that match the role names.
origin_resource_types = ["application", "database"]
# 3. The value of the property is the name of a resource of this type.
target_resource_type = "provider"
# 4. Create a relation with this type from the asset to the provider.
primary_relation_type = "HAS_RESPONSIBILITY"
# 5. Add a property to the new relation indicating which role it represents.
property_on_relation = "role"
# 6. (Optional) Create a second link from the role definition itself to the provider.
link_relation_to_target = true
secondary_relation_type = "IS_PERFORMED_BY"
# 7. Use overrides to map properties like 'vendor' to the 'maintainer' role.
[control.target.map.property_map_overrides]
vendor = "maintainer"
This single rule scans hundreds of assets. If it finds an application with a property vendor = "team-alpha", it
performs two actions:
- It creates the primary responsibility link:
(application) -[HAS_RESPONSIBILITY {role: "maintainer"}]-> (provider:team-alpha). - Because
link_relation_to_targetistrue, it also creates a secondary link modeling which provider performs the role:(role:maintainer) -[IS_PERFORMED_BY]-> (provider:team-alpha).
graph TD
subgraph "Primary Responsibility Link"
App["application<br>vendor: team-alpha"] -- "HAS_RESPONSIBILITY<br>{role: maintainer}" --> Provider["provider<br><b>team-alpha</b>"]
end
subgraph "Secondary Role Performance Link"
Role["role<br><b>maintainer</b>"] -- "IS_PERFORMED_BY" --> Provider
end
This pattern is the key to managing organizational responsibility at scale.
- The
mapfeature dynamically reads the graph (viaderived_type = "role"). If a user adds a new role (e.g.,data_steward) torole.csv, the graph automatically maps thedata_stewardproperty on all assets to the respective provider. - The specialized map performs an
M × Nmapping (Asset Types × Role Types) in one block.
Creating an Ownership Report
Once the graph models these responsibilities explicitly, you can automatically generate comprehensive ownership and compliance reports. By using Rescile’s output templating, you traverse the HAS_RESPONSIBILITY edges to extract the provider and roll up to the legal party.
data/output/ownership_report.toml
origin_resource = "application"
[[output]]
resource_type = "ownership_report"
name = "report-{{ origin_resource.name }}"
filename = "ownership-{{ origin_resource.name }}.md"
mimetype = "text/markdown"
template = '''
# Responsibility Report: {{ origin_resource.name }}
## Asset Details
* **Type:** Application
* **Environment:** {{ origin_resource.environment | default(value="Unknown") }}
## Responsibilities
{% for provider in origin_resource.HAS_RESPONSIBILITY | default(value=[]) %}
### Role: {{ provider._relation.role | capitalize }}
* **Operational Provider:** {{ provider.name }}
* **Legal Entity:** {{ provider.OPERATED_BY[0].name | default(value="Internal") }}
* **Service Category:** {{ provider.service_category | default(value="N/A") }}
{% else %}
*No responsibilities mapped.*
{% endfor %}
'''