Skip to main content
Version: v5.7.0

The rpc pack (packs:rpc)

packs:rpc

The packs:rpc entitlement delivers two things as one grant: the rpc YAML action pack — remote execution and file transfer over SSH, driven by rpcu, the standalone LimePoint transport tool — and the transport-provider family those actions broker remote-host access through (providers, grants and scope bindings). One entitlement natively grants both; there is no separate feature flag, and bundles compose it in at licence minting — packs:mintpress today, and every licence granting packs:infrastructure is minted with packs:rpc alongside. See entitlements for the gate's exact refusal shapes, and transport providers for the brokering machinery itself.

The pack mounts like any other action pack — a packs: entry in the template's .opschain/packs.yaml naming the pack remote and a pinned revision (opschain docs getting-started/2-4-using-packs) — and its signed manifest requires packs:rpc, so an unentitled mount refuses with the named pack refusal before any action runs.

The shipped actions​

Every action is an ordinary YAML action invoked as rpc:<name>, so it composes with uses:, workflows and triggers like any other. Family by family:

Execution

ActionPurpose
rpc:execRun a command over SSH — streamed logs; exit_code, stdout/stderr tails and duration_ms published as step outputs. Non-zero exit fails the step unless allow_failure: true. Key inputs: command (required), target, sudo_user/sudo_method, hops (OpenSSH -J-form jump chain), timeout, capture.
rpc:exec_viarpc:exec with the jump chain required — auditable bastion use.
rpc:factsNative host facts over SSH (no remote agent), every fact a step output.

File transfer

ActionPurpose
rpc:put / rpc:getCopy a file or whole tree (recursive: true) between the step workspace and the target — modes preserved, never 0666.
rpc:syncrsync-ish tree synchronisation with a checksum level and the guarded delete/delete_under; a sync report is published.

Surgical remote edits — all three are idempotent by construction (a re-run reports changed: false and writes nothing), write atomically with a timestamped backup published as the backup output, and record counts only, never file contents:

ActionPurpose
rpc:file_editConfig edits on the target in three families — key=value, line-in-file (line_ensure/line_match/line_absent), ini-section (ini_section/ini_set/ini_rm). For shared, distro-owned files where one key must change and hand edits must survive; a platform-owned file is rendered whole and rpc:put instead.
rpc:zip_editTokenise members inside a remote zip archive (open-edit-rewrite, no target-side zip tooling); members matching include/exclude globs get tokens (TOKEN=VALUE pairs) replaced.
rpc:tokeniseTokenise a remote directory tree (sync-down, transform, sync-up) — only changed files re-upload.

Protocol clients

ActionPurpose
rpc:sqlOracle SQL through the session — go-ora over TNS by default (third-host listeners via the jump machinery), or via: sqlplus on the target for bequeath/OS-auth connects and sqlplus scripts. A missing sqlplus is refused by name, never a raw command-not-found.
rpc:httpHTTP client through the session — cookie jar, selections, form round trips, PUT uploads from a runner-local file, per-client TLS posture; console-only endpoints via the jump machinery.

Tooling

ActionPurpose
rpc:ensureMakes the pinned rpcu binary available on the runner: PATH (image-baked) → the runner cache → a verified download from rpc.binary_url, checked against the pack's pinned sha256 before use. A public URL is never a default.

Two input conventions run through the whole catalog:

  • The connection profile. Every action's connection inputs default to the target node's rpc.* properties — rpc.target, rpc.hops, rpc.tofu_store, rpc.binary_url, rpc.connect_timeout, rpc.keepalive — so a workflow step can be just with: { command: "…" }; an explicit with: value overrides the profile. Host keys are trust-on-first-use into the property-backed rpc.tofu_store (first contact logged loudly, a changed key refused); known_hosts selects a strict policy instead.
  • Secret-valued inputs (db_password, password, token, secret_value) are vault-resolved secret inputs handed to rpcu by env-var name — never argv, masked in logs, absent from the step record.

How actions reach remote hosts​

Credentials never live in node properties. An rpc action's wrapper reads a one-shot session from the masked OPSCHAIN_TRANSPORT_SESSION environment variable and pipes it to rpcu --credentials-stdin: at dispatch the engine resolves the transport provider governing the step's target, enforces the run principal's transport grant (a miss fails the step by name), resolves the provider's credential reference with platform authority and mints the session bundle — the resolved key or password for an ssh-static provider, a short-lived CA-signed certificate plus ephemeral key for ssh-ca. The whole brokering model — provider kinds, grants, scope bindings, the dispatch precedence and the events it lands — is transport providers; the runner-side environment contract is on opschain docs runner-executors. Run standalone outside the platform, the wrappers fall back to the ambient RPCU_KEY_DATA / RPCU_PASSWORD / RPCU_SUDO_PASSWORD channel.

The tools:rpcu composition and the licence hand-off​

The rpcu binary is itself licence-gated, offline: its user verbs refuse by name without a LimePoint-signed licence document granting tools:rpcu — and packs:rpc or packs:infrastructure satisfy the same gate. Minting a licence that grants packs:rpc, packs:infrastructure or packs:mintpress composes tools:rpcu into the document alongside, so the tool's own gate holds whichever spelling the estate's licence carries.

The platform hands the licence to the tool the same way it hands the session: a step whose command references OPSCHAIN_LICENCE_DOCUMENT gets the estate's applied licence envelope by value in that variable — picked as the latest-expiring applied licence granting tools:rpcu, packs:rpc or packs:infrastructure, masked out of logs like any minted secret. The wrappers materialise it into a private temp file for rpcu's offline dial and remove it on exit; the document is never embedded in the pack. When no applied licence grants the tool, the variable is simply not set — the ambient RPCU_LICENCE fallback stays in charge and rpcu's own gate streams its named refusal to the step log. A local (--local) run makes the same hand-off from the licence files on disk.

Lapse behaviour​

The polite-lapse posture, uniformly: on entitlement lapse, creating a transport provider or enabling a disabled one, creating or directly approving a transport grant, and declaring a scope binding each refuse with the typed entitlement refusal naming the pack — while stored providers, grants and bindings keep serving dispatch, reads and the test verb stay up, and disabling, deleting, rejecting, revoking and binding deletes stay open so a lapsed estate can always unwind authority. Running work is never stopped by the pack gate alone.

Composition with other packs​

  • packs:infrastructure satisfies every packs:rpc gate — it bundles the rpc engine, every licence granting it is minted with packs:rpc alongside, and the gate itself backstops a licence minted without the bundle. The reverse never holds: packs:rpc alone grants neither the infrastructure pack's host-targeted dispatch (host_ref re-pointing at registered infrastructure host nodes) nor its provision hand-over.
  • packs:mintpress bundles packs:rpc at licence minting, so MintPress customers drive the rpc actions and the transport family under their existing grant.

Where the rest lives​

  • Transport providers, grants and bindings — the brokering component this pack's actions ride.
  • Entitlements — the gate vocabulary and refusal shapes.
  • opschain docs cli/admin-transport-providers and opschain docs cli/admin-transport-grants — the admin CLI families.
  • opschain docs getting-started/2-4-using-packs — mounting a pack onto a template.
  • opschain docs runner-executors — the step environment contract (OPSCHAIN_TRANSPORT_SESSION, OPSCHAIN_LICENCE_DOCUMENT).