Skip to content

Portal v0.37.0 — Self-rotating runner credentials and a harder execution boundary

Choose a tag to compare

@AndrewDryga AndrewDryga released this 08 Aug 10:28
· 746 commits to main since this release
Immutable release. Only release title and notes can be modified.
v0.37.0
ceea9e7

A runner token now carries a 90-day life and refreshes itself two thirds of the way through, over the connection it already holds. Rotating a fleet credential no longer means going back to the host, and a token presented after its expiry is refused at connect. The execution boundary tightened to match.

Security

  • Every runner token has a bounded life, including the ones minted before rotation existed. The refresh happens over the existing connection with no host access, and an expired token is refused.

  • Action children start with no_new_privs, so an execve inside a pack cannot pick up setuid or file-capability privileges the runner does not already hold.

    Upgrade note. This applies to the whole process tree, so a setuid or setgid helper no longer elevates. If a non-root runner reached a resource through one, give it direct access instead. The concrete case in our own catalog: postqueue is setgid postdrop, which is how mailq reads the Postfix queue — that runner user now needs to own the queue or be a postdrop member, which is what the pack's notes already required.

  • An action argument that resolves into the runner's own credential or state directory is refused before it reaches a shell, and one symlink-containment walk now covers every path the runner opens.

  • Reads that can return credentials are approval-gated rather than classified low risk. The runner masks a run's sensitive values in a single pass, so one match cannot rewrite another's marker.

  • MFA enrollment and recovery-code regeneration both require proof of the current inbox. Credential step-up codes are rate limited across the cluster rather than per node.

  • Every pack's curl is confined to an explicit protocol with globbing off, so a URL arriving in an API response cannot expand into extra transfers or carry a credential to a host that response chose.

Runner

  • Official multi-architecture container image at ghcr.io/andrewdryga/emisar-runner, with build provenance and an SBOM.
  • Backlogged run output ships as one frame instead of one per line, the redaction window is scanned once per line rather than rescanned, and the connection lease renews at half its life instead of on every heartbeat.
  • EMISAR_GROUP and EMISAR_RUNNER_ID can relabel a fleet without editing configs on each host.

MCP

  • Signed dispatch signs the narrative a human approver actually reads (attestation v5), and the bridge verifies that narrative rather than a reconstruction of it.
  • The bridge's signing key comes from a pinned credential directory instead of the environment.
  • Runbook targets can name a runner group in the model contract, and an agent can revise and test a runbook draft before a human publishes it.
  • Resolving one action reads that action's own pack instead of the whole catalog, and a catalog listing compares a stored descriptor digest instead of every descriptor column.

Packs

  • The catalog now carries 95 packs and 1,498 actions, adding Apache Airflow, Spark, Google Cloud billing, and BunnyCDN.
  • Every risky action in a modeled pack is either proven by a behavior case against a real service or carries a declared reason it cannot be.
  • Each pack's structured output is bounded to fit the runner's cap at its own advertised worst case, and jq filters stay on core builtins so a minimal host cannot fail after the command already ran.

Console

  • Runbook target selection scales to a real fleet: one stable trigger that names the chosen targets, a searchable roster of dense one-line rows, and scope icons that encode cardinality instead of infrastructure nouns.

Platform

  • The reads behind agent and console traffic got measurably cheaper: keyset pages have the indexes their cursors need, the console stopped paying for its first paint twice, and the MCP bridge cut its per-request overhead.
  • The web layer is now a strict adapter — it calls top-level contexts and never builds a changeset — enforced by custom Credo checks rather than review.

Components

  • Runner 0.18.0
  • emisar-mcp 0.7.0

Verifying a download

gh attestation verify emisar-0.18.0-linux-amd64.tar.gz --owner andrewdryga
sha256sum -c SHA256SUMS
gh attestation verify oci://ghcr.io/andrewdryga/emisar-runner:0.18.0 --owner andrewdryga