Publish, versioning and preview builds
Compile a metadata app into a versioned artifact, then install it from the catalog or pin it as the runtime's boot artifact to preview and publish.
Your app and the platform move on separate clocks
A metadata app is not part of the platform. It compiles to its own immutable artifact, carries its own version, and is released by you, when you decide. The platform — the runtime image, the kernel, the plugins — is released on the ObjectStack release train. Neither version implies the other, and neither has to wait for the other.
That independence is the point of the model, and it is also the question this page answers: if the app is a separate thing, how does it get into a running platform?
The compile half is always the same:
objectstack.config.ts -> os compile -> dist/objectstack.jsonThe delivery half has two shapes, and picking the wrong one is the expensive mistake. Both are described below, with the rule for choosing.
The one place the two clocks touch is the artifact's declared engines.protocol
range: it states which platform majors the app can run on, and a runtime outside
that range refuses the app rather than half-loading it. Everything else about
the two versions is independent.
The legacy direct-to-environment os publish / os rollback commands (which wrote
sys_environment_revision) were removed (#2237). Publishing now goes through the
package catalog: os package publish uploads a versioned package, and an
environment is updated by installing a version into it. Revision-style rollback
is no longer a framework CLI command — version and environment management are a
Cloud control-plane concern.
The two ways in
| Catalog install | Artifact-pinned boot | |
|---|---|---|
| The app arrives | after the platform is running | at boot, as an input to the process |
| Named by | package id + version (com.acme.crm@1.2.0) | a URL to the compiled artifact (OS_ARTIFACT_URL) |
| Who picks the version | whoever runs the install — an admin in the Console, CI calling the CLI, or the Cloud control plane | whoever sets the runtime's environment (your deploy pipeline) |
| Switching versions | install another version; no restart | change the variable, restart |
| Apps per runtime | many, side by side | one — it is this runtime's app |
| Integrity | the catalog is the trust boundary | optional #sha256= pin in the URL fragment; boot is refused on mismatch |
| Needs | a catalog the runtime can reach — or the artifact file, handed over inline | somewhere to host the artifact file |
| Detail lives in | this page | Artifact-pinned boot |
The rule for choosing
One question decides it:
Is this app the reason the runtime exists?
Yes — pin the artifact. The deployment defines the app, so the app belongs in the deployment's own inputs. No — install from the catalog. The runtime is a platform that receives apps, so apps arrive through a surface that stays open while it runs.
Three tie-breakers when both still look plausible:
- Must "which bytes is this instance running?" have exactly one auditable
answer? Pin the artifact. A
#sha256=fragment makes the answer verifiable, and a mismatch stops the boot instead of quietly serving something else. A catalog install is a live mutation of a running platform — the right shape for an app store, the wrong shape for a reproducible deployment. - Must the app change without a restart, or must several apps share one runtime? Install from the catalog. The pinned artifact is resolved once, at boot; there is no in-place upgrade of it, by design.
- Is there no catalog to reach (air-gapped)? Either still works, so choose
on 1 and 2 rather than on connectivity:
os package install ./dist/objectstack.jsonhands the compiled artifact over inline with no catalog round-trip, andOS_ARTIFACT_URL=file:///srv/app/objectstack.jsonpins a mounted file.
Using both at once
They are not exclusive, and the combination is well-defined:
-
A pinned runtime still accepts installs. The install surface is mounted independently of how the runtime obtained its own app, so a runtime booted from
OS_ARTIFACT_URLcan still receive catalog installs — of other apps. -
Two sources naming the same app is a refusal, not a merge. An install whose
manifest.idis already registered by the runtime's own boot is rejected with409 MANIFEST_CONFLICT("already defined by this runtime's local code") rather than overwriting it. Uninstall or unpin first; do not expect last-write-wins. -
A collision is the second refusal, not the first. Before it looks for a collision, the install parses
manifest.idexactly as the package manifest declares it: reverse-domain notation, lowercase dot-separated segments (com.acme.crm). An id that is not —late-app,com.acme.my_app— is rejected with400 PLUGIN_MANIFEST_INVALIDcarrying the declaration's own remedy, and nothing is registered or written (502when the id arrived in a catalog snapshot rather than in your request). Anameis not an id: a manifest withoutidis refused the same way. -
Artifact sources have a fixed precedence, so a container that presets one variable cannot shadow a deliberate override:
--artifact > OS_ARTIFACT_URL > OS_ARTIFACT_PATH > <cwd>/dist/objectstack.jsonOS_ARTIFACT_URLalso outranks anobjectstack.config.tssitting in the working directory — setting it is an instruction to boot one specific artifact, not a hint.
A catalog address is not an artifact URL. OS_ARTIFACT_URL reads a compiled
artifact from wherever you host it — release storage, an object-store URL, a
mounted file. The catalog's manifest endpoints answer with an API envelope that
wraps the manifest, not with a bare artifact, so a catalog URL pasted into
OS_ARTIFACT_URL is not a supported reference. Publish to the catalog and
upload the artifact if you want both doors open.
1. Compile
os compile
# -> dist/objectstack.jsonThe artifact contains metadata, manifest requirements, and packaged function code. Deployment config stays outside the artifact: database URLs, secrets, runtime credentials, and environment identity are host inputs.
This one artifact feeds both delivery shapes — it is what os package publish
uploads, and it is what an artifact host serves to OS_ARTIFACT_URL.
2. Publish a package version
# upload dist/objectstack.json as a new version in your org catalog
os package publish
# explicit artifact + install into an environment in one step
os package publish ./dist/objectstack.json --env env_prod --install
# no --env: installs into the active environment (`os environments switch`,
# or `os environments create --activate`)
os package publish --installThe CLI:
POST /api/v1/cloud/packages— ensure asys_packagerow exists (id derived fromartifact.manifest.id, or--manifest-id).POST /api/v1/cloud/packages/:id/versions— snapshotdist/objectstack.jsonintosys_package_version.manifest_json(statuspublished).- (optional) install the new version into
--env.
Common flags:
| Flag | Env var | Purpose |
|---|---|---|
artifact (positional) | — | Path to the compiled artifact (default dist/objectstack.json) |
--server, -s | OS_CLOUD_URL | Cloud control-plane URL |
--token, -t | OS_CLOUD_API_KEY | Bearer token (service mode) |
--version, -v | — | Semver version (default: artifact.manifest.version) |
--visibility | — | org · private · marketplace. Omitted: the control plane decides — an existing package keeps its visibility, a new one gets its default (org on ObjectStack Cloud) |
--org | OS_ORG_ID | Owner org id (service mode) |
--env | OS_ENVIRONMENT_ID | Environment to install the new version into. Defaults to the environment os environments switch (or os environments create --activate) recorded for this control plane in ~/.objectstack/cloud.json |
--install | — | Auto-install the new version into --env after publishing |
The --env default is read from the cloud credential store only, and only
when that file's url is the control plane being published to. It is never
taken from ~/.objectstack/credentials.json: that file records the runtime
identity's server (http://localhost:3000 by default), so an environment id
from there can name an environment on a different control plane.
In user mode the package is owned by your active organization; in service mode
(bearer key) pass --org. See Packages for the package model.
Publishing changes nothing that is running. It puts a version in the catalog; step 3 is what moves an app.
3. Install a version
Two install targets, same catalog:
Into a Cloud environment — either at publish time with --env --install, or
separately through the Cloud control plane / Marketplace. To "roll back," install
the prior version; there is no revision-activate CLI command anymore.
Into a running runtime you operate — os package install calls that
runtime's local install endpoint, which registers the app into the live kernel
and caches the manifest on disk so the install survives a restart:
# catalog mode: the target runtime fetches the version from its own catalog
os package install com.acme.crm --version 1.2.0 --runtime https://app.example.com
# air-gapped mode: the artifact is read locally and sent inline, no catalog
os package install ./dist/objectstack.jsonThis authenticates against an account on the target runtime, not your cloud login. Uninstall removes the cached manifest; the kernel needs a restart to fully unload it, because app registration is additive.
For the full command reference — every flag, every subcommand — see the CLI reference.
4. Preview patterns
Use one of these shapes:
| Pattern | Command |
|---|---|
| Local artifact preview | os dev --artifact ./dist/objectstack.json --ui |
| Production artifact host | OS_ARTIFACT_PATH=./dist/objectstack.json os start |
| Pinned, exactly as production runs it | OS_ARTIFACT_URL="file://$PWD/dist/objectstack.json" os start |
| Cloud environment preview | Install to a preview environment, then route clients to it via /api/v1/environments/:environmentId/... or X-Environment-Id. |
The third shape is worth using before a pinned rollout: it exercises the same boot
path production will take, including the engines.protocol handshake and the
boot-time schema-drift policy, against a local file.
Related
- Artifact-pinned boot — the operational detail for
OS_ARTIFACT_URL: schemes, integrity pinning, cache behaviour, pre-signed URLs - CLI reference
- Packages
- Deployment Overview
- Environment Variables
- Environment-Scoped Routing
Single-Environment Mode
Run ObjectStack as a one-environment local stack without a Cloud control plane.
Install Modes
Declare which install modes a package permits with installModes, then pick one at install time. What a managed install keeps, and what an unmanaged copy keeps and drops, the vendor's protection locks included.