Skip to content
andrew.dunn.dev

postern

Source Releases

postern routes events from cloud storage and message systems to GitLab pipeline triggers. An event arrives, pushed over HTTP or pulled from a queue or topic; postern matches it against YAML routes, pulls the fields a route asks for out of the payload, and calls a GitLab project’s trigger API with them as CI variables. The pipeline stays in that project’s .gitlab-ci.yml. It is the receiving hop of a one-way transfer, the pattern the diode exhibit in exhibits carries end to end.

200 or 503ack or leavePOST, retriedPUSHHTTP webhooksAzure, GCP, CloudEventsPULLconsumer loopsAWS SQS, KafkaCONTAINERposternnormalize to CloudEventsdrop seen event idsmatch YAML routesextract gjson pathsqueue to workersGITLABtrigger APItoken from envTARGET PROJECTpipelinefields as CI variablesCONTRACTcontainer only • no database • secrets from env • bounded dedup • SIGHUP reloads routes
Event sources to GitLab pipeline trigger
  • Push adapters (Event Grid, Pub/Sub, CloudEvents) mount an HTTP handler and check the caller by shared secret, Google-signed OIDC token or bearer token; pull adapters (SQS, Kafka) loop on their own protocol.
  • Every adapter implements one Run(ctx, emit) method and emits CloudEvents 1.0, so nothing past it knows a vendor’s shape.
  • A ring buffer keyed on event id drops duplicates, sized in config and empty after a restart.
  • Matched jobs enter a bounded queue in front of a worker pool that POSTs to the trigger API with retries.
  • A full queue answers 503 to a push and leaves a pulled message in place, so backpressure reaches the producer instead of dropping an event.

A route is the whole of the logic between event and trigger.

EVENTCloudEvents envelopetype, source, id, subject, timedata: the producer’s payloadmatchROUTEmatch + variablestype exact or prefix*VAR: gjson path or shortcutextractTRIGGERproject, ref, variablesproject id and tokennamed from env
One route, from event to CI variables
  • Match is on event type, exact or prefix wildcard, optionally a substring of the source.
  • Variables are envelope shortcuts (id, subject, time) or gjson dot-paths into the payload, @ modifiers refused.
  • The trigger token is named by environment variable and never reaches the config, the logs or the status endpoint.
  • Every config opens with version: 1, required rather than defaulted, so an unknown format fails at startup instead of half-parsing.

postern ships as a container:

  • a static Go binary on a UBI micro base pinned by digest, non-root, signed keyless with cosign through GitLab’s OIDC;
  • /livez, /readyz, /status, /metrics for in-cluster probes and Prometheus;
  • SIGHUP reloads routes and dedup capacity; a source change needs a restart;
  • a Helm chart in chart/.

postern covers the hop between an event and a pipeline trigger and nothing further: no database, no logic on the cloud side, and no state that outlives a restart.