Tutorial Part 6: Governance

Model the full digital supply chain and automate responsibility assignment.


Part 6: Governance & The Complete Digital Supply Chain

Goal: In this section, we will model the full digital supply chain from code to cloud, and—crucially for any enterprise—automate the assignment of ownership and responsibility. We will define the actors in our ecosystem and use a powerful rescile pattern to map responsibilities across all assets, platforms, and services.

Step 22: Defining the Actors - Parties, Providers, and Roles

rescile uses a clear hierarchy for governance modeling:

  • Party: The legal entity (e.g., “Amazon Web Services, Inc.”). This is the ultimate counterparty for contracts.
  • Provider: An operational team or a vendor’s service offering (e.g., aws-cloud, platform-engineering-team). Every provider is operated by a party.
  • Role: A formally defined function or responsibility (e.g., maintainer, owner).

Let’s create the asset files for these entities in data/assets/.

data/assets/party.csv

name,legal_name,jurisdiction
our-company,Our Company Inc.,CH
aws-inc,Amazon Web Services Inc.,US
red-hat-inc,Red Hat Inc.,US
equinix-inc,Equinix Inc.,US
oracle-corp-party,Oracle Corporation,US
canonical-party,Canonical Group Ltd,UK
dell-party,Dell Technologies Inc.,US

data/assets/provider.csv

name,party
platform-engineering-team,our-company
aws-cloud,aws-inc
redhat,red-hat-inc
equinix,equinix-inc
oracle-corp,oracle-corp-party
canonical,canonical-party
dell,dell-party

data/assets/role.csv

name,description
owner,The business owner accountable for the asset.
maintainer,The team responsible for developing or maintaining the asset.
data_processor,The provider responsible for operating a service and processing its data.

Finally, let’s explicitly assign an owner to our applications in application.csv.

data/assets/application.csv (add owner column)

name,type,network_zone,environment,database,supported_runtimes,microservices,repository,container_image,owner
billing-legacy,monolith,backend,production,oracle-db1,"java, tomcat",,,billing-legacy-repo,,platform-engineering-team
order-frontend,container,edge,production,aurora-main,"nodejs","auth-service,payment-service,order-api",order-frontend-repo,order-frontend-img:v1.2.3,platform-engineering-team
image-processor,function,backend,production,,"nodejs, python",,image-processor-repo,,platform-engineering-team
billing-legacy-dev,monolith,backend,development,oracle-db1,"java, tomcat",,,billing-legacy-repo,,platform-engineering-team

Run rescile-ce serve.

Result: The foundational data for a comprehensive governance model is now in the graph. We have nodes for legal entities, operational teams, and roles, but they are not yet connected to the assets they are responsible for.

Step 23: Automating Responsibility with control.target.map

Now for the magic. We will use a single, powerful compliance rule to connect all the provider information we’ve modeled throughout this tutorial. This includes the vendor column on databases, the os_provider and platform_provider properties we added with models, and the owner column we just added to our applications.

The control.target.map directive is designed for this. It scans assets for properties that match role names and automatically creates HAS_RESPONSIBILITY relationships.

Create a new compliance file for this rule:

data/compliance/ownership.toml

audit_id = "GOVERNANCE-POLICY"
audit_name = "Asset Ownership and Responsibility Policy"

[[control]]
id = "OWN-1"
name = "Map Asset Roles to Providers"

[[control.target]]
[control.target.map]
# 1. The type of resources that define the roles (from role.csv).
derived_type = "role"

# 2. The asset types to scan for properties that match role names.
origin_resource_types = [
    "application",
    "database",
    "onprem_vm",
    "dev_server",
    "managed_k8s_cluster",
    "cloud_function_service",
    "onprem_datacenter",
    "cloud_region"
]

# 3. The value of the property will be the name of a 'provider' resource.
target_resource_type = "provider"

# 4. Create this relationship from the asset to the provider.
primary_relation_type = "HAS_RESPONSIBILITY"

# 5. Add a property to the new relationship indicating which role it represents.
property_on_relation = "role"

# 6. Map property names from our source data to the canonical role names.
# For example, if an `onprem_vm` has a property 'os_provider = "redhat"',
# this rule will treat it as 'maintainer = "redhat"'.
[control.target.map.property_map_overrides]
owner = "owner"
vendor = "maintainer"
os_provider = "maintainer"
hardware_provider = "maintainer"
platform_provider = "maintainer"
provider = "maintainer"

Run rescile-ce serve.

Result: A fully mapped and auditable graph of ownership! The rule has scanned all our assets and automatically created HAS_RESPONSIBILITY edges. platform-engineering-team is now linked as the owner of the applications, while aws-cloud is linked as the maintainer of the managed database, Kubernetes platform, and cloud region. redhat and dell are maintainers of the on-prem VM, and so on.

Step 24: Querying the Full Supply Chain

With our governance model in place, we can now ask powerful, cross-domain questions. Let’s write a query to find the full chain of responsibility for our order-frontend application, from our internal team to the cloud vendor’s legal entity.

Run this query in GraphiQL:

query FullSupplyChainTrace {
  application(filter: {name: "order-frontend"}) {
    name
    # Who is responsible for the application code?
    has_responsibility {
      properties { role }
      node { # The 'provider' node
        name
        operated_by {
          node { name legal_name } # The 'party' node
        }
      }
    }
    # Who provides the platform it runs on?
    managed_k8s_cluster {
      node {
        name
        has_responsibility {
          properties { role }
          node { # The 'provider' node
            name
            operated_by {
              node { name legal_name } # The 'party' node
            }
          }
        }
      }
    }
    # Who provides the database it connects to?
    connects_to {
      node {
        name
        has_responsibility {
          properties { role }
          node { # The 'provider' node
            name
            operated_by {
              node { name legal_name } # The 'party' node
            }
          }
        }
      }
    }
  }
}

Result: The query result provides a complete, multi-layered view of accountability. It clearly shows:

  • The platform-engineering-team (operated by our-company) is the owner.
  • The managed_k8s_cluster is maintained by aws-cloud (operated by aws-inc).
  • The aurora-main database is maintained by aws-cloud (operated by aws-inc).

This demonstrates how rescile connects business governance (parties, owners) to technical architecture (applications, platforms) and supply chain (vendors, providers) in a single, unified blueprint.

Step 25: Generating Compliance Artifacts with Reports

Goal: In this final step, we will leverage the fully modeled and governed graph to generate a structured, machine-readable compliance artifact: an OSCAL component definition in JSON format.

rescile includes a powerful reporting engine that runs after all modeling and compliance rules have been applied. Reports query the final state of the graph and use Tera templates to generate new data resources, which can be formatted as JSON, YAML, or any other text-based structure.

Let’s create a report that generates an OSCAL component definition for our order-frontend application.

First, create a reports directory:

mkdir -p data/reports

Now, create the report definition file:

data/reports/oscal_component.toml

origin_resource = "application"

# Define some static metadata for the OSCAL document
oscal_metadata = { oscal_version = "1.1.2" }

[[output]]
resource_type = "oscal_component_definition"
name = "oscal-comp-for-{{ origin_resource.name }}"
match_on = [
  { property = "name", value = "order-frontend" }
]
# The template renders a complete JSON object. The keys and values of this
# object become the properties of the new 'oscal_component_definition' resource.
template = """
{
  "component-definition": {
    "uuid": "fd997558-86f4-413c-8889-299f180f0ed1",
    "metadata": {
      "title": "OSCAL Component Definition for {{ origin_resource.name }}",
      "last-modified": "{{ now() | date(format='%Y-%m-%dT%H:%M:%S%z') }}",
      "version": "{{ origin_resource.container_image[0].name | split(by=':') | last }}",
      "oscal-version": "{{ oscal_metadata.oscal_version }}",
      "parties": [
        {
          "uuid": "e828a167-1425-472d-a22a-43a08272b534",
          "type": "organization",
          "name": "{{ origin_resource.has_responsibility[0].node.operated_by[0].node.legal_name }}"
        }
      ]
    },
    "components": [
      {
        "uuid": "b8f6c568-6f68-4504-8025-4cf53f5a2f1b",
        "type": "software",
        "title": "{{ origin_resource.name }}",
        "description": "The {{ origin_resource.name }} is a {{ origin_resource.type }} application in the {{ origin_resource.network_zone }} zone.",
        "props": [
          { "name": "repository", "value": "{{ origin_resource.repository[0].name }}" },
          { "name": "owner", "value": "{{ origin_resource.has_responsibility[0].node.name }}" }
        ]
      }
    ]
  }
}
"""

Run rescile-ce serve.

Result: The importer now runs a final reporting phase. It found the order-frontend application, executed the template, and created a new oscal_component_definition resource in the graph. The properties of this resource contain the generated JSON object.

Let’s query for it in GraphiQL:

query GetOscalArtifact {
  oscal_component_definition(filter: {name: "oscal-comp-for-order-frontend"}) {
    name
    # This property holds the entire generated JSON object
    component_definition
  }
}

Final Result: The query returns the complete, structured OSCAL document, dynamically generated from our graph data. This demonstrates the full power of the rescile workflow: from raw data to a connected model, to an auditable governance layer, and finally to automated generation of compliance artifacts. Your digital supply chain is now not just modeled, but also machine-readable and ready for automation.