Skip to content

Versions and publishing

Forge separates creating an app version from moving the clean public URL to that version.

  • Deploy snapshots a Git ref, reviews the manifest and source snapshot, builds it, starts the runtime, and records an addressable internal version and app-environment routing records.
  • Publish points the app's clean public URL at one reviewed version whose deploy lifecycle is complete. Subsequent deploys do not move that clean public URL until you publish again.

This separation lets a developer deploy and inspect release evidence before an operator moves user traffic.

When to use this page

Use this page when you need to:

  • see which app versions exist;
  • confirm which version is published;
  • publish a reviewed version to the clean public URL;
  • move the clean public URL back to a previous reviewed version;
  • prevent a publish from moving traffic to a version with the wrong source SHA.

For the deploy path that creates versions, see Deploy.

Operator flow

Use this sequence for a customer-visible release:

  1. Wait for the deploy to reach Succeeded. A deployment that is already Promoted is also publishable.
  2. Record the deployment ref and the reviewed source SHA. Use the deploy response or map versions <app> for the deployment ref. When you already have a deployment ref, use map --json status <deployment-ref> to confirm it reached Succeeded or Promoted. Use the reviewed Git commit from the release review, direct deploy request, or custom-CI run as the source SHA.
  3. Run map versions <app> for the app ref you intend to publish, and choose the internal version that points at the reviewed deployment ref.
  4. Publish with the reviewed SHA guard:
map publish <app> --version <version> --expected-sha <sha>
  1. Run map versions <app> again and confirm the published line points at the intended deployment ref.
  2. Open or curl the clean public URL and keep the result with the release evidence.

Do not use the app-environment alias as the publish target. Publish selects an internal version label or an explicit deployment ref.

Before moving customer-visible traffic, check:

Check Required evidence Stop if
Deploy state map --json status <deployment-ref> shows deployment.status.status is Succeeded or Promoted. The deployment is still running, failed, blocked, superseded, or unknown.
Version selection The internal versions row you picked has the deployment ref you reviewed. The deployment ref only appears under aliases, or does not match the reviewed evidence.
Source guard --expected-sha is the 40-character Git commit SHA that was reviewed. You only have a branch, tag, version label, or unverified SHA.
Exposure The selected version has the intended public_edge.exposure. The selected version has the wrong reviewed exposure value.
Published clean URL map versions <app> shows published on the same deployment ref after publish. The published record is absent or points at a different deployment.

The routing model

map versions <app> reads the control-plane route status and separates three kinds of routing records for the app:

Record What it means Who uses it
Internal version An addressable per-version record created by a deploy. It has a version label, deployment ref, and hostname. Developers and operators inspecting or selecting a deploy.
App-environment alias The app-environment record, such as production, that can move as deploys or release aliases update. Developers validating the active app-environment target.
Published clean URL The clean public URL. It is pinned by map publish to one internal version. End users and operators controlling customer-visible traffic.

The published clean URL is separate from the app-environment alias. A production deploy can succeed and update the production alias while the clean public URL continues to serve the already published version.

No clean public URL is pinned until map publish creates the published record. Subsequent deploys can create new internal versions and move app environment aliases without moving the published record.

Canary releases

Forge can split an app-environment alias between the current deployment and a canary deployment. Canary does not move the clean public URL. Use Canary releases for the operator workflow, verification steps, and stop conditions.

Canary and publish answer different questions:

Control What it moves
Canary The target app-environment alias split.
Publish The clean public URL pinned by the published record.

After a canary promote, publish is still the operation that moves the clean public URL to a reviewed version. Use the publish flow below when the clean public URL should change.

When publish succeeds, Forge issues the clean public hostname to the chosen version backend, records a pinned published record, and returns that hostname. The published route uses the access model recorded on the selected version, so confirm the selected version's public_edge.exposure before publishing.

The hostname comes from the deployment's app ref. In the self-service path, keep the served app ref aligned with the manifest app ID, such as app:inventory-portal with metadata.app_id: inventory-portal, so map versions, map publish, and the hostname all refer to the same app.

List versions

Run:

map versions <app>

<app> can be the short app name, such as inventory-portal, or a literal app: ref, such as app:inventory-portal. The CLI normalizes the short form to app:<name>.

Use the output to identify:

  • the internal versions Forge can address;
  • app-environment aliases and the deployment ref each alias serves;
  • whether the clean public URL is published;
  • the version label you want to publish or restore;
  • the deployment ref you will compare with deploy status or evidence.

When selecting a version, match the internal version's deployment ref to the deploy status or evidence you reviewed. If the deployment ref does not match, choose another version or stop and re-check the release evidence.

The text output is organized as:

app: inventory-portal

internal versions:
  <label>  <deployment-ref>  <version-hostname>

aliases:
  production (ProductionPromote)  ->  <deployment-ref>  <alias-hostname>

published: <deployment-ref>  https://<app-hostname>

If the app has never been published, the published line says (not published).

With --json, map versions returns:

Field Meaning
app Short app name after normalization.
app_ref Normalized app ref, such as app:inventory-portal.
versions[] Internal version records for that app. Each entry includes label, deployment_ref, hostname, app_env, platform_env, and route_pointer_ref.
aliases[] App-environment records. Each entry includes app_env, updated_from_action, deployment_ref, hostname, pinned, and route_pointer_ref.
published Published clean-public-URL record, or null when the app has not been published.

Publish a reviewed version

Publish by version label after you have reviewed the deploy evidence:

map publish <app> --version <version>

When a human reviewed a specific commit SHA, include the SHA guard:

map publish <app> --version <version> --expected-sha <sha>

--expected-sha tells Forge to publish only if that version still records the exact source SHA you reviewed. If it does not match, Forge rejects the publish with a conflict instead of moving the clean public URL to an unexpected build.

Use --expected-sha for customer-visible releases and rollback restores. It is the operator guardrail that binds the publish action to the reviewed source.

The SHA must be the 40-character Git commit SHA for the deployed source. Do not use a branch name, tag name, or version label as the --expected-sha value.

On success, text output prints:

published https://<app-hostname>

With --json, the response includes the recorded published record under published, including its hostname, route_pointer_ref, and current deployment ref.

Publish by deployment ref

If the operator has an explicit deployment ref, publish it directly:

map publish <app> --deployment-ref <deployment-ref> --expected-sha <sha>

Use this when deploy evidence names the deployment ref instead of a version label. Keep the same SHA guard when a human reviewed a specific commit.

--version and --deployment-ref are mutually exclusive. --version resolves the deployment ref from the internal versions entries in map versions; --deployment-ref skips that label lookup and sends the deployment ref directly. The server still applies the reviewed-and-complete deploy gate and the --expected-sha guard, then publishes the app recorded on that deployment. After publishing by deployment ref, verify the returned hostname and map versions <app> output before treating the clean public URL as moved.

Move traffic back to a previous version

To move the clean public URL back to a previous reviewed version:

  1. List versions:
map versions <app>
  1. Identify the previous reviewed version that reached Succeeded or Promoted.
  2. Publish that version:
map publish <app> --version <previous-version> --expected-sha <sha>
  1. Verify the clean public URL. Use the hostname returned by map publish or shown in the published line. For an app served as app:my-app:
curl https://my-app.apps.mithran.cloud/

This changes the published record. It does not remove the newer deploy record, change source access, delete routes, or change the app's manifest exposure.

What publish will not do

  • It will not create a version. Deploy creates versions.
  • It will not publish a version that has not reached Succeeded or Promoted.
  • It will not override a mismatched --expected-sha; that returns a conflict.
  • It will not change GitHub App grants, onboarding, custom-CI workflow state, or protected-app access policy.
  • It will not move the clean public URL on subsequent deploys. Publish pins the published record until another publish changes it.
  • It will not change the app-environment alias that map versions lists under aliases.

Verification checklist

After publishing:

  • confirm the command succeeded;
  • run map versions <app> and confirm the published version changed as expected;
  • confirm the published deployment_ref matches the deploy evidence you reviewed;
  • open or curl the app hostname;
  • for protected apps, verify an allowed and denied access result;
  • keep the app hostname, version, commit SHA, and publish command output with the release evidence.

Use the hostname returned by map publish or shown in map versions. For an app served as app:my-app:

curl https://my-app.apps.mithran.cloud/

Failure handling

Symptom Likely cause Next step
map publish says the version is not publishable. The deployment ref does not point at a deployment in Succeeded or Promoted. Re-run map versions <app> and choose a publishable version, or inspect the deploy status first.
map publish returns a conflict when --expected-sha is set. The selected version does not record the SHA you expected. Re-run map versions <app>, choose the reviewed version, or re-check the commit SHA.
map publish --version <label> cannot find the label. The label is not an internal version for that app. Run map versions <app> and copy the label from the internal versions section.
map versions shows published: (not published). No clean public URL has been pinned for this app yet. Publish the reviewed version you want users to reach.
The app hostname still shows an older version. The clean public URL has not been published to the new version. Run map versions <app> and publish the intended reviewed version.
The hostname is not the one you expected. The app ref used for deploy or publish does not match the app label you expected. Compare the deployment's app ref, map versions <app>, and the hostname returned by publish.
Manifest review rejected the version for exposure. The selected version may contain an unsupported public_edge.exposure value. Inspect public_edge.exposure and use App access and sharing.

If the publish result or clean public URL behavior does not match the documented model after the checks above, collect the publish evidence named in Support and escalation.