Release Management Pro

The receipt is
the release.

nimbus release validate proves a payload — locally, then inside the target org — and writes an immutable receipt. nimbus release deploy accepts nothing else. Approval sits between them, and the two halves are free to run on different machines on different days, because the evidence travels as a file rather than as a claim about a worktree.

Anatomy of a receipt

A receipt is a JSON file written under .nimbus/releases/ beside the content-addressed bundle it describes. It records what was proven, against which bytes, in which org, at which moment. Signing is a one-time setup (nimbus release keygen creates a key and trusts it in the project); once it is on, a receipt carries a signed attestation of who validated it, so the record is tamper-evident rather than merely written down — and a profile can set requireSigned to refuse unsigned receipts outright.

It is deliberately a portable artifact, not a local cache. Receipts carry no org aliases, no credentials, and no source, so uploading one between CI jobs leaks nothing. When a downloaded receipt is passed by path, Nimbus locks, reads, and updates that file beside its sibling bundle instead of quietly creating a second local copy.

nimbus release verify re-hashes the bundle and checks the signature offline, on any machine, with no org and no network. nimbus release export writes the whole receipt set plus the signer roster into one self-verifying evidence document for an archive an auditor can re-check years later — without Nimbus, using verify-export.

bash
# Prove a payload and write the receipt
nimbus release validate --target-org staging

# Only what is new or differs from the org —
# the gates still run the full suite
nimbus release validate --target-org staging --changed

# Read the record back
nimbus release list
nimbus release get rel_20260720T141342_0397df68b0 --json

# Offline: re-hash the bundle, check the signature
nimbus release verify rel_20260720T141342_0397df68b0

# Archive the whole evidence set
nimbus release export --org-kind production \
  --since 2026-07-01 --output evidence-2026Q3.json
01 · Bundle digest

A content-address over the exact uncompressed bundle that validate staged. At deploy time the stored bundle is re-hashed against it. A byte that moved is a release integrity violation — exit code 25 — not a warning.

02 · Git revision

The commit the bundle was staged from, so the evidence points back at reviewable source. A profile can require a clean worktree; an uncommitted tree needs --allow-dirty and that fact is recorded on the receipt itself.

03 · Org fingerprint

A one-way fingerprint of the target org — never the alias, never a credential. Deploying the receipt at a different org fails closed. It is also why the deploy step still needs its own --target-org or --release-profile.

04 · Gate results

What the local gates actually measured against that bundle: tests, line coverage, mutation score where a threshold is set, Salesforce Code Analyzer violation counts with the rule policy that produced them, and permission-seam counts.

05 · Validation job ID

The Salesforce check-only validation job that checked the same payload in the target org. For production targets that job is what a later quick deploy reuses, so the org-side proof travels with the receipt.

06 · Quick-deploy expiry

The exact moment the validated job stops being quick-deployable. maxReceiptAge caps how old any receipt may be at deploy time and defaults to the 10-day Salesforce quick-deploy window — an aged bundle would deploy against a drifted org.

Immutability is enforced at deploy, not documented in a README. A receipt whose source changed, whose validation expired, whose target org does not match, or whose toolchain is incompatible is rejected and you revalidate. There is no --force, no --skip-verify, and no configuration key that reintroduces one. An override flag that exists is an override flag that gets used at 5pm on a Friday, so the flag does not exist.

Validate, approve, deploy — three acts, two machines

A one-shot gated deploy is the right shape when one person decides and ships in the same minute. Regulated and multi-environment teams do not work that way: the proof is produced by CI, the decision is made by a human hours later, and the deployment is executed somewhere else entirely. The release workflow is built for that gap rather than around it.

1

Validate — on whichever runner is free

nimbus release validate snapshots the selected source into a content-addressed bundle, runs the local gates against that bundle, then has Salesforce check the same bytes in the target org. With no selector it defaults to the packageDirectories in sfdx-project.json, exactly like sf project deploy start. It writes the receipt and the bundle under .nimbus/releases/ and stops. Nothing is deployed.

2

Approve — a human, or your platform gate

The receipt and bundle move as an ordinary pipeline artifact through whatever approval your platform already enforces: GitHub required reviewers, a GitLab manual job, an Azure Environment check, or a person reading the release notes. Nimbus's --confirm-production is not that approval — it is the non-interactive acknowledgement that the gate was already passed, because a CI runner has no terminal to type a confirmation into. A production profile can additionally require a counter-signature, so the identity that validated a release cannot be the identity that ships it.

3

Deploy — the validated payload, and only that

nimbus release deploy re-checks the receipt before it does anything: org fingerprint, bundle digest, receipt age, toolchain version policy, and whether baseline components drifted in the org since validation. Where Salesforce still supports it, the validated org-side job is quick-deployed within the 10-day window — no source retransmitted. Otherwise the exact stored bundle is re-sent. It reruns no gates and restages no source, which is precisely why the bytes cannot change between the proof and the deploy.

Each failure mode gets its own exit code, so a pipeline can react without parsing prose: 21 a local gate, 22 Salesforce rejected the payload, 23 the deploy failed, 25 a release integrity violation, 27 baseline components drifted in the org after validation, 29 a required counter-signature is missing.

Because the receipt stores only a one-way org fingerprint and never the alias, the deploy step is told its target explicitly. After a successful validate the printed Next: line is copy-paste runnable and already carries it.

bash
# Machine A — CI, on merge
nimbus release validate --release-profile production

# ── approval happens here ──

# Machine B — the gated deploy job
nimbus release deploy \
  --receipt .nimbus/releases/rel_....json \
  --release-profile production \
  --confirm-production

# Both halves in one step, when that is honest
nimbus release run --release-profile staging

# Read-only: what would reconciling the org
# to your source actually change?
nimbus release plan --target-org staging
After the deploy. nimbus release promote carries the identical bytes to the next org, re-checked there, as a new receipt chained to its origin. nimbus release rollback restores what a deploy overwrote, from the snapshot taken automatically just before it ran — with one honest limit worth reading before the incident, not during it: components the bad deploy added stay in the org, because a snapshot of prior content holds nothing for something that did not previously exist. Removing them is a destructive change and stays a separate, explicit step.

When a Salesforce deployment is stuck in Pending

Salesforce runs one deployment at a time per org and does not show you the queue. So a deploy that is politely waiting its turn looks exactly like a deploy that is hung — and a Pending job the scheduler silently dropped looks exactly like one that is about to start. Without the queue, the only available move is to wait longer, and the only available diagnosis is a guess.

nimbus release status reads the org's deployment queue directly: each job's position, live component and test counters, and recent history correlated with your local receipts. A job that is genuinely stuck — Pending beyond a threshold, never started, with nothing running that would explain the wait — is called out as stuck, with the remedy. The threshold is nimbus.release.stuckPendingAfter, three minutes by default.

nimbus release watch follows a Salesforce job ID (0Af…) or a receipt until it reaches a terminal state and mirrors the outcome in its exit code. Watching is read-only: it never cancels and never resubmits, so it is safe to point at anyone's deployment — including the one that is blocking yours.

nimbus release requeue is the remedy. It cancels the stuck Pending job and resubmits the same validated bundle, which is the manoeuvre that unsticks it, because a job that never started has executed nothing org-side. It takes a receipt — never a bare job ID — and refuses anything that is not provably the stuck case: the org must match the receipt's fingerprint, and the job must be Pending and never started. A running job is left alone; a finished one asks you to revalidate.

bash
# Is it queued, running, or actually stuck?
nimbus release status --target-org production

# Stable JSON for a scripted gate
nimbus release status --json --limit 20

# Follow a job — or a receipt — to its outcome.
# Read-only: safe on anyone's deployment.
nimbus release watch 0Af5g00000XXXXXCAA
nimbus release watch rel_20260720T141342_0397df68b0

# Cancel the stuck Pending job and resubmit
# the byte-identical validated bundle
nimbus release requeue rel_20260720T141342_0397df68b0

# Exit code 26 means "submitted, outcome
# unconfirmed" — the deploy may still be running
nimbus release watch <job>
Most of the time you will never run requeue. While Nimbus is attending its own job it polls the org, streams progress, and — when that job sits Pending past nimbus.release.autoRequeueAfter (five minutes, up to nimbus.release.maxRequeues attempts) — cancels and resubmits the identical bundle by itself, recording every recovery attempt on the receipt. No re-confirmation is asked, because nothing ran and the payload cannot drift. Deployments Nimbus did not create are diagnosed but never touched: cancelling a colleague's deploy is a human decision. nimbus release status is read-only and deliberately not Pro-gated — knowing your deploy is stuck is safety, and the safe default is never paywalled.

Two jobs, one artifact, one approval

The pipeline shape is the workflow shape: a validate job that ends by uploading .nimbus/releases/, your platform's own environment protection, and a deploy job that downloads the same artifact into a fresh runner. The receipt's own verification is the safety net for that hand-off — nothing about the artifact needs to be trusted, because it is re-checked before a single byte is sent.

One detail the deploy job is easy to get wrong: it still needs the project skeleton on disk. The Salesforce CLI deploys in project context, so the checkout that runs nimbus release deploy must contain sfdx-project.json and every path its packageDirectories declares. An empty force-app/ is fine — the directory just has to exist.

Ready-to-copy templates exist for GitHub Actions, GitLab CI/CD, Azure DevOps, and generic shell. All four read the same ci release profile and pin the Salesforce CLI with nimbus toolchain sf install --version, so the runner provably reproduces the configured toolchain. The release-in-CI reference covers the profile keys, the secrets, and the full exit-code contract.

yaml
# GitLab CI/CD, condensed — full template in the repo
validate:
  script:
    - nimbus toolchain sf install --version "$SF_VERSION"
    - nimbus release validate --release-profile ci
  artifacts:
    paths: [".nimbus/releases/"]   # receipt + bundle

deploy:
  needs: [validate]
  when: manual                     # ← the human gate
  environment:
    name: production               # protected environment
  script:
    - nimbus release deploy
        --receipt "$(ls .nimbus/releases/rel_*.json)"
        --release-profile ci
        --confirm-production

Nimbus proves; your build and promotion tooling ships.

Nimbus release is the evidence layer, and only the evidence layer. It does not manage package versions, dependency-ordered installs, scratch-org pools, branch promotion models, or data seeding — those belong to the package manager and promotion pipeline you already run, and they are good at them. Point nimbus release at the bundle your pipeline assembles and it adds proof around the deploy rather than opinions about the pipeline that built it.

That division is the design, not a roadmap gap. A promotion tool answers “what moves next, and in what order?” A receipt answers a different question — “what exactly was proven, against which bytes, in which org, by whom, and is that still true right now?” Teams need both answers, and they are better answered by two tools that stay in their lane than by one tool with a view on everything.

Start with the free one.

nimbus deploy — local gates, Salesforce validation, and a local receipt in one command — is Free, and it is the right shape until a human approval has to sit in the middle. The release workflow on this page is Pro: currently free, no card required.