build turns your Python package into the flat, import-less namespace the platform’s sandbox executes, and
writes it into the definition file’s code port in place. It is the compiler step of
deploy, available on its own.
It makes no network calls of any kind. It never resolves a blob, never lists definitions, and never resolves
a credential — build runs in a checkout with no API key, no credentials file, and no connectivity. That is
what makes it usable as a pre-commit hook and as the first, cheapest step in CI.
Synopsis
What it does
For each manifest entry that declarescomponents[] — an entry with no bindings is skipped entirely, since
there is nothing local to compile into it:
- Reads the entry’s
sourcedocument. A missing file isMANIFEST_INVALID, hinting atpull. - Resolves the entry’s category from the document’s own
type, by the same rule every other command uses, so a manifest cannot build clean and then fail on the first command that talks to the platform. - Binds each component by
idor uniquename, and for acodebinding: checks the component’slanguageport ispython, runs the remote-edit gate below, compilesentry_pointunderbase_path, and writes the resultingcontent[]into thecodeport. - For a
value: {source}binding, reads that JSON/YAML file and writes it into the component’svalueport. - Writes the document back to
sourcein its own format, and prints one line per definition:
--json the payload carries built, planned and dry_run alongside the envelope’s schema_version.
There is no drift key here, unlike every other command in this group: build never lists the revision, so it
has nothing to compare the manifest against.
Deterministic by construction
Compilation is a pure function of the sources. The compiled content is hashed (SHA-256 over its canonical JSON) and that hash becomes both the port’scontent_hash and, through a uuid5 derivation, the message’s id. The
writer touches no other key and reorders nothing.
So a rebuild over unchanged sources leaves the definition file byte-identical, and git status stays clean.
That is the property that makes the compiled code reviewable: a diff in the definition file means a real change
in the package.
One exception, once per port: created_at is carried forward from whatever is already in the file, and stamped
with the current time only when there is nothing to carry. A code port the CLI has never built therefore changes
on its first build and is stable from the second onward.
The remote-edit gate
Before overwriting a code port that already has content,build classifies it:
--force skips the classification entirely. The other way out is blobhub workflow eject, which brings the
edited code onto disk so you can reconcile it by hand first.
A refusal stops the whole command, not just that definition. Definitions built earlier in the same run keep the
content they were given — the file is written per definition, as each one finishes.
The gate reads the code port in the local definition file, because that is the only document build has.
An edit made in the visual editor reaches that file when you pull;
diff is what reports the remote’s state without pulling. Run one of the two
before a build you intend to deploy.
Imports and the allowlist
The compiler classifies every import as local, sandbox pre-bound, blob-allowlisted, or unavailable. The allowlist — a blob limit — needs anadmin-scoped key to read, which build does not have and could not use
offline anyway, so it reads the local cache under ~/.blobhub/cache/allowlist/, keyed on the manifest’s own
blob: reference.
With no cache entry, ALLOWLIST_UNREADABLE is raised as an advisory and an unresolved import degrades from the
hard failure IMPORT_UNAVAILABLE to the advisory IMPORT_UNVERIFIED — the build proceeds and says it could not
verify. --strict-imports promotes that back to a refusal, which is what a CI job should set. A cache entry
older than seven days additionally raises ALLOWLIST_STALE. Two commands write that cache, both needing an
admin key: deploy, which refreshes it live on every run, and
blobhub blob limits --refresh.
Everything that does not depend on the allowlist is enforced regardless: local module resolution, import cycles,
symbol collisions, and names the sandbox pre-binds.
The classification rules, the import-rewrite table and the compiler’s documented limitations are in the
Compiler reference; what the allowlist extends — the pre-bound modules and names — is
in the Sandbox reference.
--definition here is not the same check
Eight commands in this group take --definition; the seven other than build resolve it while loading the
revision’s definition set, and reject an alias the manifest does not carry. build filters the manifest list
directly, so an alias that matches nothing is a silent no-op: no error, no output, exit 0. Check the
spelling against the manifest if a build reports nothing built.
--dry-run
Reads each document and resolves its category, then reports what it would build:
Errors
No credential or transport code can surface here, because no call is made. If
build reports
CREDENTIALS_NOT_FOUND, you are running a different command.
Every code above, with its remediation, is in Error codes.
See also
blobhub workflow deploy— the same compile, followed by the upload and the check.blobhub workflow diff— compiles in memory and reports, writing nothing.blobhub blob limits— the import allowlist and the key that can read it.- Component code — what the sandbox binds and what it executes.

