commands — Architecture
The commands/ directory contains one Cloud Run Function per Mnemose command handler. Each handler is a stateless function that processes a single command type, applies business logic, writes events to the event store, and publishes resulting domain events to Pub/Sub.
Note:
ProvisionResourceandRemediateFindingare not in this directory — they are Firebase Functions infunctions/commands/src/index.ts, deployed viafirebase.jsonrather than as built container images.
Design Principles
- One function per command — bounded blast radius; independent deployability; isolated scaling
- Idempotent — every handler deduplicates on
commandId; re-delivery from Pub/Sub is safe - Event-first — handlers emit domain events; they do not call other services directly
- No shared mutable state — all state lives in Postgres (via
@mnemose/db) and is read via the aggregate’s event stream
Command Handlers
| Command | Handler Directory |
|---|---|
BootstrapOrganization | commands/bootstrap-organization |
BootstrapCloudProject | commands/bootstrap-cloud-project |
DelegateCloudProject | commands/delegate-cloud-project |
SyncResourceInventory | commands/sync-inventory |
RegisterSubscription | commands/register-subscription |
RequestIamEscalation | commands/request-iam-escalation |
ApproveIamEscalation | commands/approve-iam-escalation |
DenyIamEscalation | commands/deny-iam-escalation |
RevokeIamEscalation | commands/revoke-iam-escalation |
EnrollKernel | commands/enroll-kernel |
RevokeKernel | commands/revoke-kernel |
RequestKernelSession | commands/request-kernel-session |
ApproveKernelSession | commands/approve-kernel-session |
DenyKernelSession | commands/deny-kernel-session |
| Command (Firebase Function) | Handler Location |
|---|---|
ProvisionResource | functions/commands/src/index.ts |
RemediateFinding | functions/commands/src/index.ts |
Handler Pattern
Every handler follows this shape:
// 1. Validate command with Zod schema from @mnemose/domainconst cmd = SomeCommandSchema.parse(event.data.message);
// 2. Load aggregate state from event storeconst aggregate = await loadAggregate(db, cmd.aggregateId);
// 3. Apply business logic; produce new eventsconst newEvents = handle(aggregate, cmd);
// 4. Append events to store (optimistic concurrency on sequence number)await appendEvents(db, newEvents);
// 5. Publish to Pub/Sub for subscribers (agent, kernel-broker, projections)await pubsub.publishAll(newEvents);Deployment
Each handler deploys as an independent Cloud Run Function with a Pub/Sub push trigger. The Pulumi stack in infra/ provisions the function, trigger, and necessary IAM bindings from a single deployCommandHandlers() call.