Skip to main content

temporal-registry

Temporal-backed workflow registry, scheduler, and HTTP dispatch gateway.

The Problem

Temporal gives you durable workflow execution, but clients still need to know a lot before they can safely start work:

  • Which workflow types exist.
  • Which task queue can run each workflow.
  • What input shape each workflow expects.
  • Which search attributes are supported.
  • Whether any worker that can run the workflow is currently alive.

Without a registry, that information tends to get copied into clients, deployment config, docs, and worker code. Those copies drift. Clients start hard-coding task queues, old workflow names stay around, schedule creation depends on tribal knowledge, and independent worker deployments become tightly coupled to every caller.

The operational failure mode is simple: the worker knows what it can run, but the client does not have a reliable, current way to discover that before it starts or schedules a workflow.

The Solution

temporal-registry is a small always-on registry service for Temporal workflow workers. It serves two purposes:

  • It hosts an HTTP API for clients to discover, start, schedule, and administer registered workflows.
  • It also runs as a Temporal worker for the registry workflow, which stores workflow registrations, task queues, schemas, workers, and heartbeat state.

Other workers register their runnable workflow types, task queues, input schemas, search attributes, labels, and heartbeat TTLs. The registry stores that metadata inside the durable registry workflow, then exposes it through the HTTP API.

Clients use the registry instead of hard-coding worker details. A caller can:

  • List registered workflow types.
  • Validate workflow input against the registered schema.
  • Start a workflow on the currently registered task queue.
  • Create schedules without knowing the worker deployment layout.
  • Query supported search attributes for a workflow type.

Worker heartbeats keep availability current. If a worker stops heartbeating, the registry can stop advertising that worker as a healthy dispatch target, while the registry state itself remains durable because it is backed by Temporal.

This keeps ownership clean: workers own execution and capability registration; clients own requests; temporal-registry owns discovery, validation, dispatch, and scheduling. It does not execute workflow activities directly; registered Temporal workers host the actual workflow code.

Use it when workers are deployed independently, workflow types change over time, or multiple runtimes need to advertise capabilities into one shared Temporal namespace.

How To Use It

  1. Start temporal-registry against your Temporal namespace.
  2. Have each worker register the workflow types it can run, including task queue, input schema, labels, and supported search attributes.
  3. Keep workers heartbeating so the registry knows which dispatch targets are currently healthy.
  4. Point clients at the registry API to list workflows, validate inputs, start runs, or create schedules without hard-coding worker task queues.

Terms And Configuration

temporal-registry sits between HTTP clients and Temporal. It hosts both the registry HTTP API and the registry worker. The HTTP API talks to Temporal over gRPC, while clients talk to temporal-registry over HTTP.

client / curl / docs
        |
        | HTTP
        v
temporal-registry API
        |
        | Temporal gRPC
        v
Temporal frontend
        |
        v
registry workflow state

Temporal terms:

  • TEMPORAL_ADDRESS is the Temporal frontend gRPC endpoint. Despite the word "frontend", this is not the browser UI.
  • TEMPORAL_NAMESPACE is the Temporal namespace where the registry workflow and dispatched workflows run.
  • TEMPORAL_NAMESPACE is created by the registry service at startup if it is missing. The default retention is 30 days unless the config's temporal.namespace_retention_days is changed.
  • TEMPORAL_TLS controls whether the registry connects to Temporal using TLS.
  • TEMPORAL_API_KEY authenticates the registry process to Temporal, if your Temporal deployment requires API-key auth.
  • Temporal UI is the browser UI for humans to inspect workflow histories, schedules, and runs. It is separate from this service.

Registry terms:

  • TEMPORAL_REGISTRY_URL is the HTTP URL for the temporal-registry API, for example http://127.0.0.1:8080.
  • TEMPORAL_REGISTRY_TOKEN is the bearer token for the registry HTTP API when registry auth is enabled. It is not the Temporal API key.
  • The registry workflow is the durable Temporal workflow that stores registered workflow types, task queues, schemas, workers, and heartbeat state.
  • The registry worker is the worker process started by temporal-registry to execute the registry workflow itself.

Quick Start

uv sync --frozen
uv run temporal-registry -f temporal_registry/config.yaml

The HTTP API publishes an OpenAPI spec at /openapi.json and interactive docs at /docs.

Health and visibility endpoints:

  • /health returns ok when the HTTP API process is alive.
  • /ready checks that the API can query the registry workflow through Temporal.
  • /registry/status returns registry workflow counts and the latest registry service startup marker.

The registry records a startup signal in workflow history. Runtime liveness stays on /health and /ready so the registry workflow history does not grow from periodic service heartbeat signals.

Human-readable workflow IDs (slug counters)

By default the registry generates workflow IDs that include random hex suffixes (run-agent-hello-world-eb3bd463). That keeps submissions collision-free but the IDs are hard to scan, share, or group in the Temporal UI.

The registry maintains a durable per-slug counter so callers can opt in to predictable IDs:

# Submit a run with a human name. The registry slugifies the name,
# claims the next counter, and uses `<slug>-r<N>` as the workflow id.
curl -X POST "$TEMPORAL_REGISTRY_URL/run" \
  -H 'content-type: application/json' \
  -d '{
    "agent_id":  "hello-world",
    "workspace": "/tmp/work",
    "prompt":    "say hi",
    "name":      "tui-build"
  }'
# -> {"workflow_id": "tui-build-r1", ...}

# Same for graph workflows via the generic start endpoint.
curl -X POST "$TEMPORAL_REGISTRY_URL/workflows/agent.graph.run.v1/start" \
  -H 'content-type: application/json' \
  -d '{"input": {...}, "name": "tui-build"}'
# -> {"workflow_id": "tui-build-r2", ...}

Direct admin endpoints:

# Claim a counter without starting a workflow (useful for client-side
# id construction before submitting).
curl -X POST "$TEMPORAL_REGISTRY_URL/workflow-ids/claim" \
  -H 'content-type: application/json' \
  -d '{"name": "tui-build"}'
# -> {"slug": "tui-build", "counter": 3, "workflow_id": "tui-build-r3"}

# Reset a slug back to 0 (next claim returns r1).
curl -X POST "$TEMPORAL_REGISTRY_URL/workflow-ids/reset" \
  -H 'content-type: application/json' \
  -d '{"name": "tui-build"}'

# Inspect all current counters.
curl "$TEMPORAL_REGISTRY_URL/workflow-ids"

Durability. Counters live in the registry's own Temporal workflow state (the singleton workflow-registry). Each claim is a Temporal Workflow Update, serialised on the workflow and persisted to the same Postgres backing store that holds the rest of Temporal's workflow history. Multiple registry HTTP replicas are safe — they all signal the same workflow. Worker / pod restarts preserve the state. The only clobber risk is running two registries with the same workflow_id against the same namespace, which is a deployment misconfiguration.

Retention. Counter entries are time-bounded (SLUG_DEFAULT_TTL_SECONDS, 30 days) and size-bounded (SLUG_DEFAULT_MAX_ENTRIES, 10 000 slugs). The periodic GC sweep drops entries older than the TTL, then evicts the LRU-oldest if the map still exceeds the cap. Both bounds keep the workflow's history from growing unboundedly when callers spam unique slugs.

Slug normalisation. Names are lowercased and any non-[a-z0-9-] run is collapsed to - (Tui_Build, tui build!, TUI-Build all hit the same counter). Leading/trailing dashes are stripped. A name that slugifies to empty (e.g. @@@) is rejected with HTTP 400.

Search Attribute Administration

Registered workers declare the custom search attributes their workflows use. The registry provisions those attributes in the configured Temporal namespace before advertising the worker or workflow. Missing attributes are created automatically; existing attributes with the same type are left unchanged.

Type conflicts are not overwritten during normal registration. A same-name, different-type attribute is a namespace-level admin concern, so the registry fails provisioning and keeps the workflow unavailable until an operator reconciles it explicitly.

List attributes declared by registered workflows:

curl "$TEMPORAL_REGISTRY_URL/registry/search-attributes"

List attributes that actually exist in the Temporal namespace:

curl "$TEMPORAL_REGISTRY_URL/registry/temporal/search-attributes"

Reconcile registered declarations against Temporal:

curl -X POST "$TEMPORAL_REGISTRY_URL/registry/temporal/search-attributes" \
  -H 'content-type: application/json' \
  -d '{"mode":"validate"}'

Reconcile modes:

  • validate reports missing and conflicting attributes without changing Temporal.
  • ensure creates missing attributes and refuses type conflicts.
  • replace removes conflicting custom attributes and recreates them with the registered type. It requires an explicit attributes list and confirm: true.

Example explicit replacement:

curl -X POST "$TEMPORAL_REGISTRY_URL/registry/temporal/search-attributes" \
  -H 'content-type: application/json' \
  -d '{"mode":"replace","attributes":["agent_id"],"confirm":true}'

replace never removes system search attributes. Use it only as an admin migration step because changing an attribute type can invalidate existing visibility queries and assumptions.

Useful targets:

make test
make lint
make docker-build

Contributing

Set up the project with uv sync --frozen, then run make check before opening a change. Use make fmt to apply Ruff formatting and safe fixes.

For local git hooks:

make hooks-install

The hooks run shared config sync, Ruff, mypy, and commit message linting. Commit messages should follow Conventional Commits because releases are managed by release-please.

License

MIT. See LICENSE.

Metadata

Release files for temporal-registry 0.1.7

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for temporal-registry 0.1.7
File Size Uploaded
temporal_registry-0.1.7.tar.gz 47.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for temporal-registry 0.1.7
File Interpreter ABI Platform
temporal_registry-0.1.7-py3-none-any.whl Python 3 none any Details

Total release size: 95.5 kB

Release files / temporal_registry-0.1.7.tar.gz

Download URL temporal_registry-0.1.7.tar.gz
Size 47.9 kB
Tags Source
SHA-256 checksum
How to use checksums
33914f56250dc6f62c218ec54f4a0705f29d66efed4a83804acd8d12dfa71821
BLAKE2b-256 checksum
How to use checksums
ecb4dc5c44e6bcad36186fbb04cb06ebf76545febb1611300d478d874e762db9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / temporal_registry-0.1.7-py3-none-any.whl

Download URL temporal_registry-0.1.7-py3-none-any.whl
Size 47.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c2bba87a2a52241e704f0d78ac853e920dee6ebaf25ea238bb10600ebce5ca44
BLAKE2b-256 checksum
How to use checksums
1abf6ea2fea9f6c881215dc975002c89d79e3ec426f264cb0f255b100058d6be
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.1.7 This release

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page