CI security / Runnerless Stories

How modern CI protects secrets

Modern CI systems use several controls to reduce credential exposure: masking job output, restricting secret access, and exchanging signed workflow identity for temporary tokens. Each changes a different risk. Keeping registry credentials out of build code requires an enforced isolation boundary as well.

What GitHub and CircleCI masking covers

GitHub Actions redacts configured secrets in workflow logs. Its documentation explicitly says redaction is not guaranteed when a value is transformed, and that a runner can redact only secrets used in the current job. A clean log is therefore useful evidence about that output, not proof that the job never exposed a credential. GitHub’s secrets documentation.

CircleCI masks environment values stored in project settings or contexts in job output, except values shorter than four characters and certain boolean strings. Its documented exclusions include test results and artifacts; it also warns about shell tracing and output reformatting. Masking is a safeguard against accidental display, not a containment system for code that already has the value. Neither vendor promises complete leak prevention through masking. CircleCI’s masking limits.

Restrict which jobs can receive credentials

Access controls still matter. GitHub supports repository restrictions for organization secrets and required reviewers for environment secrets. CircleCI contexts can restrict runtime access through supported project and group restrictions, and rules that evaluate pipeline values; approval jobs can be combined with restricted contexts. These controls determine who or what may receive credentials. They do not make a credential unreadable once it reaches an authorized process. GitHub secret access controls; CircleCI contexts and restrictions.

OIDC removes stored publishing tokens, not all credentials

OpenID Connect (OIDC) lets a CI provider issue a signed identity token describing a job or workflow. A registry validates that token and checks its claims—the identity fields inside the token—against a configured trust policy before granting publishing authority. The signature establishes who issued the identity; it does not establish that the workflow’s code or dependencies are safe.

For npm, the documented exchange API accepts an OIDC identity token and returns a short-lived registry token for a specified package, with creation and expiration timestamps. Its audience field identifies npm as the intended recipient. The exchange still produces a credential that publishing code can use. npm’s OIDC exchange API.

npm supports trusted publishing from CircleCI as well as GitHub Actions and GitLab CI/CD. CircleCI configuration identifies the organization, project, pipeline definition, and VCS origin, with optional context restrictions. The npm CLI can exchange the CircleCI OIDC identity for a temporary publishing token. Check which publishing actions the package’s trust policy allows; a configured workflow is not necessarily permitted to publish directly. npm’s provider setup and allowed actions.

Rust’s crates.io has a similar distinction. The official rust-lang/crates-io-auth-action exchanges GitHub workflow identity for a temporary token, exposes it as an output that later steps in the job can read, and includes a post-job step to revoke it. Cleanup is not a reason to treat the token as safe to expose while the job runs. The publishing job still receives the registry credential. crates.io authentication action.

If you describe this as “credentialless” publishing, the distinction is avoiding stored, long-lived publishing credentials. Identity tokens and temporary registry tokens still exist. Short lifetimes reduce the time a stolen token remains useful; they do not prevent it from leaking or being misused while valid. A compromised authorized workflow may also obtain new tokens while it retains that authority.

An isolated action API can keep the downstream credential out of build code

A different design moves publishing into a separate service with a narrow action API: an interface for requests such as “publish this artifact.” The registry credential—the downstream credential—stays inside that service. Build code submits a request to publish an approved artifact. The service checks the request, retrieves the exact artifact, and performs the registry operation itself. It returns a publication result, never the downstream credential.

Proposed architecture: separate build execution from credential use.
  1. Build job: produces an artifact and requests publication.
  2. Isolated service: verifies identity, policy, digest, and approval; holds the registry credential.
  3. Registry: receives the approved package from the service.
  4. Build job: receives a version or failure result, without a registry token.

The following are design requirements, not guarantees supplied merely by putting an API in front of a secret:

  • Verify identity and current authority. Validate signature, issuer, audience, expiry, and relevant repository/workflow claims against trusted configuration. Derive permissions from the verified identity and service policy, never from permissions the caller claims in a request.
  • Restrict the operation. Allow only the policy-approved package, registry, destination, and release action. Do not accept an arbitrary URL, shell command, or upload target that could redirect credentials.
  • Tie approval to the exact artifact. Verify the artifact’s digest (its cryptographic hash) and trusted build identity. Check that approval covers that digest, package, version, and destination immediately before publishing. A matching digest alone does not establish that an artifact is safe; the approved record must itself be trusted.
  • Prevent substitution and replay. Publish the bytes that were verified, and keep a durable operation record so retries cannot silently publish a different artifact or reuse an approval for a different action.
  • Enforce isolation. Build processes must not be able to read service memory, files, or debug interfaces. The publisher must not run artifact-supplied scripts with credential access. Keep credential values out of responses, errors, logs, and dumps.

When isolation is enforced, this removes the build-memory leak path for credentials that never enter the build environment. An API that fetches a secret and returns it to the job does not provide this protection. OIDC and an isolated publisher can also be combined if the registry accepts an identity available to the service. The service would perform the exchange and retain the resulting registry token. Do not assume that a registry’s support for a particular CI workflow automatically extends to an external publisher.

Planned for Runnerless: automation code would request restricted actions while a separate service handles integration credentials. That service and its isolation controls are not implemented or available in Runnerless. The planned artifact approval workflow describes a related part of the design.

Choose controls for the risk you need to reduce

These controls can work together. Their tradeoffs differ: masking needs little workflow change; trusted publishing removes the need to store and rotate a long-lived token but needs a carefully scoped trust policy; an isolated publisher adds a service and policy system to operate.

What each credential control changes
ControlWhat it helps withWhat remains exposed
Log maskingSuppresses accidental display of recognized values in supported output.Values in memory, unmasked transformations, and files outside masking coverage.
Secret access restrictionsLimits which jobs and actors receive a credential.An authorized process can still read and leak it.
OIDC trusted publishingAvoids a stored long-lived publishing token; limits temporary authority through registry policy.Temporary tokens in the publishing job; misuse by a compromised trusted workflow.
Isolated publishing serviceKeeps downstream credentials out of build code when isolation is enforced.Service compromise and misuse of allowed actions; needs protected approvals and artifact verification.

A credential-holding service becomes a valuable target. It still needs narrow permissions, patching, access controls, safe diagnostics, and a recovery process. An approved workflow can also publish a malicious package without ever stealing a token. Credential isolation does not replace code review or supply-chain controls.

Practical steps for an existing pipeline:

  1. Inventory which jobs and subprocesses receive credentials. Remove publishing authority from builds and tests that do not need it.
  2. Keep masking enabled, while checking its documented limits. It complements careful output and artifact handling; it does not replace them.
  3. Use repository, environment, or context restrictions to give secrets only to the jobs that need them. Review who can change those restrictions or approve a privileged job.
  4. Use trusted publishing where supported. Narrow registry trust and job permissions, protect workflow changes, and require approval where appropriate.
  5. If build code must never receive a downstream token, evaluate or build a publisher with an enforced isolation boundary and a restricted action API. Runnerless does not offer this yet. Verify that any publisher you adopt does not run build-supplied hooks.
  6. Have a response procedure for exposed credentials. The companion story’s inspection and incident-response steps cover revocation, investigation, and diagnostic cleanup.

The artifact handoff matters too. Why your publishing service should pull artifacts explains how service-side retrieval separates build output from the authority to publish it.

For the accidental exposure paths behind these controls, read accidental CI secret leaks: logs, artifacts, and core dumps. Start by removing unnecessary credential access and narrowing artifact uploads in your existing pipeline. GitHub’s secure-use guidance provides further hardening and incident-response steps.