Workflow registry and remote runs
Publish immutable workflow packages to S3 and execute exact versions through tide serve.
The Tide workflow registry is a separate application boundary from the plugin
lock in tide.json. The registry stores complete, executable workflow packages;
the plugin lock inside a package records which statically available Tide plugin
hosts that workflow expects. Publishing a workflow does not install native
plugins into an already-built Tide binary.
Local development
The repository includes a loopback-only MinIO setup with deterministic development credentials and a private bucket initializer:
docker compose -f compose.registry-dev.yaml up -d minio
docker compose -f compose.registry-dev.yaml run --rm minio-initThen start the registry against it:
export AWS_ACCESS_KEY_ID=tide-dev-access
export AWS_SECRET_ACCESS_KEY=tide-dev-secret
export TIDE_REGISTRY_BUCKET=tide-workflows
export TIDE_REGISTRY_REGION=us-east-1
export TIDE_REGISTRY_S3_ENDPOINT=http://127.0.0.1:39100
export TIDE_REGISTRY_S3_PATH_STYLE=true
export TIDE_REGISTRY_TOKEN=tide-local-development-token
cargo run -p tide-registry --features s3Run ./scripts/registry-e2e-smoke.sh for the bounded local MinIO →
registry → tide serve vertical-slice validation. It uses isolated temporary
state and removes its processes, containers, and volume on exit. Set
TIDE_SMOKE_KEEP=1 only when retaining diagnostics is intentional.
Publish a directory. The entrypoint defaults to main.ts; tide.json,
tide.lock, generated plugin metadata, and imported local modules travel in the
same deterministic package.
export TIDE_REGISTRY_TOKEN=tide-local-development-token
tide publish \
--server http://127.0.0.1:4200 \
--name employment-onboarding \
--version 1.2.0 \
--entry workflows/onboard.ts \
examples/employment-onboardingThe command prints the exact name@version, SHA-256 digest, and size. --json
prints a machine-readable object. Re-publishing byte-identical content is
idempotent. Reusing the same name and semantic version for different bytes
returns 409 version_conflict.
Start Tide with both development-local and registry sources:
export TIDE_SERVER_TOKEN=tide-local-server-token
tide serve \
--workflows ./workflows \
--registry http://127.0.0.1:4200 \
--store fs \
--workers 1TIDE_REGISTRY_URL is the environment equivalent of --registry. Resolution
is unambiguous: an unversioned name uses --workflows; name@version uses the
registry and never falls back to disk.
Run and follow remotely:
export TIDE_SERVER_TOKEN=tide-local-server-token
tide run --server http://127.0.0.1:4100 \
employment-onboarding@1.2.0 \
--input '{"company":"Acme","name":"Ada"}'
tide follow --server http://127.0.0.1:4100 <invocation-id>
tide resume --server http://127.0.0.1:4100 <invocation-id> \
--action approve --comment "checked"Remote run submits the exact version, follows lifecycle changes, emits console
output once using log cursors, and uses the same terminal decision prompt as a
local HITL wait. --json is non-interactive and emits one result object.
Ctrl-C stops following only: the server invocation keeps running and the CLI
prints its ID and a reconnect command.
Local filesystem paths remain local. With --server, the positional argument
must be an exact registry reference; Tide never sends a local path and asks the
server to read the client's filesystem.
Package and cache integrity
The v1 media type is
application/vnd.tide.workflow.package.v1.tar. Archives are uncompressed,
sorted, and have normalized mode, owner, group, and timestamp fields, so
identical inputs produce identical bytes. The first-class
tide-workflow.json manifest declares the package version, media type,
workflow name, exact semantic version, entrypoint, and optional tide.lock
digest.
Publish and pull reject:
- absolute paths,
.., links, special files, duplicate entries, and a missing entrypoint; - unknown package versions/media types and invalid names or semantic versions;
- packages or expanded content over 128 MiB;
- size or SHA-256 mismatches.
tide serve scopes caches by registry origin under
.tide/workflow-cache/origins/<origin-hash>/. It retains verified package bytes
and stages/atomically renames extraction by digest. Every reuse compares the
extracted files with those package bytes, so local corruption is repaired
without trusting a completion marker. Concurrent pulls and crashes cannot expose
a partial cache. The invocation queue persists the resolved name, exact version,
and digest before execution; a queued or resumed invocation can use that pinned
verified package during a registry outage and refuses to run if its identity
changes.
Registry API and S3 layout
The stable API is:
GET /health
GET /v1/health
GET /v1/workflows?cursor=&limit=
GET /v1/workflows/{name}/versions?cursor=&limit=
PUT /v1/workflows/{name}/versions/{version}
GET /v1/workflows/{name}/versions/{version}
GET /v1/workflows/{name}/versions/{version}/artifactThe publish body is the package bytes with the v1 package media type. Lists are
stable and keyset-paginated: pass the returned exact cursor unchanged; a cursor
that is absent from the current result is rejected as invalid/stale. Cursors are
not signed or time-expiring tokens. The registry currently accepts exact
versions only; there is no mutable latest alias, so an invocation cannot drift
after submit. Errors use
{ "error": { "code", "message", "details"? }, "requestId": "…" }.
Objects use this explicit key layout:
v1/namespaces/default/workflows/{name}/versions/{version}/metadata.json
v1/namespaces/default/workflows/{name}/versions/{version}/artifacts/sha256-{hex}.tarThe registry, not S3, validates names, versions, manifests, immutability, digest, and size. S3 is private backing storage rather than the public protocol. Conditional object writes prevent a version from being overwritten. Artifacts are written before metadata, so an interrupted or losing divergent publish can leave a harmless content-addressed artifact that is not reachable from metadata. Retrying is safe; automatic orphan collection is not included.
Execution and scaling limits
tide serve starts its internal worker pool by default. One process with
--workers 1 needs no separately operated worker; increasing the value adds V8
isolate threads inside that same server process.
There is no remote worker claim/lease/heartbeat protocol yet. Do not run
multiple tide serve processes against one filesystem queue and describe that
as distributed scaling. The WorkflowSource and WorkflowQueue seams are the
extension points for a future leased external-worker protocol.
Trust boundary and production gaps
The standalone registry requires TIDE_REGISTRY_TOKEN; tide publish and
registry-backed execution send that value as a bearer token. tide serve
protects every /v1 control route when TIDE_SERVER_TOKEN is set, and remote
run, follow, and resume read the same variable. Health endpoints remain
unauthenticated. Token comparison is constant-time and access logs do not
include authorization headers.
Bearer authentication does not provide transport security. Public deployments must terminate TLS and restrict origin ingress. Tide remains single-tenant: there are no tenant identities, per-workflow authorization rules, quotas, or malware scanning. S3 credentials belong only to the registry process; clients use the authenticated HTTP API.