Triggers a native build — an image, or the binaries a repo declares.
Triggers a native build — an image, or the binaries a repo declares. The fabric's own build trigger, and what `hanzo build` and git-push-to-deploy call.
POST /v1/platform/runner
| Address | https://api.hanzo.ai/v1/platform/runner |
| Method | POST |
| Operation | post_platform_runner |
| Auth | Authorization: Bearer $HANZO_API_KEY |
Triggers a native build — an image, or the binaries a repo declares.
The fabric's own build trigger, and what hanzo build and git-push-to-deploy
call. It answers 202 with the build job id: a queued build, not a pushed
artifact.
Two lanes, and a build is exactly one of them. The IMAGE lane takes repo and
the output image and launches a BuildKit Job that pushes it. The ARTIFACT lane
takes binaries — the same recipe the repo's hanzo.yml declares — and publishes
to object storage instead; it must carry no image, because a build produces
binaries or an image, never both.
PRIVILEGED, and A BUILD BELONGS TO THE ORGANIZATION ITS CREDENTIAL NAMES. Two credentials, never a third:
- one that NAMES an organization — a person who administers it (the
hanzo buildpath, so one IAM login authorizes a build with no separate build token), or that organization's own machine identity (the pipeline path). The build is attributed to that org and confined to what it owns. - the shared build-callback token, compared in constant time. It names NO organization, which is both why the fabric's own release can publish across brands with it and why anything that CAN name one is read first.
Both are bounded by the owned-registry allowlist. The org path is bounded again, by the org: the image's registry namespace must be one that organization owns, so it publishes into its own brand and can never overwrite another's through the shared push credential. The same confinement applies to the artifact lane's repo owner. There is no request field naming an organization — the attribution is read off the credential, so there is nothing for a caller to write it with.
The output image is parsed and validated as a single well-formed OCI ref before any authorization decision reads it, so a crafted ref cannot smuggle a build-exporter attribute past the check.
Request
22 fields, body application/json (required).
| Field | In | Type | Required | Description |
|---|---|---|---|---|
arch | body | string | — | Arch is the target architecture for the artifact lane. |
args | body | object | — | Args are --build-arg values. They are what lets several images off ONE Dockerfile mean different things — the sandbox classes are three entries differing only… |
args.* | body | string | — | |
binaries | body | binarySpec[] | — | Binaries selects the ARTIFACT lane (artifact.go): build what the repo's hanzo.yml binaries: block declares — a Go binary, an npm tarball, a Rust binary — and… |
binaries[].image | body | string | — | Image is the toolchain image the recipe runs in, a Go bookworm image by default. |
binaries[].ldflags | body | string | — | Ldflags are the Go linker flags, -s -w when the recipe names none, on one line. |
binaries[].main | body | string | — | Main is the Go package to build, repo-relative (. or ./cmd/x), and it selects the GO LANE. |
binaries[].name | body | string | — | Name is the artifact's base name: the prefix of every file published for this entry, and the name a host later asks for. |
binaries[].out | body | string | — | Out is the glob of files run produced, relative to the repo root; matching nothing FAILS the build rather than publishing an empty entry. |
binaries[].platforms | body | string[] | — | Platforms are the <os>/<arch> pairs the Go lane cross-compiles, [linux/amd64] by default. |
binaries[].run | body | string | — | Run is any other toolchain's build command, run by sh -c in this entry's image, and it selects the OTHER LANE. |
branch | body | string | — | Branch is the branch to build when no SHA or Ref is given. |
bucket | body | string | — | Bucket mirrors hanzo.yml's bucket: — where the artifact lane publishes. |
context | body | string | — | Context is the build context path within the repo. |
dockerTarget | body | string | — | DockerTarget is the multi-stage build target to stop at. |
dockerfile | body | string | — | Dockerfile is the path to build from; empty uses the zero-config frontend. |
image | body | string | — | Image is the output image ref to push. |
os | body | string | — | OS is the target operating system for the artifact lane. |
ref | body | string | — | Ref is the git ref to build when no SHA is given. |
repo | body | string | — | Repo is the repository clone URL to build. |
sha | body | string | — | SHA is the commit to pin; it wins over Ref and Branch. |
tag | body | string | — | Tag is the publish path segment, so both front doors write ONE index at ONE URL. |
Response
| Status | Body | Meaning |
|---|---|---|
202 | runnerBuildResp | accepted |
202 body — 6 fields.
| Field | In | Type | Always | Description |
|---|---|---|---|---|
buildJobId | body | string | — | BuildJobID is the queued build's id, and what its progress is read by. |
image | body | string | — | Image is the ref the image lane will push. |
index | body | string | — | Index is the binaries.json URL the artifact lane will publish. |
runnerPool | body | string | — | RunnerPool is the runner class the build was placed on. |
status | body | string | — | Status is queued — the build was accepted and has not finished. |
target | body | string | — | Target is the multi-stage build target, echoed back. |
Failure carries the platform error shape — see Errors.
Examples
hanzo has no subcommand for this operation — the CLI serves only what cloud's live route table confirms. Use HTTP or an SDK.
import { Configuration, PlatformApi } from 'hanzoai';
const api = new PlatformApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.postPlatformRunner({ arch: "<arch>", args: {} });from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import PlatformApi
client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = PlatformApi(client).post_platform_runner(arch="<arch>", args={})cfg := cloud.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := cloud.NewAPIClient(cfg)
resp, _, err := client.PlatformAPI.PostPlatformRunner(context.Background()).Execute()
if err != nil {
return err
}use hanzo_cloud::apis::{configuration::Configuration, platform_api};
let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();
let result = platform_api::post_platform_runner(&cfg, Default::default()).await?;import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.PlatformApi;
ApiClient client = new ApiClient();
client.setRequestInterceptor(b -> b.header("Authorization", "Bearer " + System.getenv("HANZO_API_KEY")));
var result = new PlatformApi(client).postPlatformRunner();The method above is the one at the current release of the document. [email protected] (npm) and [email protected] (PyPI) were generated from an earlier release, where this operation carried a different id, so it spells the method differently — regenerating the clients is what makes the two agree. SDKs →
curl -X POST https://api.hanzo.ai/v1/platform/runner \
-H "Authorization: Bearer $HANZO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"arch": "<arch>",
"args": {}
}'The door reaches platform through the platform tool, which names its 39 operations with its own verbs — this one among them, under a name only the door declares. describe explains any of them:
curl -X POST https://api.hanzo.ai/v1/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "describe",
"arguments": {
"op": "list_builds"
}
}
}'How is this guide?