~/projects/temporal-standalone
Temporal rodando em casa — server, UI customizada com overlays, Casdoor OIDC, e o que acontece quando vc decide operar orquestracao de workflows no proprio lab.
Temporal rodando em casa — server, UI customizada com overlays, Casdoor OIDC, e o que acontece quando vc decide operar orquestracao de workflows no proprio lab.
~/Temporal-Standalone
Eu acredito que nao adianta estudar orquestracao de workflows sem operar um orquestrador. O Temporal Standalone nasceu da necessidade de ter rodando um Temporal da mesma forma que funciona o cloud — isolamento de namespaces, API keys — sem precisar subir 1 server por projeto dentro do lab.
Esse é o tipo de projeto que sai do learning system: estudar como o Temporal funciona por dentro, depois construir um ambiente operacional. A diferenca pra so ler a docs é que quando vc opera, vc descobre os limits reais — constraints que o agente precisa respeitar, bugs que so aparecem quando o sistema sobe, e customizacoes que nao existem no upstream. Tudo isso vira skill extraida — um contrato que o agente carrega quando precisa operar orquestracao.
A arquitetura
graph TD
subgraph "192.168.2.68"
UI["Temporal UI<br/>:8080<br/>Custom Overlays"]
CAS["Casdoor<br/>:8000<br/>OIDC"]
SRV["Temporal Server<br/>:7233<br/>v1.31.0"]
PG["PostgreSQL 18<br/>:5432"]
end
Client["Worker / SDK"] -->|gRPC| SRV
Browser -->|HTTP| UI
UI -->|OIDC| CAS
UI -->|gRPC| SRV
SRV -->|SQL| PG
CAS -->|SQL| PG
UI customizada — Git Submodule + Overlays
O projeto usa o approach de submodule + overlays: o repo temporalio/ui é um git submodule em ui-custom/upstream/, e as customizacoes vivem em ui-custom/overlays/ com a mesma estrutura de diretorios.
O Dockerfile custom faz o build em tres estagios: copia o upstream, aplica os overlays por cima, builda o frontend (pnpm) e o backend (Go), e gera um container final em Alpine.
Isso significa que git pull no submodule atualiza o upstream sem tocar nas customizacoes. E qualquer mudanca nos overlays requer um rebuild:
docker compose build temporal-ui && docker compose up -d temporal-ui
API Keys
A feature principal dos overlays é o sistema de API Keys. Tres endpoints:
-
GET /api/v1/api-keys— lista keys -
POST /api/v1/api-keys— cria key -
DELETE /api/v1/api-keys/:id— deleta key
As keys sao JWTs assinadas com HS256. O middleware diferencia tres tipos de token: cookies OIDC (setados pelo proprio server), bearer tokens OIDC (verificados via JWKS do Casdoor), e bearer tokens de API key (verificados via HS256 + JWT_SECRET).
Constraints
Operar Temporal local tem regras fixas que o agente segue:
-
Nunca usar
temporalio/auto-setup— re-roda schema migration a cada restart e pode corromper o banco -
Nunca modificar o schema manualmente — sempre via
temporal-sql-tool -
O container
temporal-servernao tem CLI — operacoes administrativas usamtemporal-setup(admin-tools) -
Dynamic config so aceita keys oficiais do Temporal — validar na docs antes de adicionar
-
UI rebuild necessario apos qualquer mudanca nos overlays
Schema migration
Upgrade de versao do Temporal Server = schema migration obrigatoria. O processo:
docker run --rm --network temporal-network \
-e DB=postgres12 -e DB_PORT=5432 -e POSTGRES_USER=temporal \
-e POSTGRES_PWD=temporal -e POSTGRES_SEEDS=postgresql \
-e DBNAME=temporal \
temporalio/admin-tools:latest \
temporal-sql-tool update-schema -d /etc/temporal/schema/postgresql/v12/temporal/versioned
Cada versao do server tem seu conjunto de migration scripts — aplicar na ordem certa, sem pular.
Standalone Activities
O Temporal 1.31 introduziu Standalone Activities em public preview — a ability de executar Activities diretamente via Temporal Client, sem um Workflow wrapper. Ad-hoc scripts, manutencao, testes unitarios de Activities no CLI, sem precisar scaffoldar um workflow inteiro.
Contudo, a feature é controlada por dynamic config (activity.enableStandalone), nao por gRPC. O campo standaloneActivities na resposta de DescribeNamespace é computado dinamicamente a partir da config do server no momento da query.
# config/temporal/dynamicconfig/docker.yaml
activity.enableStandalone:
- value: true
constraints:
namespace: "default"
Habilitado por namespace. Depois é so restartar o server e verificar:
docker exec temporal-setup temporal operator namespace describe default \
--address temporal:7233
Agent-readable
O projeto inteiro é documentado como skill — ~/projects/temporal-standalone/skills/temporal-standalone/. Um orchestrador classifica a tarefa e roteia pro workflow correto: feature enablement, fix de API keys, upgrade de server, rebuild de UI, ou namespace operations.
skills/temporal-standalone/
SKILL.md <- Orchestrator (roteamento)
reference/
routing-matrix.md <- Task category -> workflow
api-keys-bugs.md <- 3 bugs e blueprints de correcao
standalone-activities.md <- Feature e como habilitar
workflows/
enable-feature/SKILL.md <- Namespace capabilities
fix-api-keys/SKILL.md <- Persistencia + JWT + auth
server-upgrade/SKILL.md <- Upgrade + schema migration
ui-rebuild/SKILL.md <- Rebuild apos overlay changes
namespace-ops/SKILL.md <- CRUD namespaces
O agente carrega so o slice relevante pra tarefa. Precisa fazer upgrade? Carrega server-upgrade. Precisa rebuild da UI? Carrega ui-rebuild. Nunca a estrutura inteira.
A memoria do sistema vive no projeto. Quando algo muda — bug corrigido, feature habilitada, server atualizado — o skill atualiza junto. O repo é a memoria.
Skill que sai do lab
Esse projeto alimenta o learning system de volta. O que o agente aprende operando Temporal vira skill reutilizavel — e a skill com mais potencial dessa pilha é orquestracao com Temporal para agents.
A ideia: workflows de agentes (pipelines de cron, DAGs de multi-step, retry com backoff, orchestration vs choreography) rodando em cima do Temporal Standalone. O agent codifica um workflow em Go ou TypeScript, registra no server, e o Temporal cuida do resto — execution, retry, timeout, state management. Nada de setTimeout improvisado ou retry manual em Python. O Temporal garante que o workflow completa, mesmo se o worker cai no meio.
O standalone é o playground perfeito pra isso. Sem billing, sem latencia de cloud, sem limites de namespace. O agente deploya, testa, quebra, corrige — tudo local. Quando funciona, a skill extraida documentando o pattern (workflow definition, activity registration, error handling, worker lifecycle) vira contrato que outros projetos consomem.
O ciclo do learning: estudou o repo, construiu o ambiente, operou, extraiu a skill. De volta pro learning, pronto pra ser usada no proximo projeto.
Referencia
# temporal-standalone (condensed)
## Architecture
192.168.2.68
:7233 Temporal Server (gRPC) — v1.31.0, schema v1.19
:8080 Temporal UI (custom overlays) — v2.49.x base
:8000 Casdoor (OIDC) — v3.49.0
:5432 PostgreSQL 18 (temporal, temporal_visibility, casdoor)
## Project Structure
~/projects/temporal-standalone/
docker-compose.yml # Main infra
docker-compose.override.yml # UI custom build + env vars
ui-custom/
upstream/ # Git submodule (temporalio/ui)
overlays/ # Custom Go/Svelte mods
Dockerfile.custom # node -> go -> alpine
config/temporal/dynamicconfig/docker.yaml
scripts/ # init-db.sh, setup-namespaces.sh
## Key Commands
# Rebuild UI
docker compose build temporal-ui && docker compose up -d temporal-ui
# Schema migration
docker run --rm --network temporal-network \
-e DB=postgres12 -e DB_PORT=5432 -e POSTGRES_USER=temporal \
-e POSTGRES_PWD=temporal -e POSTGRES_SEEDS=postgresql \
-e DBNAME=temporal \
temporalio/admin-tools:latest \
temporal-sql-tool update-schema -d /etc/temporal/schema/postgresql/v12/temporal/versioned
# Namespace ops
docker exec temporal-setup temporal operator namespace list --address temporal:7233
# Enable Standalone Activities (dynamic config)
# activity.enableStandalone: true in docker.yaml, then restart server
## Constraints
- NUNCA auto-setup
- Schema migration sempre via temporal-sql-tool
- Container temporal-server nao tem CLI — usar temporal-setup
- Dynamic config so aceita keys oficiais
- UI rebuild necessario apos overlay changes