Hookway

Examples

This appendix links to every YAML example in this directory, grouped by concern. Core YAML files are complete configurations checked by hookway validate. replay.yaml files configure the optional replay service; docker-compose.yaml files start the local demonstrations. Runtime configs stay beside their Compose examples under this directory. File headers explain each demonstration; showcase/invalid-* files intentionally fail validation.

Starting configurations

FileDescription
config.yamlMinimal config: one RabbitMQ destination and a signed GitHub flow (/webhooks/github). Default for make validate and make run, without replay.
sample.yamlKeep a configurable random fraction of events with a Sample node. Sampling drops that branch, not just inspection records.
inspect.yamlSample → Inspect on an independent branch, alongside RabbitMQ Publish for every request; uses the local replay stack. See run instructions.
filters.yamlFiltering reference: a filter node after envelope with a silent drop, a branch-level filter that fans out selectively, and on_no_match: reject (422) using numeric, regex, and exists operators. See docs/reference/configuration.md § Filtering.
adapters/named-events.yamlAuthenticated issue webhook, filtering/projection, named event validation and synchronous JSON webhook delivery. See Named events.
json-validation.yamlValidate required fields, numeric IDs and email formats with local JSON Schema references. Optional explicit conversion; malformed input returns 400. See Validate JSON.
variables.yamlDynamic routing reference: an environment comes from a path placeholder or filtered header, field_map copies a JSON body field to vars.tenant, and {name} placeholders select a pre-provisioned SQS queue and webhook URL. Unknown environments and unresolved templates fail closed. See docs/reference/configuration.md § Variables.
on_error.yamlError-port reference: http_request 5xx to a DLQ (ack:on_error>dlq) or an explicit 502 (up:on_error>fail); rate_limit:on_error to a 429; auth:on_error to a 401; envelope:on_error to a 400. http_trigger, read_body, and no_auth have no error port. Also shows http_request retry combined with an on_error path. See docs/reference/configuration.md § Flows.
xml.yamlXML reference: body_format: xml, a filter on an element path, a code node reading the DOM, xml_prune, and body: transformed forwarding. See docs/reference/configuration.md § XML bodies.
multipart.yamlMultipart reference: body_format: multipart with limits, a filter on files.document.content_type, and a code node reading upload metadata. See docs/reference/configuration.md § Multipart bodies.
multipart-split.yamlMultipart fan-out with publish.payload: object: decoded file bytes to S3, form fields (+ file metadata) as a JSON envelope to RabbitMQ.
multipart-s3-lambda.yamlSame fan-out pattern with Lambda instead of RabbitMQ: file bytes to S3 (payload: object), Event invoke of a worker with fields + file metadata (no base64).

Shared connections and reliable ingestion

FileDescription
connections.yamlReuse named AWS and PostgreSQL connections across destinations; keep credentials and connection settings in one place.
launchdarkly.yamlTenant targeting with boolean, string, numeric and JSON flags, explicit fallbacks and conditional responses. See feature flags.
reliable-ingestion.yamlSigned GitHub webhook intake with a 202 response after RabbitMQ publisher confirmation. Preserve stable provider identity; broker durability and atomic consumer deduplication remain deployment responsibilities. See delivery and recovery contracts.

Destination reference configs

Each file showcases one destination type with all available options documented via comments.

FileDescription
destinations/rabbitmq.yamlFull RabbitMQ option surface: exchange, dead-letter exchange/queue, queue_defaults, TLS, publisher_channels, and a flow that fans out to two publish nodes.
destinations/sqs.yamlTwo standard SQS queues, per-message delay, fair-queue grouping and provisioned queue attributes.
destinations/fifo-sqs.yamlSQS FIFO publication using original JSON representation, digest-based identities, bounded retries and explicit responses.
destinations/sns.yamlAWS SNS destination: single topic auto-created at startup.
destinations/lambda.yamlAWS Lambda destination: Event (default) or RequestResponse invoke; target is the function name / ARN. create: true is rejected. For file uploads, see multipart-s3-lambda.yaml (S3 object + Lambda notify).
destinations/s3.yamlAWS S3 destination: one JSON Message per event under a date-partitioned key prefix, with SSE options and auto-bucket creation. publish.payload: object stores decoded file bytes instead (see multipart-split.yaml).
destinations/redis_stream.yamlRedis / Valkey stream destination: single-node mode; annotated comments for cluster, sentinel, and TLS topology.
destinations/redis_list.yamlRedis / Valkey list destination: LPUSH work-queue. redis_options.max_len (LTRIM) is not a publish-node field.
destinations/redis_pubsub.yamlRedis / Valkey Pub/Sub destination: fire-and-forget channel broadcast; no redis_options block (all fields invalid for pubsub). Lossy — subscribers offline at publish time miss the message.
destinations/redis_kv.yamlRedis / Valkey key-value destination: SET at the default {prefix}:{endpoint}:{id} key; YAML does not set TTL. publish.payload: object stores decoded file bytes instead of the JSON Message.
destinations/postgres.yamlPostgres destination: one row per event into a shared table with fixed envelope columns + jsonb (headers/query/body/auth); target stored as a column; CREATE TABLE IF NOT EXISTS at startup; id dedup via ON CONFLICT DO NOTHING.
destinations/mongodb.yamlMongoDB destination: one BSON document per event into a collection named by the publish target; full event envelope with _id = event id; nested body/headers/query/auth; id dedup via duplicate-key no-op; no DDL (collections auto-create).
destinations/kafka.yamlKafka destination: SASL/TLS, topic create: true, two publish nodes. kafka_options.partition_key is not a publish-node field.
destinations/temporal.yamlTemporal destination: one workflow execution per event; target is the task queue. temporal_options are not publish-node fields.
destinations/webhook.yamlWebhook destination: explicitly loss-tolerant async delivery (202 means bounded volatile-queue admission), or synchronous HTTP delivery; envelope vs proxy payload modes; outbound auth and bounded retries. Neither mode provides exactly-once business processing.

MCP gateways

See the MCP gateway guide for deployment and OAuth setup. Gateway caller authentication is separate from outbound API credentials.

FileDescription
templates/mcp-gateway.yamlAPI-key-authenticated HTTP status tool and native greeting tool with independent upstream credentials.
mcp-oauth.yamlExternal-provider OAuth with a dedicated resource audience, discovery scopes and an explicit tool permission.
openapi/api.yaml, settings, generated coreImport a JSON API as authenticated read/write tools. See OpenAPI import.
mcp-endpoints.yamlIndependent /services/mcp and /utilities/mcp endpoints with separate caller keys and catalogues.

Auth strategy reference configs

Each file demonstrates one auth type with the exact fields required by that type.

FileDescription
auth/hmac_sha256.yamlHMAC-SHA256 auth: scheme variants shown — hmac_sha256_ts_colon (Paddle) and hmac_sha256_hex (GitHub).
auth/header_exact.yamlHeader exact match auth: exact scheme (static Bearer token, remove_bearer: true).
auth/jwt.yamlJWT auth: RS256 scheme with expected_issuer, expected_audience, and body_hash_claim.
auth/no_auth.yamlNo-auth passthrough: a no_auth node disables verification entirely.
auth/request-signatures.yamlTimestamped separate-header HMAC, multiple-signature HMAC and P-256 ECDSA over explicit body representations.

Ingress adapters

These use neutral resource names and explicit admission. For the complete field contracts and deployment boundaries, see Ingress adapters.

FileWhat it demonstrates
routing/shared.yamlConsistent DynamoDB lookup, environment admission and a resolved broker target.
routing/admission.yamlShared and dedicated routing profiles that reject unadmitted environments.
routing/registration.yamlRegister a route after a successful upstream creation; keep the upstream response when registration needs recovery.
proxy/admitted.yamlRelay to an admitted upstream with rate, loop and replay guards.
proxy/request-contract.yamlByte-exact file relay with method/path/query/header forwarding and GET/HEAD network retries.
credentials/static.yamlOrigin-restricted upstream headers from secret references.
credentials/oauth.yamlCached OAuth client-credentials acquisition and one bounded invalid-session refresh.
credentials/token-exchange.yamlPer-request token exchange before an XML relay.
adapters/batch-shared.yamlSequential child processing with per-child environment admission and independent outcomes.
adapters/batch-dedicated.yamlBounded concurrent children using a dedicated routing profile.
adapters/changed-fields-sns.yamlChanged-field projection and SNS attributes.
adapters/event-filter.yamlAccept allowed event types and acknowledge filtered events.
adapters/json-projection.yamlValidate a JSON schema, coerce an explicitly selected field type and strip document content.
adapters/xml-ordered.yamlPreserve ordered XML elements while pruning selected content.

Builder / showcase fixtures (showcase/)

These are the configs the web builder round-trips. make validate-examples skips invalid-*.

FileDescription
showcase/github.yamlMinimal ingest flow used by serving tests.
showcase/proxy.yamlSync reverse proxy (http_request + respond passthrough). {rest...} paths must be quoted.
showcase/production.yamlShowcase: Validate JSON, switch, transform, code, http_request with on_error.
showcase/invalid-gate-order.yamlInvalid: gate after read_body (must fail validate).
showcase/invalid-many.yamlInvalid: several distinct problems reported together.

Sentry notifications

Templates (examples/templates/)

Each file is a runnable configuration and a template in the builder palette. Notification templates insert the format-and-POST pair. Stripe/Paddle/Sentry ingest templates insert the verified prefix; connect its envelope to your destination. The XML template inserts a complete XML pipeline.

Every template file is a production-shaped config: secrets live in the environment (secret_ref with rotation overlap, webhook URLs as ${ENV}), inbound traffic is authenticated (never no_auth), /metrics is on a private port behind security.api_key, and envelopes only forward allowlisted headers.

FilePattern
templates/stripe-ingest.yamlStripe webhook ingest: loop/rate guards, hmac_sha256_ts_dot on Stripe-Signature via secret_ref, replay guard, JSON envelope. Wrapper publish HMAC-signs the next hop.
templates/sentry-ingest.yamlSentry ingress: verify unprefixed Sentry-Hook-Signature using the integration Client Secret, guard duplicates and parse JSON. Connect any destination.
templates/paddle-ingest.yamlPaddle Billing ingest: same shape with hmac_sha256_ts_colon on Paddle-Signature.
templates/slack-notify.yamlFormat an event and POST it to a Slack incoming webhook. Wrapper still HMAC-authenticates the inbound caller.
templates/discord-notify.yamlFormat an event and POST it to a Discord webhook. Same inbound auth as Slack.
templates/xml-ingest.yamlXML webhook ingest and forward: loop/rate guards, hmac_sha256_hex over the raw bytes via secret_ref, replay guard, body_format: xml envelope, a filter on an element path, a code node reading the element tree, xml_prune, and a body: transformed forward as application/xml. Inserts the whole pipeline.

Running validation

# Validate the default config
make validate

# Validate all example files (`hookway validate`, no server)
make validate-examples

# Boot every valid example, GET /healthz, and POST a signed request to each flow
make test-examples

make test-examples boots configs with test publishers and loopback HTTP; it does not contact your production destinations. invalid-* files are intentional validation failures. When adding an example directory, add it to validate-examples in the Makefile so CI checks it.

Persistent production-style local stack (production-local/)

Use production-local/docker-compose.yaml with make prod-local-bootstrap (PROD_LOCAL_MODE=full adds replay and its console; PROD_LOCAL_PROTECTION=1 selects Redis protection). Bootstrap generates private credentials and copies ordinary YAML once, preserving later operator edits. The supported workflow uses Make and Compose directly, with no test helper. Ordinary shutdown preserves named volumes; explicit reset requires the selected project name. Browser ports bind to loopback, while internal APIs and stores stay on the Compose network. See Local development for validation, operating commands, URLs and architecture limits.

This runs the same application with local HTTP and private destinations.

FileComponents
production-local/core.yamlCore and RabbitMQ with process-local rate limiting
production-local/core-shared.yamlCore and RabbitMQ with shared Redis rate limiting
production-local/core-replay.yamlReplay/console with process-local rate limiting
production-local/core-replay-shared.yamlReplay/console with shared Redis rate limiting
production-local/replay.yamlReplay configuration with distinct scoped credentials
production-local/migration/replay.yamlOwner-only migration configuration; never serve it
production-local/database/provision.sqlProvision owner/runtime roles and preserve current-schema data
production-local/database/runtime-grants.sqlExplicit runtime grants applied after migrations
production-local/tls/docker-compose.yamlOptional localhost TLS proxy overlay
production-local/tls/CaddyfileManual local certificate termination for core, replay and console
production-local/monitoring/README.mdOptional authenticated local monitoring recipe; no cloud or live responder required
production-local/monitoring/compose.yamlLoopback Prometheus/Alertmanager and local fixture receiver overlay
production-local/monitoring/prometheus.yamlPrivate core/replay scrape jobs and shipped alert rules
production-local/monitoring/alertmanager.yamlLocal-only firing/resolved webhook route, not a real paging service

Disposable dev stack (make dev-up)

FileDescription
docker-compose.yamlThe make dev-up stack: core, RabbitMQ, a second HTTP destination (notify-sink), the replay service, PostgreSQL, and a one-shot migrate. Every variable has a working default. make compose-e2e-test deploys this file and drives it end to end.
dev/core.yamlCore's config for the stack: the GitHub flow from config.yaml, fanned out to RabbitMQ and notify-sink under skip_failed, with the replay: block on. Stop notify-sink and the missed delivery waits in the replay service.
dev/replay.yamlThe replay service's config for the stack: database, core's control URL, retention, workers, and scoped keys.

docs/development/local-development.md walks through sending a webhook, taking notify-sink down, and retrying what it missed.

Replay service, two replicas each (replay/)

FileDescription
replay/docker-compose.yamlCore ×2, the replay service ×2, PostgreSQL, a one-shot migrate, and two HTTP sinks (stop audit-sink for a partial failure that gets captured). docker compose -f examples/replay/docker-compose.yaml up --build.
replay/core.yamlCore config with the replay: block: a skip_failed fan-out whose failed publishes are captured.
replay/replay.yamlThe replay service's config (hookway-replay validate --config …): database, core's control URL, retention, workers, and scoped keys. See docs/operations/replay-service.md.

The dev and replay Compose stacks build a separate console frontend using web/Dockerfile. The dev console is at http://localhost:8082, with the public builder at /builder; its API URL is http://localhost:8081. In the replicated replay example the browser connects to the API replica at http://localhost:8090. For balanced API traffic, supply an independently deployed browser-reachable API load balancer URL through REPLAY_API_URL. Set REPLAY_CONSOLE_ORIGIN to the frontend's exact origin. Console replicas and replay replicas scale independently; fixed local port mappings are a convenience for this demo, not an autoscaling front door. Production ALB/TLS/WAF configuration is deployer-owned.

Provider ingress contracts

FileDescription
providers/webhooks.yamlSigned GitHub, Stripe snapshot, Paddle Billing and Slack Events API flows, including Slack URL verification and provider-specific acknowledgements.

See the compatibility matrix for local test evidence, consumer deduplication, recovery requirements and unsupported modes.

Shared rate limits and duplicate protection

FileDescription
protection/shared.yamlPostgreSQL/Redis stores, a pre-auth IP token bucket, a verified credential rolling quota and shared duplicate reservations.

Set the referenced environment variables, choose the backend used by each node, and migrate PostgreSQL before serving. Both declared stores are checked at startup, so remove unused stores. See Protection stores.

Multi-YAML configuration

One YAML per flow keeps central settings in multi-yaml/core.yaml and references separate orders and billing flow bodies. Single-file configuration remains supported.

More in this section