Features

Secure by default

Short-lived credentials through OIDC, protected environments with required reviewers, resolved permissions, and secrets that are sealed at rest and never written to a log.

No long-lived cloud keys

permissions: id-token: write works as it does on GitHub: the job receives a short-lived identity token and exchanges it for cloud credentials that expire on their own; the identity token itself lasts five minutes. Your cloud's trust policy has to name Apex's issuer rather than GitHub's. No access key in a repository secret, no rotation calendar, nothing to leak.

permissions:
  id-token: write
  contents: read
steps:
  - uses: aws-actions/configure-aws-credentials@v4
    with:
      role-to-assume: arn:aws:iam::123456789012:role/deploy
      aws-region: eu-west-1

Environments that wait for a person

A job that names a protected environment waits for the rules an operator set: required reviewers (named people or Account roles), a wait timer, a branch policy. The run screen shows which environment the job is waiting on and why, with Approve and Reject for reviewers. Deployments are recorded whether or not anything gated them. Protection rules are set through the Apex API today; GitHub's own environment rules are not read.

Which actions may run

An organization can decide which actions its workflows may use. It can allow patterns such as actions/*, deny others, and require each action to be pinned to a commit SHA rather than a version tag. In audit mode a breach is recorded on the run and the run still starts, which shows what enforcing would stop. In enforce mode a breach refuses the run, and the refused run names the action and the rule. A repository cannot loosen its organization's policy. The organization's Owners and Account Admins set it:

apex actions policy acme --mode audit --allow "actions/*" --require-pinned
apex actions list acme/web          # what one repository pulls in, and what the policy makes of it

Permissions, resolved

permissions: is resolved the way GitHub resolves it — per-job overrides and read-all / write-all — and it narrows both GITHUB_TOKEN and whether a job may request an OIDC token. Each job's GITHUB_TOKEN is minted for that job and reaches only its own repository, so least-privilege there is least-privilege here. Without a permissions: block it carries GitHub's permissive default, limited to what the Apex App holds: issues, statuses and deployments are not among them. A pull request from a fork gets a read-only token for its own repository and no secrets.

Secrets are write-only values

A secret is sealed with KMS when it is set and opened only for jobs of a trusted run in its repository. Forks get none. It is masked in every log and never printed, and when you reproduce a job locally the command names each secret without carrying its value.

Pull requests from forks

A pull request from a fork runs code written outside your repository, so it starts with nothing, and by default it waits for a person:

  • By default, every pull request from a fork waits until an Owner or Admin approves it, from the run's page or with apex approval approve. The run's page says what approving would grant before you decide. An approval covers that commit: a new push to the pull request waits again.
  • Or choose GitHub's rules. An Owner or Admin can set a repository to hold only first-time contributors, only first-time contributors who are new to GitHub, or every external contributor, as GitHub's setting for public repositories does. A pull request that somebody other than its author updated still waits.
  • Whichever you choose, a fork's run never gets secrets. Repository and organization secrets are never opened for it, whether it waited or not.
  • A GITHUB_TOKEN that can read its own repository and nothing else, and no OIDC identity token.
  • A cache it can read but not write, so it cannot plant an entry that a trusted run restores later.
  • Its machine takes no other job. It is the last job that machine runs, and the machine is then terminated.
  • The exception is pull_request_target, as on GitHub. A workflow that listens for it runs your default branch's copy of the workflow, with the repository's secrets and no approval, on a machine of its own. Never check out the pull request's head in it: that would hand a stranger's code those secrets.
apex approval list                          # runs from forks waiting for a decision
apex approval approve <run-id>
apex approval policy acme/web first-time    # hold only first-time contributors, GitHub's default for public repositories

Isolation

Every job runs in a fresh container, with its own workspace and its own container runtime, all removed when the job ends. A runner machine takes one job at a time and may take the next, from any customer; a job from a fork's pull request, or one started by pull_request_target, is the last job its machine runs, and that machine is then terminated. An idle machine terminates after five minutes.

Same workflows. One subscription.

Every plan starts with a 10-day trial, without a card.