Skip to content

Workflow orchestration (Temporal)

Temporal is a durable workflow engine. You write ordinary functions, and Temporal makes sure they run to completion even when the process crashes, the network drops, or a step needs to wait hours for an external event. It handles retries, timeouts, state, and history automatically.

Peach hosts a Temporal service per codops. Your code connects to it with a per-codops API key, TEMPORAL_API_KEY.

gRPC (external) temporal-grpc.<codops>.peach.ebu.io:443
gRPC (in-cluster) peach-codops-<codops>-temporal-frontend:7233
Web UI https://temporal.<codops>.peach.ebu.io
Default namespace default (7-day history retention)

Replace <codops> with your codops name.

Not yet on your codops? Use the test environment in the meantime

If your codops is not yet enabled for Temporal, you can start building and testing against the shared test environment on dev-2 while your deployment is being set up.

gRPC temporal-grpc.test.dev.peach.ebu.io:443
Web UI https://temporal.test.dev.peach.ebu.io
API key test:<token> — ask the Peach team for the test token
Namespace default

When you move to your own codops, only two things change in your code:

  1. The hostname: test.dev.peach.ebu.io → <your-codops>.peach.ebu.io
  2. The API key prefix: test:<token> → <your-codops>:<token>

Everything else — SDK calls, namespace, TLS setup — stays identical.

Authentication

The gRPC endpoint takes a {codops}:{token} bearer credential. The token is TEMPORAL_API_KEY, a key in your codops' secret store, so tasks and endpoints already have it as an environment variable. It is separate from your peach-models token: rotating one does not break the other. If you need it outside the cluster, ask the Peach team.

Your api_key is then <codops>:<token>.

In-cluster code can skip the public endpoint, TLS, and the credential entirely by connecting to the internal service name. Most Temporal workloads should run this way.

Connect — Python

import os
from temporalio.client import Client

# External (from outside the cluster, or from another cluster)
client = await Client.connect(
    "temporal-grpc.<codops>.peach.ebu.io:443",
    namespace="default",
    api_key=os.environ["TEMPORAL_API_KEY"],  # "<codops>:<token>"
    tls=True,
)

# In-cluster — no TLS, no key
client = await Client.connect(
    "peach-codops-<codops>-temporal-frontend:7233",
    namespace="default",
)

Install the SDK:

# pyproject.toml
dependencies = ["temporalio"]

Connect — Go

import (
    "crypto/tls"
    "os"
    "go.temporal.io/sdk/client"
)

// External
c, err := client.Dial(client.Options{
    HostPort:    "temporal-grpc.<codops>.peach.ebu.io:443",
    Namespace:   "default",
    Credentials: client.NewAPIKeyStaticCredentials(os.Getenv("TEMPORAL_API_KEY")),
    ConnectionOptions: client.ConnectionOptions{
        TLS: &tls.Config{},
    },
})

// In-cluster — no TLS, no key
c, err := client.Dial(client.Options{
    HostPort:  "peach-codops-<codops>-temporal-frontend:7233",
    Namespace: "default",
})

Web UI

https://temporal.<codops>.peach.ebu.io is protected by GitLab OAuth (same login as the rest of Peach). The Temporal Web UI has no authentication of its own, so the OAuth session is the only gate — anyone who can log in can read every workflow history and terminate running workflows. Treat it as a privileged surface.

Things worth knowing

  • Namespace default keeps histories for 7 days. Older runs are permanently gone. Need a different retention or a dedicated namespace? Ask the platform team.
  • The datastore is shared with Prefect on the same codops. Heavy load from either service affects the other.
  • The credential is per-codops, not per-user. Rotating it breaks every caller at the same time. Load it from the TEMPORAL_API_KEY environment variable rather than hardcoding it.
  • In-cluster workers need no public credential. A pod running inside the cluster connects directly to peach-codops-<codops>-temporal-frontend:7233 with no API key.