/** * Cross-machine publish-event types for the build bus * ([[openspec/changes/build-bus-poll-cd/proposal.md]]). * * The local SQLite event bus is per-machine; the build bus extends * **publish events** (specifically) across machines via signed HTTP * webhooks. Subscribers receive an envelope, verify the signature, * deduplicate by eventId, and translate into a local-bus event for * downstream handlers to react to. * * Keep this file dependency-free — types only. Producers and * consumers import from here so the wire format stays in one place. */ /** * The publish event a build-bus emitter fires when a new package * version lands. Carried by the WebhookEnvelope across the network * and re-emitted on the receiver's local bus. */ export interface PublishEvent { /** * UUID v4. Receivers use it for deduplication — duplicate * deliveries (publisher retries on transient network failure) * become no-ops. */ eventId: string; /** ISO 8601 UTC. Used for replay-protection ("not older than N hours"). */ timestamp: string; /** * Which registry this version landed on. `npm` for npmjs.com (or * a verdaccio/cache-node standing in for it); `celilo-registry` * for celilo.computer module publishes. Strings are open-ended * so new registries can be added without changing this type. */ registry: string; /** * The dist-tag (npm) or release-track (celilo-registry) this * event applies to. `latest`, `alpha`, `beta` for npm. * `released` (or similar) for celilo modules. Subscribers can * filter on this — e.g. only fire on `alpha` events to run a * downstream verification job. */ tag: string; /** What was actually published. */ package: { name: string; version: string; }; /** * Optional: the git HEAD at publish time. Stamped by * `celilo publish --alpha` so receivers can correlate a webhook * to the source commit without an extra round-trip. Other publish * modes may omit it. */ gitHead?: string; } /** * Subscriber match rules. A subscriber receives an event when every * non-undefined field of its `match` matches the event. The fields * AND together; absence of a field means "match any." * * Examples: * { registry: 'npm' } — all npm publishes * { tag: 'alpha' } — all alpha publishes (any registry) * { registry: 'npm', packagePattern: '@celilo/*' } * — celilo's own packages, npm only */ export interface SubscriberMatch { registry?: string; tag?: string; /** * Simple glob on `event.package.name`. `*` matches zero-or-more * non-slash characters within a single name; `**` extends across * slashes (useful for scope walks). Matches against the literal * package name string, NOT the dotted form. * * Examples: * '@celilo/*' → '@celilo/cli', '@celilo/e2e' (but not '@celilo/sub/util') * '@celilo/**' → also matches deeper sub-paths * '*' → match any */ packagePattern?: string; } /** * A single subscription target. The operator manages a list of * these; the publisher fans out to each whose `match` rule fires * for a given event. * * `secret` is a per-subscriber HMAC secret. Compromise of one * subscriber's secret doesn't authenticate another's webhook. The * publisher signs the canonical-JSON of the event with this secret * and sends the digest in the X-Celilo-Signature header. */ export interface Subscriber { /** Display name for `celilo subscribers list`. Optional. */ name?: string; /** Endpoint the publisher POSTs to. Must be a full https:// URL. */ url: string; /** Per-subscriber HMAC secret. Loaded from the operator's config. */ secret: string; /** What events this subscriber wants. */ match: SubscriberMatch; } /** * The over-the-wire shape. Subscribers verify the signature against * the canonical-JSON of `event`, deduplicate by `event.eventId`, * then translate into a local-bus event for downstream handlers. */ export interface WebhookEnvelope { event: PublishEvent; /** `sha256=` — HMAC-SHA256 over canonical-JSON of event. */ signature: string; } /** * Delivery outcome the fan-out reports back to the publisher. * Operator-visible via `celilo subscribers status` (or wherever the * delivery log surfaces). */ export interface DeliveryResult { subscriber: Subscriber; ok: boolean; /** HTTP status if a response came back. Undefined on network error. */ status?: number; /** Number of attempts (1 + retries). */ attempts: number; /** Error message if !ok. */ error?: string; /** Total ms spent including retries. */ durationMs: number; }