SURFACE.md §4 quote (lines 435-438)
flows build seals a flow into a content-addressed, immutable bundle: canonical spec JSON, compiled TS with pinned deps, helper/plugin lockfile, assets, preflight declaration, identity signature — flow@sha256:…, pushed to a bucket/registry. flows deploy points a trigger at a digest; flows run flow@sha256:… executes from the bucket on any cell, no checkout. Preflight runs at build time for everything build-provable and again at deploy time for environment facts (credentials, workers, MCP servers). The working tree is for authoring; production only ever runs digests.
Depends on
Scope
flows deploy <flow-name>@<digest> --to <bucket-uri> — uploads the local bundle to a content-addressed bucket.
- First-slice URI schemes:
file:// (local filesystem) + one S3-compatible URI (e.g. s3://bucket/prefix). Additional schemes are follow-ups.
- Deploy is idempotent: re-deploying the same digest is a no-op with a
deploy_noop diagnostic on stderr.
- Deploy-time preflight runs (environment facts: bucket writable, credentials present).
flows run <flow-name>@sha256:<hex> — fetches the bundle from the configured bucket by digest, verifies its identity signature, executes.
--bucket <uri> explicit override; else uses flows.json deploy.bucket field.
- Bundle is cached locally under
$XDG_CACHE_HOME/flows/bundles/<digest>/ so a second run doesn't re-fetch.
- Trigger executor accepts a digest as its target. Configuration:
# flows.json or a trigger declaration
trigger:
kind: webhook
digest: sha256:abc...
Locks the run to that content — no working-tree fallback.
Not in scope
- Content-signing key rotation (out-of-band ops task)
- Multi-bucket replication
- GC of old digests
- Bucket lifecycle policies
- The registry side of "bucket/registry" (SURFACE line 437 leaves that split ambiguous — start with pure bucket)
Acceptance
Files (likely)
packages/sdk/src/cli/deploy.ts (new)
packages/sdk/src/bundle-transport.ts (new — file:// + s3:// scheme handlers)
packages/sdk/src/cli.ts (wire deploy verb; wire run <flow>@sha256:... digest input path)
packages/sdk/src/cli/run.ts (add digest input handling parallel to <flow.yaml|spec.json> + <flow.ts>)
packages/sdk/tests/deploy.test.ts (new)
packages/sdk/tests/run-from-digest.test.ts (new)
Written by codex agent spec-Adep-flows-deploy — deployed after slice A's #316 merge.
SURFACE.md §4 quote (lines 435-438)
Depends on
flows build) merged (2026-09-11). Bundle format is defined; this issue makes it deployable.Scope
flows deploy <flow-name>@<digest> --to <bucket-uri>— uploads the local bundle to a content-addressed bucket.file://(local filesystem) + one S3-compatible URI (e.g.s3://bucket/prefix). Additional schemes are follow-ups.deploy_noopdiagnostic on stderr.flows run <flow-name>@sha256:<hex>— fetches the bundle from the configured bucket by digest, verifies its identity signature, executes.--bucket <uri>explicit override; else usesflows.jsondeploy.bucketfield.$XDG_CACHE_HOME/flows/bundles/<digest>/so a second run doesn't re-fetch.Not in scope
Acceptance
flows deploy hello@sha256:abc --to file:///tmp/bucketcopies the bundle to/tmp/bucket/hello/sha256/abc/with a verifiable layout.flows run hello@sha256:abc --data-dir /tmp/dirfetches fromfile:///tmp/bucket, verifies signature, executes tocompletionReason: success.bundle_signature_invalidrefusal (exit 2) before any journal write.flows.jsonwithdeploy.bucket: file:///tmp/bucketand a trigger configdigest: sha256:abclocks the run to that content — attempting to run at a different digest is refused.Files (likely)
packages/sdk/src/cli/deploy.ts(new)packages/sdk/src/bundle-transport.ts(new — file:// + s3:// scheme handlers)packages/sdk/src/cli.ts(wiredeployverb; wirerun <flow>@sha256:...digest input path)packages/sdk/src/cli/run.ts(add digest input handling parallel to<flow.yaml|spec.json>+<flow.ts>)packages/sdk/tests/deploy.test.ts(new)packages/sdk/tests/run-from-digest.test.ts(new)Written by codex agent
spec-Adep-flows-deploy— deployed after slice A's #316 merge.