@founder-os/event-bus
v0.1.2
Published
Cross-Zord event bus — durable pub/sub across founderOS apps. Ships Sprint 5.
Readme
@founder-os/event-bus
Durable cross-Zord pub/sub for founderOS. Ships Sprint 5.
This package is the canonical, signed, idempotent way for one Zord to notify the other eight that something happened. Today it ships as a typed skeleton: the event taxonomy, JSON Schemas, and TypeScript types are stable, but the runtime (Redis Streams backend, signature verification, idempotency store, DLQ) lands in Sprint 5.
Why it exists
Per COMMONS_CHARTER.md §2.7: every cross-Zord wedge demo
(cap-table-aware contracts, pipeline-driven NDAs, hire-triggered
compliance, deal-triggered hiring) requires Zords talking
asynchronously. Today each Zord one-off fetch()es another Zord's REST
endpoint — synchronous, no retry, no signature, no replay. The event
bus replaces all of that.
What ships in v0.0.1 (this release)
EventEnvelope<TPayload>— the wire shape every event uses.Actordiscriminated union —user | system | webhook.EventTypeunion of 25 events (4 GTM + 3 Hiring + 3 Legal + 3 Finance + 2 Strategy + 3 Delegation + 2 Customer Support + 2 Armore + 3 Uniqlabs).- One JSON Schema per event under
schemas/<type>.v1.json, each one Draft 2020-12 valid. - Auto-generated TS payload types under
src/generated/payloads.ts— regenerate withpnpm --filter @founder-os/event-bus generate-types. - Stub
EventBusimplementation that throws "Sprint 5: bus not yet implemented" — keeps types live for IDE autocomplete + lets per-Zord agents scaffold their stub call sites today.
What ships in Sprint 5 (the real runtime)
- Redis Streams backend with consumer groups + XAUTOCLAIM-based redelivery.
- HMAC-SHA256 signature on every envelope (via
@founder-os/shared/webhooks). - Per-consumer-group idempotency via Redis SET NX EX (24h TTL).
- Per-org rate limit via
@founder-os/redis. - DLQ for messages that NACK > N times.
- Observability hooks (Sentry/OTel via
@founder-os/monitoring).
Consuming the stub today
Per-Zord agents can wire the bus into DI today and replace the factory later without touching call sites:
import {
createEventBus,
type EventBus,
type PayloadFor,
} from '@founder-os/event-bus';
// Pin to your Zord at the top of your composition root.
export const bus: EventBus = createEventBus('gtm');
// Calls throw with a clear "Sprint 5" message — but the types narrow.
// IDE autocompletes the payload shape for you.
await bus.publish('gtm.deal.won', {
deal_id: 'deal_123',
amount_usd: 50_000,
closed_at: new Date().toISOString(),
// ...rest of PayloadFor<'gtm.deal.won'>
});For wedge-demo design work, see the per-event payload shapes in
docs/architecture/cross-zord-event-taxonomy.md §4 (canonical) or
schemas/*.v1.json (machine-readable).
Adding a new event
- Append the type to
src/event-types.ts(closed enum). - Drop a
schemas/<type>.v1.jsonfile (Draft 2020-12,additionalProperties: false). - Run
pnpm --filter @founder-os/event-bus generate-types. - Document it in
docs/architecture/cross-zord-event-taxonomy.md§4. - Open a Commons-Charter coordination note so the consuming Zord(s) know it exists.
Versioning rules
- Envelope shape is implicit v1 (the only one today).
- Payload version lives in
envelope.version(default1). - Backward-compatible payload changes (add optional field): same major.
- Breaking payload changes: bump to
<type>.v2.json+ emit BOTH versions for the deprecation window, then drop v1.
Layout
packages/event-bus/
package.json
tsconfig.json
tsup.config.ts
README.md
scripts/
generate-types.mjs # JSON Schema -> TS
schemas/
gtm.deal.won.v1.json
gtm.deal.lost.v1.json
... # 25 schemas total
src/
index.ts # EventBus interface + stub
types.ts # ZordSlug, Actor, EventEnvelope
event-types.ts # EventType union + EVENT_TYPES list
generated/
payloads.ts # AUTO-GENERATED -- do not edit