Ownership and RACI Modeling
A flat property like owner = "team-alpha" on an application is only a label.
To make ownership auditable and queryable, promote the organizational concepts to
first-class graph citizens: party, provider, and role. This guide shows
how to build that graph with standard rescile features.
Core ontology
| Resource | Purpose | Example primary keys |
|---|---|---|
party |
Ultimate legal counterparty | internal-org, External Vendor Inc. |
provider |
Operational team or service offering | team-alpha, external-db-vendor |
role |
Formal organizational function | owner, maintainer, data_processor |
| Relation | Direction | Meaning |
|---|---|---|
HAS_RESPONSIBILITY |
asset → provider | This asset is managed by that provider for a specific role. |
OPERATED_BY |
provider → party | The provider belongs to / is legally covered by that party. |
You can extend the ontology with IS_PERFORMED_BY (role → provider) or any other
relation by adding the corresponding CSV assets and link rules.
Foundation data
Create the organizational resources as ordinary CSV assets.
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
owner,Accountable for the asset
maintainer,Responsible for technical uptime and patching
data_processor,Legal entity processing data on behalf of the controller
data/assets/application.csv
name,environment,owner,maintainer
myapp,production,team-alpha,team-alpha
data/assets/database.csv
name,environment,data_processor
mydb,production,external-db-vendor
Step 1: Link providers to legal parties
A single model rule turns the operated_by foreign key into a graph edge.
data/models/provider_party.toml
origin_resource = "provider"
[[link_resources]]
name = "Provider is operated by legal party"
with = "party"
join = { local = "operated_by", remote = "name" }
create_relation = { type = "OPERATED_BY" }
Graph result: (provider:team-alpha) -[OPERATED_BY]-> (party:internal-org)
Step 2: Map asset responsibility properties to providers
Use one compliance control per role. The template in match_on looks up the
provider whose name equals the role-named property on the asset, and the
relation carries the role as a property.
data/compliance/ownership.toml
[[control]]
id = "OWN-OWNER"
name = "Map owner property to provider"
[control.config]
role = "owner"
[[control.target]]
origin_resource_types = ["application"]
[control.target.resource]
type = "provider"
match_on = [
{ property = "name", value = "{{origin_resource.owner}}" }
]
[control.target.relation]
type = "HAS_RESPONSIBILITY"
properties_from_config = ["role"]
[[control]]
id = "OWN-MAINTAINER"
name = "Map maintainer property to provider"
[control.config]
role = "maintainer"
[[control.target]]
origin_resource_types = ["application"]
[control.target.resource]
type = "provider"
match_on = [
{ property = "name", value = "{{origin_resource.maintainer}}" }
]
[control.target.relation]
type = "HAS_RESPONSIBILITY"
properties_from_config = ["role"]
[[control]]
id = "OWN-DATA-PROCESSOR"
name = "Map data_processor property to provider"
[control.config]
role = "data_processor"
[[control.target]]
origin_resource_types = ["database"]
[control.target.resource]
type = "provider"
match_on = [
{ property = "name", value = "{{origin_resource.data_processor}}" }
]
[control.target.relation]
type = "HAS_RESPONSIBILITY"
properties_from_config = ["role"]
Graph result:
(application:myapp) -[HAS_RESPONSIBILITY {role: "owner"}]-> (provider:team-alpha)
(application:myapp) -[HAS_RESPONSIBILITY {role: "maintainer"}]-> (provider:team-alpha)
(database:mydb) -[HAS_RESPONSIBILITY {role: "data_processor"}]-> (provider:external-db-vendor)
Add more controls as you introduce new roles. All controls use the same standard compliance primitives; there is no special-purpose engine for responsibility.
Step 3: Query the ownership graph
The graph now supports precise queries. For example, list every asset, its responsibilities, and the legal party behind each responsible provider.
query AssetResponsibilities {
vertices(filter: { labels: ["application", "database"] }) {
name
label
HAS_RESPONSIBILITY {
name
service_category
_relation {
role
}
OPERATED_BY {
name
jurisdiction
}
}
}
}
Expected result shape:
{
"data": {
"vertices": [
{
"name": "myapp",
"label": "application",
"HAS_RESPONSIBILITY": [
{
"name": "team-alpha",
"service_category": "Engineering",
"_relation": { "role": "owner" },
"OPERATED_BY": [
{ "name": "internal-org", "jurisdiction": "EU" }
]
},
{
"name": "team-alpha",
"service_category": "Engineering",
"_relation": { "role": "maintainer" },
"OPERATED_BY": [
{ "name": "internal-org", "jurisdiction": "EU" }
]
}
]
}
]
}
}
Variation: explicit responsibility table
If you prefer not to encode responsibilities as scattered properties on each asset, centralize them in a dedicated edge asset.
data/assets/responsibility.csv
source_name,source_type,role,target_name
myapp,application,maintainer,team-alpha
myapp,application,owner,team-alpha
mydb,database,data_processor,external-db-vendor
Then use one model file per source_type to link to the provider:
data/models/application_responsibility.toml
origin_resource = "responsibility"
[[link_resources]]
match_on = [{ property = "source_type", value = "application" }]
with = "application"
join = { local = "source_name", remote = "name" }
create_relation = { type = "HAS_RESPONSIBILITY" }
[[link_resources]]
match_on = [{ property = "source_type", value = "application" }]
with = "provider"
join = { local = "target_name", remote = "name" }
create_relation = { type = "HAS_RESPONSIBILITY", properties = { role = "{{origin_resource.role}}" } }
This keeps all responsibility assignments in one auditable table.
Summary
- Model organizational entities as ordinary CSV assets:
party,provider,role. - Use
[[link_resources]]to materialize foreign-key relationships such asOPERATED_BY. - Use standard compliance targets with
match_ontemplates to createHAS_RESPONSIBILITYedges from asset role properties to providers. - Query through the graph to roll responsibilities up to legal parties.
There is no special-purpose responsibility engine; the pattern is built entirely from rescile’s asset, model, and compliance primitives.