Skip to main content
Version: v5.6.1

The infrastructure pack (packs:infrastructure)

packs:infrastructure

Host-centric remote operation: the infrastructure:* actions run commands, move files and trees, gather facts and close the estate membership loop against registered infrastructure host nodes (see infrastructure nodes) — uniformly across cloud-handed-over, virtualised and plain on-premise hosts. Every action targets a host by host_ref, the host node's path: the dial engine is rpcu (the rpc pack's), and credential custody is the platform's — connection secrets live in transport providers, never in node properties (the registry refuses credential-shaped property keys by name). Everything on this page requires the packs:infrastructure licence entitlement (see entitlements), enforced at every run-creation door through the pack's signed manifest.

The triage line: a host you operate by name is an infrastructure node — reference it with host_ref and use infrastructure:host_*. The rpc pack's rpc:* actions remain the lane for ad-hoc targets carried in an asset's own rpc.* properties; this pack exists to end passing raw user@host strings around a workflow. The host node is the single home for the connection profile, discovered facts and lifecycle state.

The shipped actions​

ActionDoes
infrastructure:host_execRuns a command on the referenced host — streamed logs, typed exit_code/stdout/stderr result; a non-zero remote exit fails the step unless allow_failure: true. Sudo shaping via sudo_user/sudo_method.
infrastructure:host_put / infrastructure:host_getCopies a file or whole tree (recursive: true) between the step workspace and the host (src/dest; put adds mkdirs and an explicit mode — never 0666).
infrastructure:host_syncRsync-ish tree sync (direction: up pushes the workspace tree, down pulls the remote one) with checksum, exclude patterns and the guarded delete/delete_under.
infrastructure:host_factsGathers native host facts over the session and records them on the host node through the facts hand-over (freshness stamped); every fact is also a step output. record: "false" gathers without the node write.
infrastructure:host_registerThe membership hand-over: registers (or updates) the host node's estate membership under a parent path with code, name, address, fqdn and a properties document.
infrastructure:host_deregisterCloses the host node's estate membership — the soft-close posture; re-registering the same code revives the same node.
infrastructure:ensureMakes the pinned rpcu binary available on the runner: PATH, then the runner cache, then a download from an explicit mirror url verified against the pack-pinned sha256. A public URL is never a default; the other actions call it implicitly.

The dialing actions share the connection inputs: host_ref (required), with target, hops and tofu_store defaulting from the host node's own rpc.target, rpc.hops and rpc.tofu_store properties via estate(inputs.host_ref) — an explicit with: value overrides. collect_facts: "true" on any dialing action adds the opt-in facts piggyback: facts over the same admitted session after the primary command, recorded as its own sub-result, never failing the primary. Fan-out over an estate scope rides the filtered estate() enumerate (opschain docs expressions):

- type: change
name: patch_all
for_each: ${{ estate('/projects/acme/environments/prod', "type == 'host'") }}
as: host
target: /projects/acme/environments/prod/assets/fleet
action: infrastructure:host_exec
with:
host_ref: '${{ host.node_path }}'
command: dnf -y update openssl

How a step reaches its host​

A host_ref input re-points the whole transport seam at the referenced host node. At dispatch the engine resolves the reference and refuses every miss by name — an unresolved expression, an unknown node, a non-host node, and a deregistered or destroyed host each fail the step loudly, never silently falling back to the run's target. For an admitted host, the provider governing the host is selected (the host's own rpc.provider_ref property, else the deepest-wins scope binding at the host's path, else the single enabled provider), the caller's transport grant is enforced against the host node's path — the grant covers the host, not the declaring scope — and a one-shot rpcu credentials document is minted into the masked OPSCHAIN_TRANSPORT_SESSION environment variable the wrapper scripts pipe to rpcu --credentials-stdin: never argv, never a file, never node properties. Every admitted session lands the transport session event; grant refusals land their own warn events. The mechanics — providers, bindings, grants, caps — are the rpc pack's transport engine: see transport providers and the rpc pack.

The membership hand-over​

Any action that makes a machine exist — a cloud instance, a virtualised guest, a hand-built box — closes its loop by calling infrastructure:host_register, so the machine becomes estate content: visible in the infrastructure screens, referenceable by host_ref, governed by path RBAC. When the machine leaves the estate, infrastructure:host_deregister closes its membership — the node is soft-closed with full history retained, and registering the same code again revives the same object. The hand-over asserts estate membership only: it never marks a node provisioned or destroyed — those operational states are reserved for the integration that performs the operations. It rides the ordinary opschain infrastructure verbs (opschain docs cli/infrastructure), no separate API surface and no credentials involved.

Entitlement composition​

packs:infrastructure never travels alone:

  • Minted as a bundle. Every licence granting packs:infrastructure is minted with packs:rpc and packs:common alongside, so the transport-provider family and the baseline pack surfaces arrive with it.
  • The gate backstop. A packs:infrastructure grant satisfies every packs:rpc gate, so even a licence minted without the bundle still drives the pack's transport engine. The reverse never holds — packs:rpc alone does not grant the infrastructure pack.
  • The tool grant. Minting a licence granting packs:infrastructure (as with packs:rpc and packs:mintpress) also composes in tools:rpcu, the standalone rpcu binary's own offline entitlement; rpcu's gate accepts tools:rpcu, packs:rpc or packs:infrastructure. At dispatch the engine hands the applied licence envelope by value to exactly the steps whose command references OPSCHAIN_LICENCE_DOCUMENT (the pack wrappers' licence-env channel), and leaves it unset when no applied licence grants rpcu.

Enforcement is the signed-pack funnel: the pack's repository-root manifest declares requires: packs:infrastructure, verified against the product keyring and checked at every door that creates a run using the pack — direct changes, the templated lane including workflow-nested changes, and retries. An unentitled create refuses with the named refusal (pack, entitlement, remedy); see entitlements for the gate idiom.

On licence lapse​

The gate sits at run creation only, so a lapse is polite:

  • Creating a run that uses the pack refuses with the named entitlement refusal; already-created, in-flight runs finish undisturbed (dispatch is deliberately not re-gated). A retry re-gates on the run's frozen pack set, so a lapsed entitlement refuses it.
  • The host-node registry is base platform, not pack-gated: registered hosts, their facts, reads, updates and deregistration all stay available for inventory and lapse cleanup — see infrastructure nodes.
  • Transport providers and grants follow their own polite-lapse posture under packs:rpc (which this pack's grant satisfies): stored records keep serving, only create/enable writes gate.

Where the core documentation lives​

  • The host node kind — attributes, lifecycle, registry semantics: infrastructure nodes.
  • The transport engine — providers, bindings, grants, session minting: transport providers and the rpc pack.
  • Declaring packs in a template and the pack funnel: opschain docs getting-started/2-4-using-packs.
  • The packs:infrastructure entitlement, composition and refusals: entitlements.