Core Concepts

Intent vs. Reality

Two patterns for modeling intention and reality in the rescile graph — dedicated resources versus relation properties on the intent resource.

Intent vs. Reality

Every cloud orchestration task has two sides:

  • Intent — the generic architectural role a resource plays (“I need a compute instance called web-01 with 4 vCPUs and 16 GiB RAM”).
  • Reality — the concrete, provider-specific object that satisfies that intent (“an Exoscale medium instance with template ubuntu-22.04 and provider id i-8f7a…”).

Rescile supports two complementary ways to model that relationship in the graph.


Approach A: Specific resources (entity-centric)

The physical cloud object is a first-class resource. The generic intent resource links to a dedicated provider-specific resource through a relationship such as DEPLOYED_AS.

[compute: web-01] --(DEPLOYED_AS)--> [exoscale_instance: web-01-exo]

This is the right choice when:

  • You want a clear boundary between intent and physical state.
  • You need to attach provider-specific properties, lifecycle actions, and compliance rules to the concrete resource.
  • You want one intent fanning out to multiple concrete targets (multi-cloud HA, canary deployments).

Schema inheritance makes this pattern maintainable. A generic compute schema declares shared properties; provider schemas such as exoscale_instance or aws_instance declare implements = ["compute"] and inherit them.

{
  "resources": {
    "compute": {
      "properties": { "vcpus": { "type": "integer" }, "ram_gb": { "type": "integer" } }
    },
    "exoscale_instance": {
      "implements": ["compute"],
      "properties": { "instance_type": { "type": "string" } }
    }
  }
}

Approach B: Relation properties (relation-centric)

The graph keeps only the generic intent resource. Provider-specific configuration is stored directly on the relation that connects the intent to the provider target.

[compute: web-01] --(DEPLOYED_AS { instance_type: "medium", encrypted: true })--> [provider: exoscale]

This works best when:

  • The architecture itself is the source of truth.
  • You want a compact graph without a resource per provider object.
  • Provider modules can inject configuration without declaring new resource schemas.

Central schemas can describe relation properties through property_schema, and validation will enforce required relation properties as well as type-check their values under --strict-schemas.

{
  "resources": {
    "compute": {
      "relations": {
        "DEPLOYED_AS": {
          "target": "provider",
          "property_schema": {
            "properties": {
              "instance_type": { "type": "string" },
              "encrypted": { "type": "boolean" }
            },
            "required": ["instance_type"]
          }
        }
      }
    }
  }
}

Choosing between the approaches

The approaches are not mutually exclusive. A workspace can use resources for long-lived cloud objects and relation properties for lightweight provider bindings.

Concern Approach A Approach B
Graph growth Adds a resource per concrete object Minimal graph
Schema surface One schema per provider/object type Schemas describe only generic types
Multi-target deployments One intent → many concrete resources One intent → many relations
Physical state history Replace or historicize concrete resources Couples intent and deployment state
Query ergonomics Union across provider types Read generic resource, inspect relations

The schema engine supports both: inheritance and merging for Approach A, typed relation property schemas and relation-targeted compliance for Approach B.