Skip to main content
Version: Current

Building a Backend

A CRADLE backend translates resolved Deployment IR into files for a target testbed. It remains separate from the CRADLE language and compiler, so adding a target does not require changing scenario syntax or rebuilding CradleXC.

important

Treat the JSON produced by cxc dump-ir as the backend's source data. The worked translator on this page starts from that JSON, validates its references and writes target files without reparsing the .cradle scenario.

Backend executables follow this naming convention:

cxc-backend-<name>

For example, cxc-backend-example is selected with --target example.

Understand the boundary​

CradleXC handles parsing, validation and backend-neutral resolution. Backend code receives the resulting Deployment IR and translates it into the files required by one target.

CRADLE source
↓
CradleXC validation and lowering
↓
Deployment IR
↓
Backend translation
↓
Target-specific files

Before implementing a target translator, read Hello World: Deployment IR. It contains a complete generated IR example and explains every structure a backend consumes.

Start with an IR fixture​

Capture Deployment IR from a small, known scenario:

cxc dump-ir -i scenarios/HelloWorld.cradle > fixture.ir.json

Keep this file with the backend's tests. It provides a stable compiler output that can be used to develop and verify translation logic independently from the scenario parser.

Add more fixtures as backend support grows. Useful cases include:

  • multiple networks
  • DHCP and static endpoints
  • Linux and Windows instances
  • routers and computed routes
  • local and remote objects
  • ordered events with dependencies
  • roles and resolved role variables

Translate the IR, not the source​

Backend code should not parse .cradle files. CradleXC has already validated and resolved the scenario into a deployment-ready structure.

At a minimum, translation logic normally reads:

IR structureTypical backend use
project_nameName the target project and generated resources.
instancesDefine machines, operating systems, resources and provisioning requirements.
networksDefine subnets, gateways and target-side network resources.
interfacesAttach each machine to its resolved networks and addresses.
routesConfigure static routing where required.
objectsLocate artifacts required by instances and events.
event arraysProduce ordered event-execution definitions.
boot_orderPreserve router-first machine ordering.

Use resolved values exactly as supplied. In particular, do not allocate new addresses for endpoints that were written as DHCP in source, and do not recalculate router or Windows status from configuration names.

Map the Hello World IR into a target plan​

Start by deciding what files your testbed tooling needs. A small backend can translate the supplied HelloWorld IR into this target-neutral layout:

output/
├── project.json
├── networks.json
├── instances.json
├── events.json
├── artifacts.json
└── boot-order.txt

The example IR maps into those files as follows:

IR valueTranslation decision
project_name: "HelloWorld"Use it as the target project name.
Network lan_0Create one network plan for 192.168.56.0/24 with gateway 192.168.56.1.
Endpoint win7, 192.168.56.121Attach win7 to lan_0 at that exact address.
Endpoint router, 192.168.56.122Attach router to lan_0 at that exact address.
Instance resourcesRequest the supplied CPU and memory values from the target.
Instance configsMap each identifier to a target-supported image step, role or setup action. Reject an identifier that the backend cannot implement.
Object HelloWorldMake the resolved remote artifact available to the router instance using ttp and filename as destination components.
main_eventsPreserve the supplied order and execution metadata. Do not invent an action when both subject and run_object are null.
boot_order: ["router", "win7"]Start the router before the Windows instance.

Empty arrays and null values are meaningful. An empty routes array means that the backend must not invent extra static routes. Empty lifecycle phases mean that there is nothing to schedule in those phases.

Adapt the writers to your testbed​

The example separates CRADLE semantics from target syntax. Keep the validation and indexing logic, then replace each output with the equivalent accepted by your environment:

Generated modelReplace with your target's format
networks.jsonNetwork definitions, virtual switches, subnets or VLAN configuration.
instances.jsonMachine definitions, image identifiers, resource sizes, interface attachments and setup steps.
events.jsonScheduled jobs, playbooks or testbed event instructions.
artifacts.jsonArtifact download/copy instructions and destination paths.
boot-order.txtDependency edges or ordered startup instructions.

Keep provider credentials and site-specific identifiers outside generated files where possible. Accept them through the backend's own configuration, resolve them at runtime, and never copy secrets into logs or fixtures.

After generation, the user reviews the files and applies them with the tools approved for their own testbed. CradleXC supplies the resolved plan; the backend owns the target syntax; the testbed tooling owns infrastructure and runtime execution.

Connect the translator to CradleXC​

Keep the translator independent from the executable integration layer. The integration layer for cxc-backend-<name> should be thin:

  1. receive the IR file and requested output directory from CradleXC
  2. decode backend-specific settings into a typed configuration
  3. call load_ir() and the target-specific equivalent of translate()
  4. write only beneath the requested output directory
  5. return success only after every required file has been written
  6. report unsupported IR values and target capabilities as clear errors

Use the backend API from the matching CradleXC release for this integration layer rather than duplicating its process-handling logic. Keep all target decisions inside the translator so it can still be run and tested directly with a captured dump-ir fixture.

Name the installed executable after the target. For a target named lab, the executable is:

cxc-backend-lab

Place it on PATH or in ~/.cxc/plugins/, make it executable, and verify that CradleXC discovers it before testing file generation.

Handle IR compatibility​

The top-level ir_version identifies the Deployment IR contract. Backend code should:

  1. read and validate the version before translating the rest of the document
  2. reject versions it cannot safely understand with an actionable error
  3. accept unknown object keys so additive fields do not break older code
  4. avoid depending on JSON key order or whitespace
  5. keep fixtures for every supported IR version

Do not compare raw JSON text. Deserialize it into typed structures or access fields by name.

Generate deterministic output​

Given the same IR and backend configuration, target-file generation should produce the same result. Deterministic output makes reviews, tests and troubleshooting much easier.

Recommended practices include:

  • sort generated maps when the target format does not preserve order
  • preserve IR ordering where it carries meaning, especially events and boot_order
  • use provider_name for target-side network identifiers
  • keep backend-specific image and provider mappings in backend code
  • avoid embedding machine-local temporary paths in generated files
  • fail clearly when a required target capability is unsupported

Resolve objects safely​

Instance and event structures refer to objects by id. Resolve those IDs against the top-level objects array.

Object sources use one of these shapes:

{"Remote": "https://172.18.178.10:4443/TTP/HelloWorld/artifact/HelloWorld.sh"}
{"Local": "/absolute/path/file.sh"}

Treat ttp and filename as resolved destination components. Do not parse the original repository placeholder from CRADLE source again.

Verify discovery and generation​

Once the backend package has been installed, confirm that CradleXC can discover it:

cxc plugin list

Then test the public target-file workflow:

cxc build \
-i scenarios/HelloWorld.cradle \
--target example \
-o ./output

Inspect the generated directory and compare it with the expected golden files. Repeat the check with each supported IR fixture and target configuration.

Backend checklist​

Before publishing a backend:

  1. document the supported CradleXC and IR versions
  2. document supported operating systems, configurations and scenario features
  3. validate required external dependencies early
  4. test local and remote artifact handling
  5. test mixed static and DHCP-originated endpoints
  6. preserve lifecycle event and boot ordering
  7. reject unsupported features with clear diagnostics
  8. keep target-specific state and generated files inside the requested output location
  9. test failure paths as well as successful generation
  10. publish compatibility notes alongside each backend release

Use CradleXC's Deployment IR types and backend API as the implementation authority. Keep the public backend documentation focused on supported inputs, generated outputs and target compatibility.