Actions Developer Guide

Pluggable Runtimes

Learn about the secure runtimes supported by rescile-runner.

Pluggable, Secure Runtimes

To maintain system security, rescile requires execution isolation using specific runtimes. The engine parameter dictates the isolation boundary:

  1. wasi (WebAssembly): Modules compile logic to .wasm. Executed using wasmtime with zero external dependencies, providing strong isolation and high performance.
  2. nix (Nix Flakes): Spawns an execution boundary wrapped in a nix develop shell for highly reproducible environments without polluting the host OS or requiring pre-installed tooling.
  3. nix-shell: Runs commands inside a nix-shell environment defined by a shell.nix file.
  4. container (Docker/Podman): Spawns an ephemeral container, mounts required outputs, streams logs, and destroys the container when finished. Ideal for legacy tooling like Terraform or Ansible.
  5. http: Securely triggers existing CI/CD pipelines using signed webhook payloads, or executes multi-step REST/GraphQL API chains directly from the core.
  6. api: Executes a multi-step REST/GraphQL API chain ([[runtime.steps]]) directly in the controller core. No runner participates — these actions need no exec array and no runner runtime capability, which makes them the canonical form for integrating external HTTP APIs (vault issuance, OCSP sync, renewal jobs). See HTTP chains with engine = "api" below.
  7. system (Operating System): Executes a command directly on the runner host with the privileges of the rescile-runner process. This engine relies on tools already installed on the runner (for example, ansible-playbook, ssh, or cloud CLIs) and does not provide isolation by itself.

Runner engine support: The rescile-ce runner natively supports system, nix, and nix-shell. Other engines, such as container or wasi, require a runner implementation that explicitly supports them.

HTTP chains with engine = "api"

engine = "api" runs the action’s [[runtime.steps]] chain directly inside the controller process. No runner participates: these actions declare no exec array and require no runner runtime capability, which makes them the canonical form for multi-step HTTP integrations that must work on controller-only deployments (vault certificate issuance, OCSP refreshes, ACME renewals):

# actions/auto_renew.toml
name = "auto_renew"
description = "Renew a certificate through the PKI API"
run_mode = "action"

[[input]]
name = "certificate_name"
type = "string"
required = true

[vault_secrets.pki_token]
collection = "pki"
secret = "api_token"

[runtime]
engine = "api"

[[runtime.steps]]
method = "POST"
url = "https://pki.internal/v1/renew"
headers = { "Authorization" = "Bearer ${secrets.pki_token}" }
body = """
{ "certificate": "{{ input.certificate_name }}" }
"""

Semantics:

  • Step templating uses {{ input.<name> }} (stringified action payload values) and ${secrets.<key>} (vault-mapped secrets resolved as plaintext inside the controller; no age sealing is involved because secrets never leave the controller).
  • Steps execute sequentially; the first non-success response fails the whole chain.
  • REST bodies, GraphQL steps (graphql_query / graphql_variables), and per-step headers behave exactly as with engine = "http".
  • Step logs are written to the action log (same UI as runner logs), and the queue tracks the execution Running -> Completed / Failed like any runner-executed action.
  • Service-mode (run_mode = "service") api actions execute once; they are not duplicated per connected runner.

Use engine = "http" instead when the chain must run on a specific runner (e.g. inside an isolated network where only that runner can reach the API); the step format is identical.

Platform Constraints: Some runtimes depend on the underlying architecture and operating system. The runtime block evaluates declarative platform constraints against the host system, emitting a warning if the action cannot be executed locally.


Execution via rescile-runner

The rescile-runner component is responsible for securely executing the generated action bundles. It is compiled as a standalone binary designed to reduce the blast radius. Depending on your network and security requirements, actions support four distinct modes of execution:

  1. Air-Gapped (Manual): You download the fully packaged artifact (bundle.tar.gz) directly from the Rescile UI. This bundle contains the executable, context data, injected secrets, and a manifest.json. You then manually transfer this bundle to an air-gapped machine and execute it locally using the runner (rescile-ce runner ./bundle.tar.gz).
  2. Webhook (Fetch/Watch): External systems can pull the action payload and execute it independently by invoking the rescile-runner CLI with the Controller API URL. Optionally, the runner can act as a background daemon (rescile-ce runner --watch <URL>) that automatically re-executes when changes are detected via WebSocket subscription.
  3. Agent (Push): Autonomous Agent runners connect outward to the Controller via a persistent gRPC multiplexed stream. When an action is placed in the queue, the Controller instantly pushes the payload to a matching agent, making it ideal for crossing NAT boundaries and executing workloads inside isolated VPCs.