hook

package
v0.3.0-20260825223919-... Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 25, 2026 License: Apache-2.0 Imports: 2 Imported by: 0

README

Hook

Vendor-agnostic interface for fire-and-forget side effects run in response to pipeline lifecycle events: warehouse exports, code-host comments, notifications, audit trails. See the hooks framework RFC for the design and api/base/hook for the event contract.

Interface

Hook

Handles one lifecycle event. Name identifies it in logs, metrics, and failure attribution.

Four obligations, all of them consequences of running behind an at-least-once queue:

  • Idempotent on the event id. The same event may arrive more than once, including after a successful Handle. The id is derived from the transition, so a redelivery carries the id the first delivery did.
  • Return nil to ignore an event. There is no filter or subscription API. A hook that does not care about a type returns nil and costs nothing; routing can become a wiring decorator if it ever pays for itself.
  • Return plain errors. Classification is the consumer's job. An error must mean the side effect did not happen — reporting failure for work that succeeded turns at-least-once delivery into repeated duplicate effects.
  • Never write pipeline state. A hook's outcome is invisible to the pipeline, which is exactly what makes it unable to affect the transition that triggered it.
Hooks

Resolves the hooks that run for an event. The controller in platform/hook asks it once per delivery and runs everything it returns; returning none is ordinary and means nothing this host wired cares about the event.

For takes the event rather than a queue name because the envelope carries no queue. Which scope selects hooks differs per domain — queue, source, event type — and only the host that publishes the payload can read a queue out of it, so the choice belongs to the resolver. Resolution runs on every delivery and cannot fail: an integration that cannot be reached is a Handle error, not an absent hook.

Wiring

There is no Config and no Factory here. Selection is the resolver's job, and the resolver is built in the wiring layer — the only place that knows the full set of queues and the integrations wired for each. The host constructs its Hooks and hands it to the controller in platform/hook, which owns the consumer side: decode, validate, resolve, invoke.

Two queues in one host can point at different providers and want different integrations, which is why hooks are resolved per event rather than fixed per deployment.

Implementations

  • noop/ — accepts every event and does nothing. A placeholder for a host that wants the stage registered before it has any integration; a resolver that returns no hooks does the same thing.

A sink that serves several domains is one implementation wired into each domain's host, not one implementation per domain.

Implementing a Hook

  1. Create platform/extension/hook/{name}/ for a hook reusable across domains, or {domain}/extension/hook/{name}/ for one that is domain-specific.
  2. Implement Handle and Name, keying any deduplication on event.GetId().
  3. Decide per event type what to do, and return nil for the types you ignore.
  4. Return it from the host's Hooks resolver for the events it should run on.

Every hook the resolver returns for an event shares one consumer and therefore one retry budget: one chronically failing integration eventually dead-letters events the others handled fine. See platform/hook before wiring several.

Documentation

Overview

Package hook defines the contract for a hook: a pluggable side effect run in response to a pipeline lifecycle event. Warehouse exports, code-host comments, notifications, and audit trails are all hooks.

Which hooks run is a property of the event rather than of the deployment: two queues in one host can point at different providers and want different integrations. A host therefore supplies a Hooks resolver, and the controller in platform/hook asks it once per event.

Hooks run behind a durable queue, never inline in the pipeline, so a slow or failing integration cannot stall or fail the work that triggered it.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Hook

type Hook interface {
	// Handle performs the side effect for event.
	//
	// Delivery is at-least-once, so the same event — identical id — may arrive
	// more than once, including after a successful Handle. Implementations must
	// be idempotent on the event id.
	//
	// Returning nil means "done with this event", which is also how a hook
	// ignores one: there is no filter or subscription API, because a hook that
	// does not care about a type simply returns nil, and routing can be added as
	// a wiring decorator if it ever pays for itself.
	//
	// Returning an error retries the event and, past the retry budget,
	// dead-letters it. Return plain errors; classification is the consumer's
	// job. An error must mean the side effect did not happen — reporting failure
	// for work that succeeded turns at-least-once into repeated duplicate
	// effects.
	//
	// A hook must never write pipeline state. Its outcome is invisible to the
	// pipeline by design: that is what makes the side effect unable to affect
	// the transition that triggered it.
	//
	// ctx is the delivery's, canceled when the consumer shuts down. Honor it
	// and bound the work Handle does: nothing interrupts a Handle that blocks,
	// and the delivery waits for it.
	Handle(ctx context.Context, event *basehook.HookEvent) error

	// Name identifies the hook in logs, metrics, and the failure attribution
	// the controller reports. Stable and unique among the hooks a host wires.
	Name() string
}

Hook performs a side effect in response to a lifecycle event.

type Hooks

type Hooks interface {
	// For returns the hooks to run for event. They run concurrently, so the
	// order of the returned slice does not sequence them: no hook may depend on
	// another having already run.
	//
	// Returning none is an ordinary outcome: it means nothing this host wired
	// is interested in the event.
	//
	// It takes the event rather than a queue name because the envelope carries
	// no queue. Which scope selects hooks differs per domain — queue, source,
	// event type — and only the host that publishes the payload can read a
	// queue out of it, so the choice belongs to the resolver.
	//
	// Called on every delivery, so resolution must be cheap and must not fail:
	// an integration that cannot be reached is a Handle error, not an absent
	// hook.
	For(event *basehook.HookEvent) []Hook
}

Hooks resolves the hooks that run for an event.

Directories

Path Synopsis
Package mock is a generated GoMock package.
Package mock is a generated GoMock package.
Package noop provides a hook.Hook that accepts every event and does nothing.
Package noop provides a hook.Hook that accepts every event and does nothing.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL