Skip to content

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: ProvisionResource and RemediateFinding are not in this directory — they are Firebase Functions in functions/commands/src/index.ts, deployed via firebase.json rather 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

CommandHandler Directory
BootstrapOrganizationcommands/bootstrap-organization
BootstrapCloudProjectcommands/bootstrap-cloud-project
DelegateCloudProjectcommands/delegate-cloud-project
SyncResourceInventorycommands/sync-inventory
RegisterSubscriptioncommands/register-subscription
RequestIamEscalationcommands/request-iam-escalation
ApproveIamEscalationcommands/approve-iam-escalation
DenyIamEscalationcommands/deny-iam-escalation
RevokeIamEscalationcommands/revoke-iam-escalation
EnrollKernelcommands/enroll-kernel
RevokeKernelcommands/revoke-kernel
RequestKernelSessioncommands/request-kernel-session
ApproveKernelSessioncommands/approve-kernel-session
DenyKernelSessioncommands/deny-kernel-session
Command (Firebase Function)Handler Location
ProvisionResourcefunctions/commands/src/index.ts
RemediateFindingfunctions/commands/src/index.ts

Handler Pattern

Every handler follows this shape:

// 1. Validate command with Zod schema from @mnemose/domain
const cmd = SomeCommandSchema.parse(event.data.message);
// 2. Load aggregate state from event store
const aggregate = await loadAggregate(db, cmd.aggregateId);
// 3. Apply business logic; produce new events
const 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.