5.10.2.1 Builds
The Builds domain of releases. What a version contains: the catalog of projects, applications, platforms and depots, and the builds with their manifests and chunks, uploaded by CI through signed URLs and deduplicated by content. Staff shape the catalog; the build uploader creates builds; nothing here reaches a client until a release on a channel. It is one API, with its own contract and its own version.
5.10.2.1.1 Versions¶
| Version | Status | Prod URL | Contract |
|---|---|---|---|
v1 |
Proposed | https://releases.moonitoring.com/builds/v1 |
builds/v1/openapi.yaml |
The resources follow the data model. Adds the upload handshakes (ChunkCheckRequest, ReleaseManifestInput with its signed upload URL, ReleasePatchArtifactInput) and the purge of req 1006.16.
5.10.2.1.2 Operations¶
Every operation names who calls it and the capability it checks (x-moonitoring-audience, x-moonitoring-capability in the contract); every capability is in the catalog of the data model (req 1002, 1006). Lists are paged by cursor, writes that create something carry an Idempotency-Key, errors are Problem Details.
| Operation | Request | Who | Capability |
|---|---|---|---|
| List projects | GET /projects |
Staff | releases.releases.read |
| Create a project | POST /projects |
Staff | releases.release_catalog.manage |
| Update or archive a project | PATCH /projects/{project_id} |
Staff | releases.release_catalog.manage |
| List applications | GET /applications |
Staff | releases.releases.read |
| Create an application | POST /applications |
Staff | releases.release_catalog.manage |
| Update an application | PATCH /applications/{application_id} |
Staff | releases.release_catalog.manage |
| List an application's depots | GET /applications/{application_id}/depots |
Staff | releases.releases.read |
| Compose an application with a depot | PUT /applications/{application_id}/depots/{depot_id} |
Staff | releases.release_catalog.manage |
| Remove a depot from an application | DELETE /applications/{application_id}/depots/{depot_id} |
Staff | releases.release_catalog.manage |
| List platforms | GET /platforms |
Staff | releases.releases.read |
| Create a platform | POST /platforms |
Staff | releases.release_catalog.manage |
| List depots | GET /depots |
Staff | releases.releases.read |
| Create a depot | POST /depots |
Staff | releases.release_catalog.manage |
| Update a depot | PATCH /depots/{depot_id} |
Staff | releases.release_catalog.manage |
| List builds | GET /builds |
Staff | releases.releases.read |
| Create a build | POST /builds |
Staff | releases.builds.upload |
| Read a build | GET /builds/{build_id} |
Staff | releases.releases.read |
| Check which chunks to upload | POST /builds/{build_id}/chunks/check |
Staff | releases.builds.upload |
| Register a manifest | POST /builds/{build_id}/manifests |
Staff | releases.builds.upload |
| Set a build's manifest for a depot | POST /builds/{build_id}/depots |
Staff | releases.builds.upload |
| List a build's depots | GET /builds/{build_id}/depots |
Staff | releases.releases.read |
| List a build's installers | GET /builds/{build_id}/installers |
Staff | releases.releases.read |
| Finalize a build | POST /builds/{build_id}/finalize |
Staff | releases.builds.upload |
| Revoke a build | POST /builds/{build_id}/revoke |
Staff | releases.builds.revoke |
| Read a manifest | GET /manifests/{manifest_id} |
Staff | releases.releases.read |
| List a manifest's files | GET /manifests/{manifest_id}/files |
Staff | releases.releases.read |
| List a depot's patches | GET /depots/{depot_id}/patches |
Staff | releases.releases.read |
| Register a patch | POST /depots/{depot_id}/patches |
Staff | releases.builds.upload |
| Purge unreferenced content | POST /storage/purge |
Staff | releases.release_storage.purge |
5.10.2.1.3 Contract v1¶
Proposed version
v1 is a proposal for review: its endpoints, errors and capabilities were designed from the requirements and the flows and are not confirmed. It leaves proposal with its first release; from then on it changes only as described in Every API Is Versioned.
5.10.2.1.4 Rules Confirmed with the Implementation¶
Decided on 2026-11-05, from the choices made while building releases:
- Manifest files come from the manifest itself: when
releasesvalidates an uploaded manifest it fills its files from it; no input carries them. - An application's slug is unique across projects, so the download page is addressed by it alone.
- Only an application marked
carries_adapters, the farm sync client, may declare the adapters its builds carry; a build of any other application that declares some is a 422. - Installers: every build has a single installer file per platform (decided 2026-11-11), the installer of
releasesmade for the build's application when the build becomes ready (ReleaseInstallerArtifact). It embeds the application, the build and the root metadata of every listed channel of the application at that moment, so it verifies what it installs from a pinned root and needs no anonymous root path, and the hosts of the environment with what stands in front of them, so one base installer serves every environment (decided 2026-11-13, Installer and Client Library); it carries no secret, and the person signs in through the browser when it runs. Executables stay unsigned for now (docs PR 109). The download links give it.
5.10.2.1.5 Uploading a Version from the Console¶
A member of staff with releases.builds.upload uploads a new client version from the Console, in the browser or on the desktop (decided 2026-11-11). CI keeps publishing with releases-publish, through the Console's host too (below); staging and the two-person approval do not change, and whoever created the build cannot approve its release.
- The person chooses the application, the version label and a folder for each platform and architecture, with the browser's directory picker.
- The Console packs each folder itself with
releases-client, featurepack, the same code asreleases-publish: FastCDC chunks, BLAKE3 or SHA-256 hashes, zstd compression and the manifest of format 1. Compiled towasm32-unknown-unknown, it needs the C library of zstd built for WebAssembly with clang, through thezstdcrate, at the same version and level asreleases-publish. A pure-Rust encoder was not chosen: it would store chunks as other bytes than CI does, and a manifest recordssize_storedfor chunks that are deduplicated by their uncompressed hash.blake3andfastcdcbuild for WebAssembly as they are. - It creates the build, asks which chunks to upload, and uploads chunks and manifest blobs straight to the signed storage URLs from the browser. This needs CORS on the content bucket for the Console's host (
console.moonitoring.com,stage-console.moonitoring.com), a change ofmoonitoring-infra. console-servercarries only the small JSON calls (create, check, register, finalize). No call of the upload needs a body above Cloud Run's limit of about 32 MiB: chunks, manifests and the installer files never pass through it.- On finalize,
releasesmakes the build's installer of each platform. The Console then shows copy buttons for each version link and for the application's universal latest link, next to each build and each release.
CI uploads through the Console's host as well, with no key (decided 2026-11-13). releases-publish calls the same operations at https://<env>-console.moonitoring.com/api/releases (https://console.moonitoring.com/api/releases in prod), never at the host of releases:
Proxy-Authorization: Bearercarries a Google ID token of the CI's Google service account, for an audience the Console's IAP admits; IAP lets it in and passesconsole-serverits assertion.x-moonitoring-consoleis on every call, as the Console's app sends it;console-serverrefuses a call without it.- No
Authorizationand no staff token in the CI:console-serverexchanges the assertion ataccessfor the staff token of the staff service account linked to that Google service account (Service Accounts That Sign In Through IAP) and forwards each call to the private URL ofreleaseswith it. Chunks and manifest blobs go straight to the signed storage URLs, as from the browser. - The staff service account holds
releases.builds.uploadandreleases.releases.read, and nothing else (decided 2026-11-13).releases-publishfirst reads the applications, depots and builds (GET /applications,/depots,/applications/{application_id}/depots,/builds/{build_id}and/builds/{build_id}/depots), which needreleases.releases.read. It does not holdreleases.releases.stage, so CI creates and finalizes builds but never stages them: a person does, and a second one approves.
5.10.2.1.6 The Manifest, Format 1¶
A manifest's blob is the full file list of one depot, chunked so that an update downloads only what changed (decided 2026-11-10, implemented by releases-publish and read by the farm sync client). The Builds API keeps the blob opaque; CI writes it and the client reads it. Format 1 (format_version: 1) is the canonical JSON (keys sorted, no whitespace) of:
{"chunk_hash":"blake3",
"chunking":{"algorithm":"fastcdc","avg_size":65536,"hash":"blake3","max_size":262144,"min_size":16384},
"files":[{"chunks":[{"compression":"zstd","hash":"<64 hex>","size_raw":70000,"size_stored":31000}],
"flags":["executable"],"hash":"<64 hex>","path":"bin/farm-sync.exe","size":70000}],
"format_version":1,"total_size_raw":70000}
- Files are sorted by
path(relative,/); a file is itschunksin order, and itshashis of the whole file. - Chunks are cut with FastCDC using the depot's
chunking_params, so a changed file shares most chunks with the build before. A chunk'shashis of its uncompressed content, with the depot's hash (blake3orsha256); it is stored zstd-compressed when that is smaller, otherwise as it is (none). - The manifest's
content_hashis the SHA-256 of the blob. The channel's targets document names the blob<depot id>/<content_hash>.manifestwith its length and SHA-256, so a client checks the blob against the signed chain before it trusts any hash in it, then each chunk and each file against the blob. - A chunk is downloaded from the update check's
chunk_base_urlwith its hash appended to the path, keeping the query of the signed prefix. The base is a short signed URL of the CDN in prod and in stage alike (decided 2026-11-11), so it is never empty. - Encrypted depots and patch artifacts are not in format 1.