Anvil Media commands¶
Product families · CLI overview · Control Plane & Fleet
anvil-serving media is the bounded operator path for named image and video
workflows, exact worker bundles, durable jobs, qualification, cancellation,
and opaque artifacts. The CLI and the gateway's MCP/A2A projections use the
same operation contracts.
The gateway never accepts a caller-supplied ComfyUI graph, node name, model filename, server filesystem path, install request, backend route, host selection, or fallback list. Operator configuration maps one workflow version to one media service and resource owner.
Choose a journey¶
| Goal | Start here | Then |
|---|---|---|
| Discover callable and blocked workflows | media capabilities |
Inspect the exact version with media workflow show. |
| Prepare a media worker | media bundle inventory |
Preview missing exact assets with media bundle stage --dry-run; apply only with --confirm. |
| Check workflow compatibility | media workflow validate |
Keep the workflow unavailable when any required feature, node, model, or backend fact is unknown. |
| Compile a shot brief | media workflow prepare |
Inspect exact run arguments; submit them unchanged through existing admission. |
| Provide a reference | media asset upload --dry-run |
Confirm the same bounded local file; bind its returned owned artifact ID. |
| Run a named workflow | media workflow run ... --dry-run |
Review the binding, then submit the same request with --confirm. |
| Follow or cancel work | media job status |
Use media job cancel --dry-run, then --confirm, for the same principal. |
| Inspect a result | media artifact inspect |
Fetch bytes only through the authenticated gateway artifact boundary. |
| Qualify a blocked workflow | media qualify run ... --dry-run |
Apply with --confirm, retain evidence, and require independent quality review. |
Discover capabilities¶
Discovery includes workflow categories and an optional operator-owned recipe
catalog. discovered, static-reviewed and needs-qualification entries are
research candidates; they cannot execute merely because their source JSON
parsed. A ready catalog entry must name an available registered workflow.
UI workflow imports still require a reviewed API export and local qualification.
anvil-serving media capabilities
anvil-serving media workflow list
anvil-serving media workflow show image.flux2-klein-4b-fp8-v1 --version v1
Capability discovery reports stable workflow ids, versions, parameter schemas,
explicit quality profiles, limits, and availability reasons. available=true
means the exact deployed workflow passed its declared functional, capacity,
and independent quality gates. It does not mean every prompt is guaranteed to
meet a perceptual goal, and it does not set promoted=true automatically.
--registry, --state-db, and --artifact-root select operator-owned state.
Public packaged workflow descriptors remain templates and pinned defaults; real
deployment values belong in the private operator home.
Prepare a shot request¶
media_workflow_prepare accepts an explicit workflow/version, a stable
idempotency_key, optional parameters, quality_profile, and a structured
brief. media_workflow_show exposes each recipe's supported brief fields,
literal prompt template, reference roles and bounded parameter schema. The
server compiles those fields into declared bindings. It uses no LLM and does
not infer identity mapping, invent unsupported controls or choose another model.
Dialogue is inserted verbatim; unsupported brief fields and profile overrides
fail before submission.
anvil-serving media workflow prepare '<WORKFLOW>' --version '<VERSION>' --principal caller-1 --idempotency-key shot-01 --brief '{"scene":"Three hosts in a studio.","camera":"Wide shot."}'
Select a workflow that declares a prompt template and replace the quoted
placeholders with its advertised id and version. The result contains
runArguments and a digest covering the owner, complete workflow contract,
rendered graph, resolved parameters and request key.
Preparation checks owned reference metadata but does not contact/start the
worker, create a render job, or establish backend readiness. Call
media_workflow_validate with only workflow_id and version, then pass
runArguments unchanged to media_workflow_run. A changed/stale prepared
request fails with media_plan_changed. Existing direct submissions remain
supported and retain all validation/admission gates.
CLI callers can pass compiled parameters with media workflow run and its
--plan-digest; the MCP uses the same compiler and renderer. References are
owned opaque artifact IDs in declared slots, never caller filesystem paths or
node IDs. An image input does not imply face replacement, a speaker map or
lip synchronization; the chosen recipe must declare and qualify those roles.
Upload reference media¶
When MCP capabilities report assetUpload.available=true, use the existing
protected MCP connection definition to upload a local PNG, JPEG, MP4 or WAV:
anvil-serving media asset upload --file reference.png --client-config connection.json --dry-run
anvil-serving media asset upload --file reference.png --client-config connection.json --confirm
The client discovers the intake policy, hashes the file and sends its bytes to
POST /media/assets on the configured gateway origin. It reuses the protected
credential source and runs on the machine containing the local file, without
resource-owner dispatch to the gateway host. It sends neither server paths nor
credentials in tool
arguments or URLs. The raw intake route requires media:submit, one strict
Content-Length, a supported Content-Type and X-Content-SHA256. It does not accept
chunked/compressed bodies, remote source URLs or signed download authorization.
Retries create new owned input artifacts; they never submit generation jobs.
Intake permits at most32MiB and retains inputs for24hours. The managed gateway
must include ffprobe; otherwise intake stays unavailable. It checks signatures,
actual decoding, stream count, dimensions, decoded frame count, duration and
audio channels/rate. Decoder calls have deadlines, one thread, a file/pipe
protocol whitelist and disabled external MOV tracks, following the
FFmpeg demuxer controls.
The global RGB-float32 extent bound is2GiB, with at most4096pixels per axis,
3600frames,120seconds, two audio channels and96kHz. This tensor estimate is not
a prediction of the complete generative runtime's memory requirement.
An imported reference can enter only a recipe declaring tighter applicable
media_limits for that artifact parameter; these appear as
x-anvil-media-limits in its schema. Existing generated outputs remain usable
by legacy recipes. Ownership, expiry, content hash and per-recipe bounds still
apply at submission. Unsupported audio/reference roles are rejected. Importing
three face images does not establish subject tracking, identity replacement or
three-speaker support. Original audio bytes remain a separate retained input;
native video saving can trim/re-encode audio and needs a separately qualified
master-preservation step.
Prepare the worker bundle¶
For private deployment registries, follow the operator-owned video workflow contract. Keep model selection, asset identities and qualification records in private operator storage.
Inventory verifies the exact workflow version, lock file, and protected model volume without writing:
For an existing host directory or downloaded snapshot cache mounted read-only
into the worker, inventory and qualification accept --models-root <PATH>
instead of --models-volume. Locked targets must resolve inside that root;
missing files and size/hash mismatches stay unavailable. This path reads files
without creating volumes, installing helpers or copying/downloading weights.
It does not prove which file a running loader selected: separately verify the
declared read-only mount, model search paths and loader filenames. The lock's
canonical graph digest must match the selected qualification workflow before
any backend or GPU probe. Staging continues to require a named volume.
Stage only the missing, digest-pinned assets after reviewing the same plan:
anvil-serving media bundle stage <WORKFLOW> \
--version <VERSION> \
--models-volume <VOLUME> \
--user-volume <VOLUME> \
--runtime-uid <UID> \
--runtime-gid <GID> \
--dry-run
anvil-serving media bundle stage <WORKFLOW> \
--version <VERSION> \
--models-volume <VOLUME> \
--user-volume <VOLUME> \
--runtime-uid <UID> \
--runtime-gid <GID> \
--confirm
Staging never replaces an existing mismatched file. A mismatch is a refusal, not permission to repair by guessing or downloading another revision.
Inventory uses only a cached, digest-pinned verification image. If that image
is missing, staging preview reports helperPullRequired and defers asset
inventory. Confirmed staging pulls the exact helper and checks existing files
before writing to either volume; unknown files are never treated as missing.
Validate one named workflow¶
anvil-serving media workflow validate image.flux2-klein-4b-fp8-v1 \
--version v1 \
--backend-url http://127.0.0.1:8188
Validation compares the immutable descriptor with bounded worker feature, node, and model inventories. Backend readiness is transport evidence only. It does not prove capacity, perceptual quality, or promotion.
Preview and submit a workflow¶
The request binds only descriptor-declared parameters. Quality profiles are locked parameter sets inside the same workflow; they cannot select another model, worker, backend, or provider.
anvil-serving media workflow run image.flux2-klein-4b-fp8-v1 \
--version v1 \
--quality-profile standard \
--parameters '{"prompt":"a forged steel anvil","seed":7}' \
--principal caller-1 \
--backend-url http://127.0.0.1:8188 \
--dry-run
Submit the identical reviewed request with --confirm. Supply an
--idempotency-key when a caller may retry. An accepted job is durable and
continues across caller disconnects. If the declared worker is cold, ordinary
submission does not silently start it: the job remains awaiting_approval
with the exact managed lifecycle preview unless an already-reviewed private
policy authorizes the existing transaction.
Inspect and cancel durable jobs¶
anvil-serving media job status <JOB_ID> --principal caller-1
anvil-serving media job cancel <JOB_ID> \
--principal caller-1 \
--backend-url http://127.0.0.1:8188 \
--dry-run
anvil-serving media job cancel <JOB_ID> \
--principal caller-1 \
--backend-url http://127.0.0.1:8188 \
--confirm
Status derives phase and end-to-end latency from ordered durable events.
For a standalone CLI submission without a gateway reconciliation daemon, add
--backend-url with the same endpoint used for submission. This refreshes only
that principal-owned job from backend history and retains completed artifacts:
Repeat while the job is queued or running. Refresh never submits another prompt or starts a worker. Without the option, status reads the stored snapshot. Submission records a private endpoint digest before sending the prompt; refresh rejects a different endpoint before contacting it. Older jobs without that binding support stored status only. Phase times are observation times; a delayed refresh is not engine latency.
Cancellation is principal-scoped, idempotent, and reconciled with the selected backend. It never cancels unrelated backend work by filename or queue position.
Background completion¶
Run a watcher on the harness host after MCP submission returns a job ID:
The protected connection definition contains the existing stdio MCP command,
args, and optional env. It refers to the existing credential file; no
credential is added to the command line. The watcher calls only
media_job_status and media_artifact_inspect. It emits state changes and a
final JSON record containing completed artifact metadata and any download links
returned by the gateway. A gateway without link support returns relative
resources; the watcher never invents a URL.
Polling defaults to five seconds, with a two-hour watcher deadline.
In Pi, use its installed bg_run with isAgent:false,
notifyOnCompletion:true, and triggerOnCompletion:true. Set that task's
timeout at least 30 seconds longer than the watcher deadline for client cleanup.
Keep the background registration
receipt in the conversation, then continue independent work or end the turn.
Completion wakes a follow-up turn in the same open Pi session; no manual
"check" prompt is needed. See the anvil-media-watch companion skill.
Exit 0 means completed; exit 1 means a failed/canceled job or observation error; exit 3 means operator approval is required; exit 4 means the watcher deadline expired. A watcher deadline or shutdown never cancels rendering. After a session restart, reattach to the saved job ID. The watcher cannot notify a closed Pi session and never starts workers or resubmits a job.
Inspect opaque artifacts¶
Artifact records contain bounded metadata and provenance. Backend output paths
are never public identifiers. Full bytes are delivered through the
authenticated /artifacts/{opaque-id} boundary; eligible small images may
also be returned as bounded native MCP image content. Video is resource-only.
Through a gateway with media_public_origin configured, media_artifact_inspect
also returns artifact.downloadUrl for caller-owned artifacts. MP4 results
include a native MCP resource link to the same URL. By default,
requiresAuthentication: true means HTTP GET/Range requires the existing MCP
credential in the Authorization header. Each response remains bounded by
artifact.maxRangeBytes (16 MiB); larger files require sequential bounded
Range requests. A full GET above that limit returns 416.
For direct browser playback, an operator can set ANVIL_MEDIA_SIGNED_DOWNLOADS=1
on a dedicated gateway using static MCP authentication (device-key policy is
unsupported and rejected at startup). Inspection then returns
requiresAuthentication: false, downloadAuthorization: "signed-url" and
downloadExpiresAt. This URL authorizes reads of that one artifact for at most
one hour, capped by artifact expiry; it never contains the reusable MCP key.
Anyone holding it can read the video until expiry, so keep it private. Key
rotation revokes outstanding links. Inspect the artifact again to refresh an
expired link. This mode supports full GET, HEAD and one fixed, open-ended or
suffix byte range, streaming in bounded chunks. It does not authorize MCP calls
or other artifacts. No network exposure or tailnet configuration is changed.
The client verifies downloaded size and SHA-256 against inspection metadata.
The URL
uses the configured origin, never a caller-supplied Host header. Without an
origin, inspection keeps the relative resource path instead of guessing a URL.
Cross-principal inspection also keeps the relative path; its metadata scope
does not grant cross-principal HTTP downloads.
Use an owned result as workflow input¶
A descriptor may declare a parameter with kind: "artifact" and optional
media_types, such as ["video/mp4"]. Pass the opaque artifact ID returned by
a completed job. Before submission, the shared operation checks ownership,
expiry, declared MIME types and upload size; it verifies the content SHA-256,
uploads the bytes to the selected worker, and binds the returned input name
only inside the private execution graph. Public requests and job records keep
opaque IDs. An idempotent replay returns the existing job without uploading or
submitting it again, including after the original input expires.
Input handoff currently supports PNG and MP4 artifacts, bounded by 32 MiB and the workflow's artifact-byte limit, whichever is smaller. It does not accept a caller filesystem path or import an arbitrary local recording. Long recordings must first be cut into supported, bounded shots by the owning application; external-media ingestion is a separate integration boundary.
Qualify without promoting¶
anvil-serving media qualify run image.flux2-klein-4b-fp8-v1 \
--version v1 \
--quality-profile standard \
--parameters '{"prompt":"qualification sample","seed":7}' \
--principal qualification \
--backend-url http://127.0.0.1:8188 \
--models-volume <VOLUME> \
--dry-run
Apply with --confirm only on the declared worker. The qualification captures
functional artifact checks and bounded capacity evidence. Perceptual quality
must be decided by a human or separately administered evaluator that did not
generate the artifact. A successful run does not change availability or
promotion state by itself.
Lifecycle and integration boundary¶
Windows host memory guard¶
For a guarded Windows/Docker deployment, copy the operator-owned
media-guard.toml policy into the operator home. Give lease_path a dedicated
subdirectory beneath that home. Defaults retain the measured trial safeguards:
stop below 4 GiB available Windows RAM, below 16 GiB commit headroom, or below
8 GiB available RAM for 15 seconds. The worker also stops when the host signal
is more than five seconds old or its 45-minute lifetime expires.
anvil-serving media memory-guard sample
anvil-serving media memory-guard install --confirm
anvil-serving media memory-guard status
Installation starts a hidden, small Windows publisher and registers it in the current user's login startup entries. It reads native physical-memory and commit counters; it does not load a model or operate other services. Its global mutex prevents duplicate publishers across login sessions. The dedicated signal directory has private ACLs, with existing explicit permissions reset. Its policy and implementation digest must match the worker's expectations. The digest includes both guard and worker-runtime enforcement modules. After bounded Windows file-replacement retries fail, the publisher retries a fresh sample on its next iteration; the previous signal still expires normally.
The Linux worker's fixed entrypoint uses memory-guard supervise --confirm
with its declared ComfyUI command after --. Mount the signal directory
read-only and map its path with --lease; never mount credentials in the
worker. Unsafe, missing, invalid or stale signals prevent startup or immediately
terminate only the supervisor's own child process group. The lifetime limit is
for the worker container, including idle time, rather than an individual shot.
Normal job-owned teardown remains the lifecycle owner's responsibility.
A reviewed worker image can declare anvil.media.runtime-reservation.v1.
Its operator policy also sets admission_seconds and
admission_margin_seconds; their sum must be less than timeout_seconds.
For example, a7200-second lifetime may reserve2700seconds plus30seconds of
margin for each admitted job. Choose the reservation from the allowed recipes'
qualified envelope. This reserves time; it does not guarantee a stalled render
will finish or turn the descriptor timeout into a hard per-job deadline.
The supervisor sets one fixed monotonic deadline before starting the child.
The image must wire the supplied runtime helper into native queue insertion
under its existing lock, advertise the feature, and expose bounded
system.anvil_runtime status. Installing the helper alone does not enforce
native queue admission. Status is a receipt-time snapshot; the final queue
check is authoritative. That check refuses unknown/changed instances, a busy
queue, insufficient remaining time or a requested workflow timeout above the
approved reservation. Requests never reset the worker lifetime.
Require the feature in descriptors using that contract. The adapter checks it again after reference uploads and sends the expected worker instance. Only an exact correlated pre-enqueue refusal becomes an unambiguous failed job; a lost or malformed submission response retains existing durable recovery without a second submission. History, status, cancellation and output retrieval remain usable when new admission closes. Restarting an expired worker still requires the existing owner-controlled lifecycle procedure.
install and uninstall preview unless --confirm is supplied. To update a
guard policy or implementation, run uninstall --confirm, allow the publisher
to report guard stopped, then reinstall. Uninstall removes only the matching
login entry and requests its publisher to stop; it preserves its files. A
running worker will stop when its host signal becomes unsafe. Foreground
run --confirm is available for diagnosis and writes an unsafe signal on exit.
Connect Codex to the media MCP¶
Register a dedicated connection to the authenticated gateway after the operator has installed its configuration and protected token file:
codex mcp add anvil-media -- anvil-serving mcp serve --controller-url http://127.0.0.1:8189/mcp --auth-file /protected/media-token
Use the installed gateway URL and token-file path for your machine. The file
contains one UTF-8 token line and uses an absolute path. The bridge checks the
opened file's ownership and permissions before and after reading. On POSIX,
only the current user or root may own it and group/other permissions must be
absent. On macOS, extended ACLs on the file or any ancestor are rejected;
native macOS acceptance remains unverified. On Windows, access must be limited to the current user, SYSTEM, and
Administrators. Missing permission information fails closed.
Its contents are never stored in Codex settings or
process arguments. --auth-env remains supported when the client already has
the credential in its environment. The bundled bridge requires Node.js 20+ and
the same Anvil version as the gateway. Start a new Codex session after adding
the connection so its tools can be loaded.
A dedicated media gateway may use an empty [router] table with a complete,
authenticated [server] media declaration (auth_env, media_principal,
media_scopes, and media_public_origin). Set its explicit
ANVIL_MEDIA_BACKEND_URL and private state/artifact locations in the managed
deployment. No chat route or chat-model serve is required. An ordinary empty
router without that media declaration remains invalid.
Verify the connection by listing the nine media_* tools, validating an
available workflow, submitting a named image workflow, polling its job to
completion, and inspecting the resulting artifact. The image content digest
must match the artifact metadata. Wrong-token and cross-principal access must
be refused. The connection does not grant model installation, worker lifecycle,
or workflow-promotion authority; a cold worker still needs its managed operator
startup path.
The resource-owning host operates ComfyUI through managed serves and
controller tools; raw Docker is not the operational path. The caller-facing
media catalog excludes lifecycle and general operator tools. To reconcile the
packaged media-only skill and nine-tool allowlist into selected Hermes
profiles, use anvil-serving harness sync hermes-media --dry-run and review
the Control Plane & Fleet reference.
See ADR-0040 for the gateway/controller authority split and ADR-0041 for the initial workflow and qualification policy.