docs/STACK.md
Stack
One tool per job. Versions live in the manifests and the lockfile. Prerelease integrations get checked against installed code, never against memory.
- UI: Solid 2 and Solid Router. Reads go through the Effect read adapter. No Effect Atom, no query cache.
- Build: Vite+ over a pnpm workspace. The Cloudflare Vite plugin runs SSR in workerd.
- Styling: Panda CSS with
strictTokens,css()/cva()/cx()fromstyled-system. Bespoke keyframes and reveal hooks stay in plain CSS. - Components: native elements first. Kobalte for complex accessible widgets, once its Solid 2 support is verified.
- Icons: Phosphor Bold through
unplugin-icons. The brand and notebook drawings stay custom. - Forms: native controls. TanStack Form only when a real screen needs it and a Solid 2 adapter works.
- Public config: T3 Env with Effect Schema. Secrets and bindings stay on the server.
- Backend: Effect 4 for workflows, typed errors and services. Pure math stays plain functions.
- Protocol: Effect RPC, schemas in
packages/contracts. Plain HTTP for Better Auth, webhooks, health and downloads. - Worker adapter: Alchemy's Effect Worker runtime. Init builds storage, link tokens and auth once per isolate. Domain services are built per request (STRUCTURE.md).
- Database: D1 through Drizzle, wrapped in the
Databaseservice. Alchemy applies migrations. - Auth:
@alchemy.run/better-authon the Drizzle adapter, over the lazy D1 handle. - Upload transport: Uppy, straight from the browser to private R2 multipart. A pnpm patch makes its
ListPartsfollow pagination. - Browser recovery metadata: idb-keyval.
- Part hashing for resume verification: hash-wasm MD5.
- Multipart signing: the
Storageservice signs Distilled S3 requests against the R2 endpoint, using a bucket-scoped API token Alchemy mints per stage. - Billing, not built: Polar. Entitlements would live in D1 and get reconciled from webhooks.
- Infrastructure: Alchemy v2 in
infra/alchemy.run.ts, on the app's Effect version. - Local URLs: Portless.
- Lint and format: oxlint with type-aware rules and Solid diagnostics, oxfmt. Rules live in
lint.config.ts. - Later, when real work needs it: a maintenance Worker or Queues for cleanup, KV for cache only, Electron with a separate Bun transport process and SQLite for desktop.
- Observability, before launch: PostHog for product analytics, Axiom for the log and trace drain, Sentry for error capture.
- Emitter, executor-validated: Effect spans to
WebTracerProviderto batch OTLP atapi.axiom.co/v1/traces. Their solved pitfalls: the browser-platform OTLP exporter build (node build crashes in workerd), lazy per-isolate provider (module-scope I/O fails deploy), Batch not Simple span processor,waitUntil(forceFlush), URL and header redaction on span attrs. Browser spans forward through an edge route with server-held creds; PostHog server-side via direct HTTP, neverposthog-node; e2e asserts spans against a local OTLP collector. Alchemy ships@distilled.cloud/axiom.
Upload, recovery, integrity and abuse rules live in RELIABILITY.md. Code placement lives in STRUCTURE.md.