CAF Demo — Read-Only Static vs Live App
Side-by-side per founder's request (2026-06-15 night), after the live-edit + generate-on-call requirement surfaced (see [[2026-06-15-caf-architecture-decisions-and-meta-council]] decision D, the open fork). Founder's framing: "Read-only may still be good for the hardened second pass, especially if many of the same rails are used." — which is exactly the synthesis below: they're not either/or.
A. Read-only static demo (the original quick-sites pattern)
- What it does: browse a per-client catalog (cards → detail modal → capabilities mapping). No editing, no live generation.
- Data flow: the agent reads the catalog store once at generation time → emits a per-client JSON bundle → a static site renders + filters it client-side. The site connects to no database.
- Stack: static Astro (or any SSG) → S3 + CloudFront, or Amplify (static). Gated per client via CloudFront + Cognito / Amplify access control.
- AI: none live (any generation happens offline, by the agent, before the bundle is built).
- Security: smallest possible attack surface — no DB, no writes, no live AI. A static per-client snapshot literally cannot leak another client's data (it isn't in the bundle). This is decision-A's external isolation chokepoint.
B. Live app (edit + generate-on-call)
- What it does: reps edit use cases in the UI and persist them; generate a new use case live on a call via the Anthropic SDK.
- Data flow: the app reads/writes Postgres/RDS live; server endpoints handle CRUD and the Anthropic SDK (input → Claude → persist → return).
- Stack: full-stack framework (Next.js / Remix / SvelteKit) on server compute (Amplify SSR/functions, or Lambda / ECS / App Runner) + RDS (Postgres) in a VPC + an auth layer (Cognito).
- AI: live, server-side only (API key never in the browser).
- Security: larger surface — live DB, writes, auth, server, AI orchestration all need hardening.
Side-by-side — the cost/complexity delta
| Dimension | A · Read-only static | B · Live app |
|---|---|---|
| Core capability | Browse a per-client catalog | Edit + persist + generate-on-call |
| Writes | None | Full CRUD |
| DB at runtime | None (JSON bundle) | Postgres/RDS live |
| Framework | Astro / any SSG | Next.js / Remix / SvelteKit |
| Server compute | None (static) | Required (SSR/functions or Lambda/ECS) |
| AI | None live | Anthropic SDK, server-side |
| Auth/gating | CloudFront+Cognito / Amplify access | Cognito (login + roles) |
| Hosting | S3 + CloudFront / Amplify static | Amplify SSR or Lambda/ECS + RDS in VPC |
| Rough cost | ~pennies–low-$/mo (static + a batch read) | RDS floor (~$15–60/mo small/serverless-v2) + compute + per-generation Claude API; scales with usage |
| Ops burden | Very low (rebuild + redeploy) | Real (DB ops, backups, auth, server, security, AI) |
| Attack surface | Minimal | Meaningful — needs hardening |
| Best fit | External, client-facing, hardened delivery | Internal, rep-facing authoring tool |
Headline delta: B adds a database, server compute, auth, and an AI backend — each a real ops + security commitment. A is nearly free and nearly unbreakable but read-only.
Shared rails (why building both isn't ~2x)
Founder's instinct is right — most of the system is common:
- Data model / schema — one (the ERD). Postgres is the live store; the static bundle is just a query/export of the same data.
- The JSON / display contract — the per-client bundle the static site renders is the same shape the live app serves its frontend. Define it once.
- UI components — the catalog cards, the use-case detail modal, the capabilities-mapping view render the same contract → built once, used by both (a shared component lib, or the static site reuses the app's components in SSG mode).
- Generation logic — the prompts/contract for producing a use case are shared between the offline agent (bundles) and the live server endpoint (on-call generation).
- Auth + AWS account + IaC + CI — one Cognito, one account, one deploy pipeline.
- Tenancy model — the same "open internal, hard-walled external" call.
So the incremental cost of A on top of B (or vice-versa) is mostly the export/render pipeline, not a second system.
The synthesis (recommended) — one system, two surfaces
This is exactly the founder's "read-only as the hardened second pass":
- Live app = the internal authoring source of truth. Reps edit + generate against Postgres. Internal-only (per the tenancy model).
- Static read-only = a hardened external export — a per-client snapshot of the live app's data, rendered with the shared components into a static, gated, no-DB site. It's the safest possible thing to put in front of a client (no live data path to leak) and it's decision-A's isolation chokepoint made literal.
- Build order:
- Shared rails — schema (Postgres) + JSON contract + shared UI components.
- Live app (Next.js-class + RDS + server-side Claude) — the internal authoring tool. This is the harder, higher-value piece; build it first so it's the source of truth.
- Static export — the "second pass": snapshot a client's data → render with the shared components → hardened read-only demo. Cheap once the rails exist.
Open decisions for founder
- Framework for the live app: Next.js (largest ecosystem, first-class on Amplify + Vercel, server actions for the edit/generate flow) is my lean; Remix or SvelteKit are fine if the team prefers. Your call.
- Confirm the tenancy mapping: live app = internal-only; static read-only = the external client deliverable? (This is the clean version of "open internal, hard-walled external.")
- Snowflake's role: source-of-truth/analytics behind Postgres (sync Postgres→Snowflake), or drop Snowflake for this app and let Postgres be the store? (The catalog is small; Postgres alone may be enough.)
- RDS shape: Aurora Serverless v2 (scales to near-zero between calls) vs a small provisioned instance.
Cross-references
- [[2026-06-15-caf-architecture-decisions-and-meta-council]] — decision D + the open fork this resolves
- [[2026-06-15-caf-catalog-data-model-erd]] — the shared schema (already VARIANT→JSONB portable)