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:
- Wait for the deploy to reach
Succeeded. A deployment that is alreadyPromotedis also publishable. - 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, usemap --json status <deployment-ref>to confirm it reachedSucceededorPromoted. Use the reviewed Git commit from the release review, direct deploy request, or custom-CI run as the source SHA. - Run
map versions <app>for the app ref you intend to publish, and choose the internal version that points at the reviewed deployment ref. - Publish with the reviewed SHA guard:
- Run
map versions <app>again and confirm thepublishedline points at the intended deployment ref. - 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:
<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:
When a human reviewed a specific commit SHA, include the SHA guard:
--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:
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:
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:
- List versions:
- Identify the previous reviewed version that reached
SucceededorPromoted. - Publish that version:
- Verify the clean public URL. Use the hostname returned by
map publishor shown in thepublishedline. For an app served asapp:my-app:
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
SucceededorPromoted. - 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 versionslists underaliases.
Verification checklist¶
After publishing:
- confirm the command succeeded;
- run
map versions <app>and confirm the published version changed as expected; - confirm the published
deployment_refmatches 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:
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.