原始内容
Bloodbank
The event backbone of the 33GOD ecosystem. Dapr over NATS JetStream, CloudEvents
on the wire, repo-local contracts under schemas/ driving both sides.
This repository contains the self-hosted sandbox (Docker Compose), reference
services that exercise the platform, the operator CLI (bb), and the operator
workflows (bootstrap, smoketest, replay, trace).
Architecture
- Broker: NATS JetStream. Two durable streams:
BLOODBANK_EVENTSfor immutablebloodbank.evt.v1.*traffic,BLOODBANK_COMMANDSforbloodbank.cmd.v1.*/bloodbank.rpy.v1.*round-trips. round-trips. Topology incompose/nats/streams.json. - Runtime: Dapr sidecars. Pub/sub component fronts NATS via
compose/components/pubsub.yaml. State and secret stores are siblings under the same directory. - Wire format: CloudEvents 1.0 (JSON), with
correlationidandcausationidon every envelope. - Schemas: Canonical source-of-truth lives in
schemas/in this repo (seedocs/event-naming.md§12). Runmise run validate:schemasto verify the tree. Runtime lookup falls back to a siblingholyfields/schemas/while downstream consumers cut over. Re-extraction into a neutral registry repo becomes appropriate once two or more serious consumers live outside Bloodbank. No envelope is ever invented outside the schema tree. - Discovery: EventCatalog. The mount point at
compose/eventcatalog/siteis populated from the local schema tree. - Durable audit trail: Candystore lives in the sibling
../candystorerepository and is run by thecandystorecompose profile. Seedocs/candystore-integration.mdfor the ownership boundary and runtime wiring.
ADR-0001 in the metarepo ratifies these decisions (TBD; not yet committed).
Quick start
# Sanity-check the scaffold without booting Docker.
mise run doctor
mise run bootstrap
# Boot the core sandbox.
mise run up
# Run the full smoke battery once everything is healthy.
mise run smoketest
mise run smoketest:command
mise run smoketest:dapr
mise run smoketest:dapr-subscribe
mise run smoketest:heartbeat
The sandbox compose project is bloodbank; everything attaches to the
bloodbank-network bridge and binds host ports in the 3500–3603 / 4222 /
8080 / 3000 range. See compose/README.md for details.
Repository layout
| Path | Contents |
|---|---|
compose/ |
docker-compose.yml + Dapr/NATS/Apicurio/EventCatalog/Candystore wiring |
cli/ |
bb operator CLI (doctor, trace, replay, emit) |
ops/bootstrap/ |
Pre-boot file-presence validator |
ops/smoketest/ |
End-to-end round-trip tests |
ops/replay/ |
Operator-facing replay workflow contract |
ops/trace/ |
Correlation/causation walkthrough |
services/ |
Reference services (heartbeat producer/consumer, lifecycle controller, agent hooks) |
adapters/ |
Migration scaffolds for legacy producers (blocked on Holyfields) |
Operator CLI
Run any subcommand without arguments to see usage:
python3 cli/bb.py doctor # scaffold present-and-correct
python3 cli/bb.py trace ... # walk a correlation chain (forthcoming)
python3 cli/bb.py replay ... # replay an event into the sandbox (forthcoming)
python3 cli/bb.py emit ... # publish a handcrafted event (forthcoming)
doctor is the only subcommand with real behavior today; the others are
intentional stubs that will be filled in as the operator surfaces land.
CI
.github/workflows/ci.yml runs two jobs on every PR to main:
- static-checks —
compileallthe CLI, runbb doctor, validate every Dapr component manifest,streams.json, the compose config, and shellcheck the operator scripts. - smoke-tests — boot the sandbox layer-by-layer and run all five smoke tests end-to-end against real NATS + Dapr containers.
Conventions
- Subjects:
bloodbank.evt.v1.<domain>.<entity>.<action>,bloodbank.cmd.v1.<domain>.<entity>.<action>, andbloodbank.rpy.v1.<domain>.<entity>.<action>. - Every envelope carries
correlationidandcausationid. - Sandbox identifiers all use the
bloodbankprefix (project, network, containers). No version suffix. - Schemas live under
schemas/. Adapters translate; they do not invent.
Anti-patterns
- No service-to-service calls that bypass NATS.
- No locally-defined envelopes.
- No synchronous I/O in event handlers.
- No "central publisher" service — production traffic flows through Dapr sidecars embedded with each service.