Reference

Schema Validation

Validate module footprints against central, versioned schemas and understand binding, merging, and capability conformance.

Schema Validation

Central schemas describe the shape of your graph: which resource types exist, what properties and relations they must have, and what lifecycle capabilities they expose. Rescile validates every module footprint against these schemas before the graph is built or served.

Schema files use the .schema.json extension and are loaded from local paths, Git URLs, or declared directly in a module’s module.toml.


Schema File Format

A minimal schema file looks like this:

{
  "schema_id": "rescile/infrastructure",
  "version": "20260729",
  "resources": {
    "virtual_machine": {
      "required": ["name"],
      "properties": {
        "name": { "type": "string" },
        "cpu_cores": { "type": "integer" },
        "ram_gb": { "type": "integer" }
      },
      "relations": {
        "network": { "target": "network", "required": true }
      },
      "capabilities": {
        "actions": {
          "provision": { "description": "Create a new instance" },
          "destroy": { "description": "Remove an instance" }
        }
      }
    }
  }
}
  • schema_id / version — uniquely identify the schema.
  • resources.<name>.properties — JSON-Schema-like property definitions (type, enum, required).
  • resources.<name>.relations — relations to other resource types and whether they are required.
  • resources.<name>.capabilities.actions — lifecycle verbs the resource supports (e.g. provision, destroy, start, stop).

Declaring Schemas in module.toml

The simplest declaration loads a single schema for the whole module:

schemas = ["./schemas/infrastructure.schema.json"]

[module]
name = "my-module"
version = "1.0.0"
url = "https://github.com/example/my-module"

You can also use explicit declarations to bind a schema to specific resources:

[[schema]]
schema = "./schemas/infrastructure.schema.json"
resources = ["virtual_machine", "network"]

[[implements]]
schema = "rescile/infrastructure"
version = "20260729"
resources = ["virtual_machine"]

Per-asset overrides take precedence:

[assets."virtual_machines.csv"]
schema = "./schemas/custom-vm.schema.json"

If a resource is created by a module but no schema is declared explicitly, the engine falls back to automatic binding: when exactly one loaded schema defines that resource, it is bound automatically. If more than one schema defines it, the resource remains ambiguous.


CLI Validation

rescile-ce validate

# Use schemas declared in module.toml
rescile-ce --data-dir ./data validate

# Validate against a specific schema only
rescile-ce --data-dir ./data validate --schema ./schemas/20260729.schema.json

# Validate strictly (CSV cell values must satisfy schema types)
rescile-ce --data-dir ./data validate --schema ./schemas/20260729.schema.json --strict-schemas

rescile-ee validate

The service-oriented edition provides the same command with syslog-style output:

rescile-ee --data-dir ./data validate
rescile-ee --data-dir ./data validate --schema ./schemas/20260729.schema.json --strict-schemas

mod check-schema

You can also trigger validation from the module management subcommand:

rescile-ce mod check-schema --schema ./schemas/20260729.schema.json --strict-schemas
rescile-ee mod check-schema --schema ./schemas/20260729.schema.json --strict-schemas

--schema override behavior

When --schema is passed, only the supplied schema source is used. Schemas declared in any module.toml (module-level declarations and per-asset schema = "..." overrides) are ignored for that run.


What is Validated

The engine walks every module footprint and reports:

  • Missing required properties — a required property is not created by any implementing module.
  • Missing required relations — a required relation is not present in the graph.
  • Property type conflicts — when multiple schemas define the same property with incompatible types.
  • Relation conflicts — when multiple schemas define the same relation with different targets or cardinality.
  • Capability coverage — a schema declares a lifecycle verb (e.g. virtual_machine.destroy) but no action offers it.
  • CSV value conformance (only with --strict-schemas) — every non-empty CSV cell must satisfy the property’s schema type.

Validation spans all processing phases

Schema validation does not require every resource to have a backing CSV file. The validator builds a static footprint from every phase of the pipeline:

Phase Properties come from
Asset loading CSV headers in assets/*.csv
Model application create_resource, copy_property, and link_resources rules in models/*.toml
Compliance application target.resource and target.policy blocks in compliance/*.toml
Contracts [[contract.write]] record definitions

A resource created entirely inside a model file — for example, a compute resource produced by a [[create_resource]] rule with no compute.csv — is still checked against the bound schema. A property added to an existing resource during the compliance phase is merged into the same footprint and satisfies the schema requirement.

Properties are accumulated per resource type across every loaded module and every phase, so a missing-property warning is only emitted when no phase in any loaded module provides the required key.

Value checks are limited to raw CSV cells. With --strict-schemas, type and enum validation run against the literal text found in assets/*.csv. Values produced by Tera templates in models, compliance, or output files are not checked at validation time, because the validator performs static analysis without executing templates or action runners. Structural checks — whether the property key exists — are always applied across all phases.

Multiple capability offers are expected

It is perfectly fine for several actions to offer the same schema capability. A typical setup has aws, exoscale, and lxd actions all implementing virtual_machine.start. The validator does not warn about multiple offers; it only warns when a declared capability has no implementation.

Ambiguous schemas only warn on conflict

When a resource is defined by more than one schema and the module does not choose one, the schemas are merged. An ambiguity warning is not emitted unless the merge produces a real conflict, such as incompatible property types or relation definitions. If the schemas complement each other, validation proceeds silently.


Merging Semantics

When multiple schemas are bound to the same resource, their definitions are merged:

  • Required properties — union (a property is required if any schema requires it).
  • Property types — intersection of allowed types.
  • Enum values — intersection of allowed values.
  • Relations — last defined target/cardinality wins; conflicting definitions emit a warning.
  • Capabilities — union of action verbs.

Generating a Schema from a Module

rescile-ce can generate a starter .schema.json from the local data dir module. It inspects CSV assets, models, relations, and action files to infer resource types, properties, relations, and capabilities.

rescile-ce generate-schema --output ./data/my-module.schema.json

It can also generate a schema for specific resources only:

rescile-ce generate-schema "compute,os,dns"

Options:

  • --output <FILE>: write the generated schema to a file. If omitted, prints JSON to stdout.
  • --schema-id <ID>: override the schema id (defaults to the module name).
  • --schema-version <VERSION>: override the schema version (defaults to the module version).

rescile-ee does not provide this command.

What gets inferred

Schema part Source
Resource types Every resource created by local assets or models.
Required properties The primary key name and properties whose CSV column has no empty cells.
Property types Declared [assets.columns] type when available (deprecated); otherwise inferred from CSV values.
Relations Relations created by models.
Capabilities Prefix-matched [capabilities] entries in action files (e.g. virtual_machine.start).

Review the generated schema before publishing it: type inference and required-property heuristics are a starting point, not a guarantee.

Note on [assets.columns] inference: the schema generator still reads legacy [assets.columns] declarations for type hints, but new modules should use central schemas for validation instead.

REST Conformance Endpoint

While rescile-ce or rescile-ee is running, you can request a live conformance report via the REST API:

# Default, non-strict validation
GET /api/validate-workspace

# Include CSV cell value checks
GET /api/validate-workspace?strict=true

The response matches the summary produced by rescile-ce validate:

{
  "error_count": 0,
  "warning_count": 0,
  "info_count": 0,
  "issues": []
}

Each issue has severity, code, and message. Because schema validation performs static analysis of the workspace, it is exposed as an operational REST endpoint rather than through the GraphQL resource API.


Exit Codes

A validation run exits with code 0 if no errors are found. Warnings and info notes do not cause a non-zero exit unless --strict-schemas promotes a warning to an error. A run that encounters one or more errors exits non-zero.