feat(wfe-server): headless workflow server with gRPC, webhooks, and OIDC auth
Single-binary server exposing the WFE engine over gRPC (13 RPCs) with
HTTP webhook support (GitHub, Gitea, generic events).
Features:
- gRPC API: workflow CRUD, lifecycle event streaming, log streaming,
log search via OpenSearch
- HTTP webhooks: HMAC-SHA256 verified GitHub/Gitea webhooks with
configurable triggers that auto-start workflows
- OIDC/JWT auth: discovers JWKS from issuer, validates with asymmetric
algorithm allowlist to prevent algorithm confusion attacks
- Static bearer token auth with constant-time comparison
- Lifecycle event broadcasting via tokio::broadcast
- Log streaming: real-time stdout/stderr via LogSink trait, history
replay, follow mode
- Log search: full-text search via OpenSearch with workflow/step/stream
filters
- Layered config: CLI flags > env vars > TOML file
- Fail-closed on OIDC discovery failure, fail-loud on config parse errors
- 2MB webhook payload size limit
- Blocked sensitive env var injection (PATH, LD_PRELOAD, etc.)
2026-04-01 14:37:25 +01:00
|
|
|
[package]
|
|
|
|
|
name = "wfe-server"
|
|
|
|
|
version.workspace = true
|
|
|
|
|
edition.workspace = true
|
|
|
|
|
license.workspace = true
|
|
|
|
|
repository.workspace = true
|
|
|
|
|
homepage.workspace = true
|
|
|
|
|
description = "Headless workflow server with gRPC API and HTTP webhooks"
|
|
|
|
|
|
|
|
|
|
[[bin]]
|
|
|
|
|
name = "wfe-server"
|
|
|
|
|
path = "src/main.rs"
|
|
|
|
|
|
|
|
|
|
[dependencies]
|
|
|
|
|
# Internal
|
|
|
|
|
wfe-core = { workspace = true, features = ["test-support"] }
|
feat(wfe-server): full feature set, debian base, name resolution in gRPC
Proto changes:
* Add `name` to `WorkflowInstance`, `WorkflowSearchResult`,
`RegisteredDefinition`, and `DefinitionSummary` messages.
* Add optional `name` override to `StartWorkflowRequest` and echo the
assigned name back in `StartWorkflowResponse`.
* Document that `GetWorkflowRequest.workflow_id` accepts UUID or
human name.
gRPC handler changes:
* `start_workflow` honors the optional name override and reads the
instance back to return the assigned name to clients.
* `get_workflow` flows through `WorkflowHost::get_workflow`, which
already falls back from UUID to name lookup.
* `stream_logs`, `watch_lifecycle`, and `search_logs` resolve
name-or-UUID up front so the LogStore/lifecycle bus (keyed by
UUID) subscribe to the right instance.
* `register_workflow` propagates the definition's display name into
`RegisteredDefinition.name`.
Crate build changes:
* Enable the full executor feature set on wfe-yaml —
`rustlang,buildkit,containerd,kubernetes,deno` — so the shipped
binary recognizes every step type users can write.
* Dockerfile switched from `rust:alpine` to `rust:1-bookworm` +
`debian:bookworm-slim` runtime. `deno_core` bundles a v8 binary
that only ships glibc; alpine/musl can't link it without building
v8 from source.
2026-04-07 19:07:52 +01:00
|
|
|
wfe = { version = "1.9.0", path = "../wfe", registry = "sunbeam" }
|
|
|
|
|
wfe-yaml = { version = "1.9.0", path = "../wfe-yaml", registry = "sunbeam", features = ["rustlang", "buildkit", "containerd", "kubernetes", "deno"] }
|
|
|
|
|
wfe-server-protos = { version = "1.9.0", path = "../wfe-server-protos", registry = "sunbeam" }
|
feat(wfe-server): headless workflow server with gRPC, webhooks, and OIDC auth
Single-binary server exposing the WFE engine over gRPC (13 RPCs) with
HTTP webhook support (GitHub, Gitea, generic events).
Features:
- gRPC API: workflow CRUD, lifecycle event streaming, log streaming,
log search via OpenSearch
- HTTP webhooks: HMAC-SHA256 verified GitHub/Gitea webhooks with
configurable triggers that auto-start workflows
- OIDC/JWT auth: discovers JWKS from issuer, validates with asymmetric
algorithm allowlist to prevent algorithm confusion attacks
- Static bearer token auth with constant-time comparison
- Lifecycle event broadcasting via tokio::broadcast
- Log streaming: real-time stdout/stderr via LogSink trait, history
replay, follow mode
- Log search: full-text search via OpenSearch with workflow/step/stream
filters
- Layered config: CLI flags > env vars > TOML file
- Fail-closed on OIDC discovery failure, fail-loud on config parse errors
- 2MB webhook payload size limit
- Blocked sensitive env var injection (PATH, LD_PRELOAD, etc.)
2026-04-01 14:37:25 +01:00
|
|
|
wfe-sqlite = { workspace = true }
|
|
|
|
|
wfe-postgres = { workspace = true }
|
|
|
|
|
wfe-valkey = { workspace = true }
|
|
|
|
|
wfe-opensearch = { workspace = true }
|
|
|
|
|
opensearch = { workspace = true }
|
|
|
|
|
|
|
|
|
|
# gRPC
|
|
|
|
|
tonic = "0.14"
|
|
|
|
|
tonic-health = "0.14"
|
2026-04-06 23:47:42 +01:00
|
|
|
tonic-reflection = "0.14"
|
feat(wfe-server): headless workflow server with gRPC, webhooks, and OIDC auth
Single-binary server exposing the WFE engine over gRPC (13 RPCs) with
HTTP webhook support (GitHub, Gitea, generic events).
Features:
- gRPC API: workflow CRUD, lifecycle event streaming, log streaming,
log search via OpenSearch
- HTTP webhooks: HMAC-SHA256 verified GitHub/Gitea webhooks with
configurable triggers that auto-start workflows
- OIDC/JWT auth: discovers JWKS from issuer, validates with asymmetric
algorithm allowlist to prevent algorithm confusion attacks
- Static bearer token auth with constant-time comparison
- Lifecycle event broadcasting via tokio::broadcast
- Log streaming: real-time stdout/stderr via LogSink trait, history
replay, follow mode
- Log search: full-text search via OpenSearch with workflow/step/stream
filters
- Layered config: CLI flags > env vars > TOML file
- Fail-closed on OIDC discovery failure, fail-loud on config parse errors
- 2MB webhook payload size limit
- Blocked sensitive env var injection (PATH, LD_PRELOAD, etc.)
2026-04-01 14:37:25 +01:00
|
|
|
prost-types = "0.14"
|
|
|
|
|
|
|
|
|
|
# HTTP (webhooks)
|
|
|
|
|
axum = { version = "0.8", features = ["json", "macros"] }
|
|
|
|
|
hyper = "1"
|
|
|
|
|
tower = "0.5"
|
|
|
|
|
|
|
|
|
|
# Runtime
|
|
|
|
|
tokio = { workspace = true }
|
|
|
|
|
async-trait = { workspace = true }
|
|
|
|
|
|
|
|
|
|
# Serialization
|
|
|
|
|
serde = { workspace = true }
|
|
|
|
|
serde_json = { workspace = true }
|
|
|
|
|
toml = "0.8"
|
|
|
|
|
|
|
|
|
|
# CLI
|
|
|
|
|
clap = { version = "4", features = ["derive", "env"] }
|
|
|
|
|
|
|
|
|
|
# Auth
|
|
|
|
|
hmac = "0.12"
|
|
|
|
|
sha2 = "0.10"
|
|
|
|
|
hex = "0.4"
|
|
|
|
|
jsonwebtoken = "9"
|
|
|
|
|
subtle = "2"
|
|
|
|
|
reqwest = { workspace = true }
|
|
|
|
|
|
|
|
|
|
# Observability
|
|
|
|
|
tracing = { workspace = true }
|
|
|
|
|
tracing-subscriber = { workspace = true }
|
|
|
|
|
chrono = { workspace = true }
|
|
|
|
|
uuid = { workspace = true }
|
|
|
|
|
|
|
|
|
|
# Utils
|
|
|
|
|
tokio-stream = "0.1"
|
|
|
|
|
dashmap = "6"
|
|
|
|
|
|
|
|
|
|
[dev-dependencies]
|
|
|
|
|
pretty_assertions = { workspace = true }
|
|
|
|
|
tokio = { workspace = true, features = ["test-util"] }
|
|
|
|
|
tempfile = { workspace = true }
|
|
|
|
|
rsa = { version = "0.9", features = ["pem"] }
|
|
|
|
|
rand = "0.8"
|
|
|
|
|
base64 = "0.22"
|