# Deploying and publishing

Load this card when a task deploys an App to a Hub or publishes it to Plaza.
Deploy or publish only when the user requests it. Its rules are generated from
the [deployment dependency contract](../reference/deployment.md), the
[Plaza publication contract](../reference/publication.md) and the
[App contract](../reference/app-contract.md#wire-and-compatibility-discipline),
which own them. The [Studio guide](../guides/studio.md) and
[Plaza guide](../guides/plaza.md) own the interactive workflow.

## Order of work

1. Compile first under the [App design card](app_design.md); deployment
   resolves the dependencies itself (`deploy.dependencies.*`,
   `deploy.version.*`).
2. Deploy through the MCP tools with an exact Hub ID (`deploy.mcp.*`); Plaza
   installation needs an administrative connection (`deploy.plaza.*`).
3. To publish, deploy every target first and publish a recommended service App
   separately (`publish.package.*`, `publish.auth.*`).
4. Check which version and release each change creates (`publish.preflight.*`,
   `publish.version.*`, `app.wire.plaza-release-immutable`), and leave the
   Studio-owned assets generated (`publish.assets.*`, `publish.documents.*`,
   `publish.install.*`).

<!-- generated:codebot-rules task=deployment-publication -->
<!-- Generated from the reference rule markers; edit the reference and regenerate. -->

## Rules to apply

Studio does not check these rules. Each is quoted from its owner.

### Wire and compatibility discipline

Owner: [app-contract.md#wire-and-compatibility-discipline](../reference/app-contract.md#wire-and-compatibility-discipline)

- `app.wire.plaza-release-immutable`: The common schema/dependency lock and each existing target's exact App version, binary, language, TypesOnly state, and distribution dependencies are immutable within one Plaza release. Changing any of them creates a new content-derived release; adding or removing targets and changing documents or allowed manifest metadata synchronizes the current release. App source never chooses the release tag or public version. See the [Plaza publication contract](../reference/publication.md#app-publication-and-version-identity).

### Deployment dependency contract

Owner: [deployment.md#deployment-dependency-contract](../reference/deployment.md#deployment-dependency-contract)

- `deploy.dependencies.just-in-time`: The language parsers cannot assign dependency versions. In particular, the new version of a local dependency in the same deployment batch is unknown until that dependency has successfully deployed. Studio therefore constructs `AppDependencies` just in time for each package:
- `deploy.dependencies.discovery`: It discovers direct package references from every parser FQN in a reference `Type`, heritage, `Bitflags`, `ForeignTypeSchema`, `FunctionDataSchema`, or `ProtocolSchema`, plus the package component of every Endpoint-client `Fixed=developer-package::function` constraint. Endpoint `Default` does not create a dependency.
- `deploy.dependencies.verified-version-only`: It validates the dependent against the complete exact resource set. The new schema records the version actually verified for each package; it never predicts a version, substitutes Plaza latest for an exact transitive pin, or copies an unverified language-package version.
- `deploy.dependencies.stop-on-failure`: If any deployment or read-back fails, Studio stops before submitting its dependents. It never predicts the Hub version, and `0.0.0.0` is not a valid dependency entry.

### Plaza installation access

Owner: [deployment.md#plaza-installation-access](../reference/deployment.md#plaza-installation-access)

- `deploy.plaza.admin-connection`: Hub-assisted Plaza search, installation-state queries, and App installation or upgrade require an authenticated administrative connection. Control connections cannot perform these operations, even when the account has creation or configuration permission. Administrative access grants admission only: reading an installed App's installation state also requires read or configuration permission on that App; installing a new App requires permission to add Apps; and upgrading an installed App requires configuration permission on that App.

### MCP deployment

Owner: [deployment.md#mcp-deployment](../reference/deployment.md#mcp-deployment)

- `deploy.mcp.prerequisites`: Deployment requires an explicit request from the user, a saved local Rust or TypeScript App project directory, and an exact registered Hub ID returned by `list_deployment_hubs`. VS Code and Libertas Studio must remain open. Hub credentials remain in Studio and are not returned by the tools.
- `deploy.mcp.automatic`: After preflight, Studio deploys automatically; there is no schema review or Submit step. Poll `deployment_status` for the current phase, package audit, progress, and final success or error. A failed package stops later packages. Rust source is built by the Hub during deployment.
- `deploy.mcp.native-packages`: If it needs native build packages that require approval, the MCP job fails and reports the plan; use Studio's interactive deployment to review and approve that installation.

### Schema availability and task requirements

Owner: [deployment.md#schema-availability-and-task-requirements](../reference/deployment.md#schema-availability-and-task-requirements)

- `deploy.dependencies.pinned-history`: At runtime, `AppDependencies` is both the client's complete package-fetch list and the Hub's preloaded authorization and exact-version lookup table. The Hub reads the historical schema, documents, manifest, and code belonging to the pinned version; it never substitutes the package's current version. Clients verify that the returned App version equals the declared pin before assembling the graph.
- `deploy.version.exact-identity`: Every modern producer and consumer uses the required case-sensitive `Version` field. It is an exact identity, never a minimum or latest-version request.
- `deploy.dependencies.schema-before-editing`: This closure covers the complete published schema, including optional fields and unselected Union alternatives. Its schemas and localized documents must already be available in the Hub's type system so editors can resolve and display every supported choice without interrupting editing for on-demand package installation.
- `deploy.dependencies.no-service-task`: Listing an App dependency does not by itself require a configured service task. Required Endpoint services are determined separately by the [task pre-creation gate](../reference/schema.md#62-task-pre-creation-static-dependency-check).

### Account and publication authorization

Owner: [publication.md#account-and-publication-authorization](../reference/publication.md#account-and-publication-authorization)

- `publish.auth.scoped-account-access`: Private account reads require `account:read`; account changes, token creation, revocation, and repository authorization require `account:write`.
- `publish.auth.token-scope`: Publication and withdrawal require `store:publish` on an active Plaza API token. Browser sessions cannot publish or withdraw, and account disabling prevents access.
- `publish.auth.delegation-bounded`: A token created using another token has only scopes held by its parent and cannot expire later than its parent. Omitting an expiry still inherits a parent's limit. Revoking or expiring any ancestor invalidates its descendants; delegation supports at most eight generations. New delegated tokens require their own publishing reauthentication.
- `publish.auth.github-recent`: Publication and withdrawal require GitHub authentication for the exact calling Plaza token within the preceding 15 minutes. Signing into the website or using a different Plaza token does not renew that authority. Publication additionally requires public-repository write permission verified for that same token and repository within 15 minutes. Repository authorization must use the GitHub identity belonging to the signed-in Plaza account. Revocation or expiry of the initiating Plaza token, or disabling its account, invalidates an unfinished repository authorization and its pending credential handoff.
- `publish.auth.github-revocation-separate`: Studio refreshes GitHub repository authorization before publication and withdrawal, preserving the explicit repository selection. Plaza rechecks a publishing repository's stable identity and public visibility when verifying release assets. External write-permission removal takes effect at the next authorization refresh or when the existing 15-minute proof expires; it is not an instantaneous GitHub revocation notification. Revoking a Plaza token stops its Plaza authority; GitHub authorization is a separate credential and must be revoked at GitHub if that access should also end.

### Package and target identity

Owner: [publication.md#package-and-target-identity](../reference/publication.md#package-and-target-identity)

- `publish.package.deployed-first`: A local manifest alone does not make a package publishable. Studio checks deployed targets and dependencies after selection and rechecks them before publication. If no deployment can be confirmed because a Hub is unreachable, restore its connection and retry; otherwise deploy the App first.
- `publish.package.service-separately`: Deploy and publish the recommended service App separately, with a target for each supported Hub platform.
- `publish.package.types-only-no-service`: Publishing a TypesOnly protocol package makes its types available; it does not publish a service that implements the protocol.
- `publish.package.service-task`: A configured service Task must then create a compatible Endpoint before the calling Task can use it; publishing or installing an App does not create that Task.

### Preflight and document convergence

Owner: [publication.md#preflight-and-document-convergence](../reference/publication.md#preflight-and-document-convergence)

- `publish.preflight.stops`: Studio reads the complete local dependency graph and all selected targets before changing any Hub or publication. Every local package must belong to the signed-in Plaza developer; remote dependencies without local source remain unchanged. Duplicate targets, undeployed current source, cycles, conflicting exact pins, unavailable retained dependencies, different canonical schemas/locks, or a binary failing its recorded SHA-256 stop preflight. Targets may have different App versions while sharing the same schema and exact dependency lock.
- `publish.documents.format-not-winner`: After successful preflight, Studio converges documents across selected Hubs in dependency-first order. For each locale, and for the [Display Template](../reference/publication.md#display-template-identity), the numerically greatest four-part content revision wins; missing locales are filled. Equal content revisions with different canonical content are an error. `DocumentVersion` is the JSON format version and never chooses a winner. Each write is refetched and verified. An unchanged Hub performs no write. A changed Hub advances its target-manifest revision while preserving runtime identity and original deployment provenance. If any write or verification fails, GitHub/Plaza publication does not start; earlier successful document updates on Hubs remain.

### Hub target manifest

Owner: [publication.md#hub-target-manifest](../reference/publication.md#hub-target-manifest)

- `publish.artifacts.exact-installed-only`: After every successful deploy, or a document-metadata synchronization that actually changes schema/document metadata, including the Display Template, the Hub writes a new immutable, separately versioned target manifest and atomically advances the current pointer for that language. An idempotent metadata request writes nothing. A document-only revision advances `targetVersion` but preserves `appVersion`, schema, artifact, language, TypesOnly state, and distribution dependencies. The Hub retains historical manifests, schemas, App manifests, localized document mappings and files, and executable artifacts by exact App version. A Hub stores only its own platform target. Studio retrieves an exact manifest and installed artifact; publishing never rebuilds an App or allocates a replacement version.
- `publish.artifacts.compression-not-content`: `artifact` is `null` for TypesOnly. `schema.sha256` and `schema.size` describe the decoded canonical schema. `documents.sha256` identifies the canonical raw format-2 documents inventory, and `documents.count` is its locale count. For a format-3 gzip inventory, this identity is computed by replacing each compressed asset descriptor with its decoded `contentSha256` and `contentSize` and its raw `document.<locale>.<contentSha256>.json` or `display.<contentSha256>.json` name, keeping sorted locales and the default locale, and serializing the resulting format-2 JSON with sorted object keys and no whitespace. The downloaded format-3 manifest has its own asset digest. Compression never changes the Hub's schema or document content identity. Because that manifest names the [Display Template](../reference/publication.md#display-template-identity), its digest covers the template too; the target manifest keeps this shape and the template is not a locale. The target manifest's APT dependency names are deduplicated and sorted. They are per-target OS installation data and never appear in the Libertas schema. This is distinct from the schema's logical `AppDependencies` array of Libertas App packages. For native Rust, the compiling Hub resolves linked shared libraries to the owning APT packages after its local build.
- `publish.artifacts.no-overwrite`: Studio publishes each target-manifest revision under a content-addressed name, `{distribution}-{architecture}.target.{sha256}.json`. It never overwrites the prior asset. For one platform and App version, the greatest numeric `targetVersion` is current; older manifest revisions remain available for in-flight downloads and exact history.
- `publish.artifacts.target-source-revision`: `sourceRevision` in every target-manifest revision is immutable Hub deployment provenance for that exact App/runtime binding. Document synchronization copies it from the prior target manifest. It must not replace it with Studio's current project or publication source revision. `builtAtUnixMilliseconds` is likewise the original target build time. Document revisions and redeployments that reuse the same source revision, schema, artifact, and OS dependencies preserve it.
- `publish.version.new-source-revision`: A deployment with a changed source revision receives a new App version, even when the compiled artifact is identical or the package is TypesOnly. Installing an existing Plaza release preserves its published build time.
- `publish.version.dependency-pin-upgrade`: Consequently changing any dependency pin changes schema identity, requires a new App version for every affected target carrying that schema, and is an upgrade even when the target artifact bytes happen to be identical. It is never classified as document-only synchronization.

### App publication and version identity

Owner: [publication.md#app-publication-and-version-identity](../reference/publication.md#app-publication-and-version-identity)

- `publish.identity.manifest-content-binding`: A runtime binding consists of the exact App version, canonical schema digest, target pair, language, TypesOnly state, artifact digest, and normalized runtime OS dependencies. Target version, source revision, build time, and documents do not replace that identity. Each admitted binding is immutable; availability and current metadata pointers may change.
- `publish.identity.high-water-never-decreases`: The one public Plaza version starts as the numeric, component-wise maximum exact `appVersion` in the reconciled creation target set. Thereafter it is the greatest exact target App version ever admitted to that publication and never decreases when a target is withdrawn. It is not an independently allocated timestamp and it does not replace any target's exact version. For example, creation bindings `20260820.142959.003.0` and `20260821.090000.010.0` produce public version `20260821.090000.010.0`, while each installation retains its own value. If a Hub later deploys content-equivalent bytes under a different `appVersion`, that exact version remains a new immutable binding and follows the normal new-publication and maximum-version rules.
- `publish.version.absent-target-joins`: A previously absent target whose exact App version is less than or equal to the publication's greatest-ever version may be appended to that same publication as a new immutable binding without changing the public version, marker, or tag. A new target above that version creates a new publication whose public version is the new maximum.
- `publish.version.withdrawal`: Withdrawing any target, including the one that established the maximum, changes only current availability and never lowers or reassigns the public version. Re-adding a withdrawn target reuses its retained exact binding; different content or version is not an in-place re-addition.
- `publish.legacy.preserve-exact-target-bindings`: Before asking the developer to confirm, Studio reads the matching GitHub Release with the newest publication time and Plaza history, then downloads and verifies its marker, schema, current document manifest, locale assets, and Display Template, and complete immutable target binding map for a current-format release. A recognized legacy release (a format-1 marker or the older asset layout without a marker) is historical input: Studio skips its obsolete asset verification and publishes a new current-format release. The selected targets must still produce a public version greater than every retained Plaza version, and existing exact target bindings must remain unchanged. Studio preserves the legacy release and its assets; it cannot synchronize documents into that release. With retained Plaza history, a missing GitHub release still stops publication. Unrecognized or malformed markers, failed current-format integrity checks, and GitHub access errors also stop publication.
- `publish.legacy.conversion-greater-version`: A release with a valid format-2 marker and the older raw `schema.json` layout is also classified as obsolete without downloading its old metadata bodies. If the compiled publication still has the same immutable creation identity, Studio appends gzip assets to that same tag and preserves the marker and old assets. A changed creation identity requires a greater public version. The schema gzip asset is uploaded after the other required assets, so an interrupted conversion remains recognizable and can be retried normally.
- `publish.identity.no-target-rebinding`: a selected target with a different Hub `appVersion` never reuses or is rewritten to an older published binding, even when other content is equal;
- `publish.version.document-only`: localized document, source-comment, function-comment, and Display Template changes update only the current content-addressed document locator, search presentation, and one new content-addressed target-manifest revision per affected target for the same publication; each `targetVersion` advances, while no target App version, public version, tag, marker, schema, runtime binding, or artifact changes; and
- `publish.documents.default-locale-remains`: At least the declared default locale must remain.
- `publish.identity.immutable-asset-authority`: GitHub is the immutable asset authority; Plaza independently rejects an in-place schema, dependency-lock, target runtime binding, existing target-manifest asset, artifact, or public-version change; advancing to a new content-addressed target-manifest revision is the allowed document-only operation.

### Display Template identity

Owner: [publication.md#display-template-identity](../reference/publication.md#display-template-identity)

- `publish.template.not-a-locale`: Every package has one compiled [Display Template](../reference/presentation.md#display-template), which is not part of the schema. It is a locale-independent member of the content-addressed documents manifest with its own four-part content revision and a SHA-256 digest of its canonical JSON; the empty template has both. It is never counted as a locale, and removing a locale never removes it.
- `publish.template.document-only-change`: **Document-only change.** A template that differs from the Hub's is a document-only change, never a schema change. It advances `targetVersion` and preserves `appVersion`, schema, artifact, language, TypesOnly state, and distribution dependencies, with no rebuild. Publication follows the document-only reconciliation outcome above: no new App version, public version, tag, or marker.
- `publish.template.format-not-winner`: **Convergence.** The document synchronization rules apply unchanged: the numerically greatest content revision wins, equal revisions with different content are an error, each write is refetched and verified, and an unchanged Hub writes nothing. `DisplayVersion` is the JSON format version and never chooses a winner.
- `publish.template.hub-storage-immutable`: **Hub storage.** The Hub retains templates by digest with the target manifests it already keeps and rejects a template that Studio did not compile from source. It never compiles or edits one.
- `publish.template.schema-delivery-pair`: **Delivery.** The Hub sends a package's template with its schema through the existing conditional delivery of cached resources, keyed by digest: an unchanged template costs nothing, and a changed one arrives without reinstalling the App. A client never receives a schema without its template, including for every exact dependency it fetches.

### Public release assets

Owner: [publication.md#public-release-assets](../reference/publication.md#public-release-assets)

- `publish.assets.studio-owned`: Studio owns these files; App authors must not hand-edit them.
- `publish.assets.revisions-immutable`: Each current GitHub Release contains `libertas.release.json`, immutable `schema.json.gz`, every immutable content-addressed target-manifest revision and executable artifact, the current content-addressed documents manifest, and its content-addressed locale assets. Published runtime bindings, schema, dependency pins, artifacts, and individual target-manifest revisions are never overwritten or removed by document synchronization; current manifest pointers may advance to a newly published revision.
- `publish.assets.metadata-only-manifest`: The documents manifest contains metadata only, never embedded locale document or template bodies. Its asset name includes its own lowercase SHA-256 as `documents.<sha256>.json`; locale assets follow `document.<locale>.<sha256>.json.gz` and the compiled Display Template follows `display.<sha256>.json.gz`. Schema, locale documents, and the template are precompressed with gzip level 9. The marker and target/document manifests stay JSON. Every compressed descriptor's `sha256` and `size` describe the downloaded bytes; `contentSha256` and `contentSize` describe the exact decoded UTF-8 JSON. The release marker's `schemaSha256` continues to identify the decoded schema:

  ```json
  {
    "defaultLocale": "en-US",
    "display": {
      "name": "display.<sha256>.json.gz",
      "sha256": "<64 lowercase hex digits>",
      "size": 64,
      "contentSha256": "<64 lowercase hex digits>",
      "contentSize": 48
    },
    "documents": [
      {
        "locale": "en-US",
        "name": "document.en-US.<sha256>.json.gz",
        "sha256": "<64 lowercase hex digits>",
        "size": 456,
        "contentSha256": "<64 lowercase hex digits>",
        "contentSize": 1234
      }
    ],
    "formatVersion": 3
  }
  ```

- `publish.assets.format-three-display-required`: Format 3 requires the `display` member, including for the empty template. Entries are sorted by locale and own the exact GitHub asset name, byte size, and digest needed for a consumer to choose before downloading. Plaza atomically advances a publication's document-manifest locator after all newly referenced assets exist. Each target-manifest revision records the logical raw inventory digest described above and its locale count. Old content-addressed documents and target manifests remain and are never replaced in place, so a failed upload or retry cannot create a missing-asset window.

### Publication completion and recovery

Owner: [publication.md#publication-completion-and-recovery](../reference/publication.md#publication-completion-and-recovery)

- `publish.install.exact-dependency`: Installation cannot substitute a different target or latest version for an unavailable exact dependency. Re-adding the same retained binding restores availability; changed runtime content follows the version rules above.

### 2.2 Deployment dependency list

Owner: [schema.md#22-deployment-dependency-list](../reference/schema.md#22-deployment-dependency-list)

- `schema.dependencies.exact-version`: `AppDependencies` records the complete flattened set of exact App-version pins. The required `Version` key means exact identity, never a lower bound. The [deployment dependency contract](../reference/deployment.md) owns source discovery, preflight, installation order, read-back verification, and failure behavior.

## Rules Studio enforces

Studio rejects a source that breaks one of these rules. The owner holds the full rule.

### Deployment dependency contract

Owner: [deployment.md#deployment-dependency-contract](../reference/deployment.md#deployment-dependency-contract)

- `deploy.dependencies.source-cache`: A direct package needs its source in the package manager's local cache; a binary-only cache stops deployment
- `deploy.dependencies.plaza-schema-match`: A direct package's source-generated schema must equal its Plaza schema; a mismatch stops before Hub mutation
- `deploy.dependencies.plaza-closure`: Missing or withdrawn Plaza targets, conflicting pins, invalid assets or cycles stop the entire preflight

### App publication and version identity

Owner: [publication.md#app-publication-and-version-identity](../reference/publication.md#app-publication-and-version-identity)

- `publish.version.must-exceed-maximum`: A changed target needs an exact version above the package's greatest published public version

<!-- /generated -->
