CortexBuild — Tech Stack Decision Log (v0.1)
For each decision: what we picked, why, what we considered, when to revisit.
Backend
| Component |
Pick |
Why |
Considered |
Revisit |
| Language |
Python 3.12 |
Mature LLM ecosystem; FastAPI is excellent; team familiarity |
Go (binary distribution), TypeScript (single-language), Rust (perf) |
If performance becomes bottleneck (unlikely v1) |
| Web framework |
FastAPI |
Async; OpenAPI auto-gen; WebSocket native; Pydantic |
Litestar (newer), Django (too heavy), Flask (sync only) |
v2 if FastAPI ecosystem stalls |
| ORM |
SQLAlchemy 2.0 (async) |
Industry standard; well-supported |
SQLModel (good but young), raw asyncpg (too low-level) |
Stick |
| Validation |
Pydantic v2 |
Bundled with FastAPI; fastest in class |
attrs (older), msgspec (faster but younger) |
Stick |
| Migrations |
Alembic |
Mature; SQLAlchemy-native |
Skema (younger), raw SQL |
Stick |
| Background jobs |
Postgres LISTEN/NOTIFY (v0) → Temporal (v2+) |
LISTEN/NOTIFY is free + good enough for v0; Temporal is correct for long-running multi-step workflows |
Celery (too painful), Dramatiq (decent), Hatchet (interesting), Inngest (closed cloud) |
v2: adopt Temporal when task graphs exceed 10 steps |
| WebSocket |
FastAPI native |
Sufficient; no separate process |
Socket.io (overkill), Centrifugo (overkill) |
Stick |
Frontend
| Component |
Pick |
Why |
Considered |
Revisit |
| Framework |
Next.js 15 (App Router) |
Mature, opinionated, great DX, easy hiring |
Remix (good but smaller community), SvelteKit (smaller hiring pool), Phoenix LiveView (different paradigm; would limit team) |
Stick |
| Styling |
Tailwind CSS v4 |
Standard |
CSS modules (too verbose), Emotion (slowing) |
Stick |
| Component library |
shadcn/ui |
Copy-paste, full control, no lock-in |
Mantine (heavier), Radix raw (too low-level) |
Stick |
| State |
Zustand + TanStack Query |
Light + powerful |
Jotai (similar), Redux (too heavy), MobX (declining) |
Stick |
| Real-time |
Custom WebSocket + TanStack Query invalidation |
Simple, debuggable |
Pusher (vendor lock), Ably (vendor lock), Liveblocks (overkill) |
Stick |
| Forms |
React Hook Form + Zod |
Best-in-class |
Formik (declining), Conform (too new) |
Stick |
Data
| Component |
Pick |
Why |
Considered |
Revisit |
| Primary DB |
Postgres 16 |
Boring; jsonb; full-text; pgvector available |
MongoDB (wrong shape), CockroachDB (overkill), MySQL (worse jsonb) |
Stick forever |
| Cache / queue broker |
Redis 7 |
Standard |
KeyDB (declining), DragonflyDB (interesting; revisit v2) |
v2: consider DragonflyDB |
| Search |
Postgres FTS (v0) → Meilisearch (v2+ if needed) |
FTS is sufficient for project-internal search |
Elasticsearch (operational nightmare), Typesense (good alt) |
When projects > 1M rows of searchable content |
| Object storage |
S3 (or compatible) |
Standard |
GCS (fine), Azure Blob (if Azure-deployed) |
Stick |
| Audit log archival |
S3 + Athena for old logs |
Cheap cold storage; SQL-queryable |
DynamoDB (too pricey for write-heavy), CloudWatch (vendor-only) |
Stick |
| Embeddings |
pgvector (on Postgres) |
One DB; no separate service |
Pinecone (vendor lock), Qdrant (decent), Weaviate (heavy) |
If pgvector latency degrades at scale; revisit at 10M vectors |
Execution
| Component |
Pick |
Why |
Considered |
Revisit |
| Dev VM substrate |
Daytona (v0/v1) → custom Firecracker (v3+) (planned, Phase 1) |
Daytona is OSS, devcontainer-native, fast |
E2B (SDK-first, fits short tasks), Modal (stateless-focus, wrong fit), Codespaces (lock-in, slow), Coder (good but smaller) |
v3: custom Firecracker for cost & control at scale |
| Sandboxing |
Docker (inside Daytona) |
Standard |
gVisor (overkill), Kata (overkill) |
Stick |
| Network policy |
Custom iptables in workspace base image |
Restrict egress |
Cilium (k8s only) |
v2: consider eBPF if compliance demands |
LLM integration
| Component |
Pick |
Why |
Considered |
Revisit |
| Provider abstraction |
Custom thin interface |
No churn from frameworks |
LangChain (changes weekly), LlamaIndex (heavy), CrewAI (rapidly changing), Llama Stack (interesting; revisit v2), Vercel AI SDK (frontend-focus) |
Stick; consider AI SDK adapters at v2 |
| Token counting |
tiktoken (OpenAI) + anthropic SDK count_tokens |
Provider-native |
Generic estimators (inaccurate) |
Stick |
| Caching |
Redis (prompt hash → response) |
Simple |
LangChain cache (too coupled) |
v2: consider Anthropic prompt caching natively |
| Observability |
OpenTelemetry → Honeycomb (v0/v1) → Langfuse (v2+) |
OTel is standard; Honeycomb best for debugging; Langfuse purpose-built for LLM |
Helicone (good cloud-only), Phoenix (great OSS) |
v2: add Langfuse alongside OTel |
Auth
| Component |
Pick |
Why |
Considered |
Revisit |
| Identity |
Clerk (v0/v1) → custom (v2+) |
Clerk for speed; expensive at scale |
WorkOS (better for enterprise), Supabase Auth (decent), Auth0 (expensive), Ory (OSS but heavy), Better Auth (interesting) |
v2: when monthly Clerk bill > $2k OR enterprise customers demand SSO customisation |
| SSO |
WorkOS (v2+) |
Enterprise SSO standard |
Clerk's enterprise tier, Auth0 |
When first enterprise customer asks |
| Authorization |
Custom + Casbin |
Fine-grained policies; project-scoped roles |
Cerbos (great but separate service), OPA (overkill) |
Stick |
| Secrets at rest |
AWS KMS (or equivalent on chosen cloud) |
Standard |
HashiCorp Vault (operational cost) |
Stick |
CI / CD (for CortexBuild itself)
| Component |
Pick |
Why |
| CI |
GitHub Actions |
Standard |
| Container registry |
GitHub Container Registry |
Same ecosystem |
| Image scanning |
Trivy + Grype |
Best OSS scanners |
| Secrets in CI |
GitHub Actions secrets + OIDC to cloud |
No long-lived keys |
| Deploy |
Fly.io (v0/v1) → AWS ECS (v2+) → AWS EKS (v3+) |
Fly is best DX for early; ECS is sufficient for v2; EKS only when needed |
Telemetry / Observability
| Component |
Pick |
Why |
| Metrics |
OpenTelemetry → Honeycomb |
OTel for instrumentation; Honeycomb for query |
| Logs |
structlog (Python) → Honeycomb |
Structured by default |
| Traces |
OpenTelemetry → Honeycomb |
Single pane |
| Error tracking |
Sentry |
Standard |
| Uptime |
Better Stack |
Cheap, good DX |
| LLM-specific |
Langfuse (v2+) |
Cost + quality dashboards per agent |
Payments
| Component |
Pick |
Why |
| Billing |
Stripe |
Standard |
| Usage metering |
Custom (write to audit_log) |
Simple; tied to our domain model |
| Invoicing |
Stripe Invoicing |
Standard |
| Component |
Pick |
| IDE |
VS Code / Cursor (whatever team prefers) |
| Repo platform |
GitHub |
| Project mgmt |
Linear |
| Docs |
Markdown in repo + Notion for non-engineering |
| Chat |
Slack |
| Design |
Figma |
What we explicitly avoid
- LangChain / LlamaIndex / CrewAI as orchestrator core — API churn breaks everything
- No-code orchestration tools (n8n, Zapier) — too opaque to debug
- Microservices on Day 1 — monolith until proven otherwise
- Kubernetes on Day 1 — Fly.io / ECS until proven otherwise
- Multi-cloud on Day 1 — pick one, master it
- GraphQL — REST + OpenAPI is enough
- Bespoke message bus — Postgres LISTEN/NOTIFY → Temporal is the path