Architecture

Repository layout

apps/api          Fastify service: REST (/v1), OAuth (/oauth), MCP (/mcp), admin API (/admin/api), webhooks, tasks, docs + admin static
apps/admin        React admin portal (built into apps/api/public/admin)
apps/docs         This documentation (built into apps/api/public/docs)
packages/core     Domain: catalog, pricing, quotes/checkouts/orders state machine, OAuth server, messaging, payments, tasks, admin
packages/db       Drizzle schema + migrations (Postgres in the cloud, PGlite in tests/dev)
packages/snappy-client  Typed Snappy Public API v3 client, fixtures, contract test against the vendored OpenAPI spec
infra/terraform   bootstrap (projects, state, WIF) · modules/platform · envs/sandbox · envs/prod
.github/workflows ci (typecheck, tests, build, tofu validate) · deploy (sandbox on main, prod on approval) · infra (plan on PR, apply on merge)

Runtime (per environment)

                 ┌────────────────────────────── GCP project snappy-agents-<env> ──────────────────────────────┐
 Internet ─▶ Global HTTPS LB (managed cert, Cloud Armor) ─┬─▶ Cloud Run `agents-api`                      │
                       /admin/* ─▶ Identity-Aware Proxy ─┘   (same service; IAP JWT verified in-app)        │
                                                                     │  Direct VPC egress                     │
                                                      ┌──────────────┼───────────────┐                        │
                                                      ▼              ▼               ▼                        │
                                              Cloud SQL Postgres   Cloud Tasks   Secret Manager                │
                                              (private IP, VPC)    (push → /internal/tasks/*, OIDC)            │
                                                      ▲                                                        │
                                              Cloud Scheduler ─▶ /internal/tasks/expire-checkouts (5 min)      │
                 Cloud NAT (static IP) ◀─ outbound to api.snappy.com, api.stripe.com, sendgrid, twilio          │
                 └─────────────────────────────────────────────────────────────────────────────────────────────┘

Why these choices

Choice Reason
Opaque tokens instead of JWT One resource server; instant revocation; nothing to leak from a decoded token
Stripe Checkout (hosted) No PCI scope, no card data in the agent or the platform, Muse's "payment link" model
Quote → checkout split The user sees and approves the exact amount; the price cannot move between consent and charge
Snappy idempotencyKey = checkout id + item index Payment webhooks and task retries can never double-order, even for carts
Credentials via the admin portal into Secret Manager Operators rotate keys without a deploy; nothing secret in Terraform, CI or the database
IAP in front of /admin Google-managed sign-in restricted to the organisation; static admin assets never reach anonymous users
PGlite in tests Full Postgres semantics (unique indexes, JSON) in CI without a service container
Vendored Snappy OpenAPI + contract test Fixture mode stays honest when Snappy's contract changes
Snappy Agents · agents.snappy.com · support@snappy.com