This repository has no description
ideonomics.app docs 07-stack.md
5.3 kB
Markdown
at dev

07 · Stack #

Choices #

Rust + axum for the server; maud (or askama, if templates-as-files are preferred) for HTML — maud's compile-time fragments pair naturally with HTMX's fragment-swap model. HTMX 2.x from CDN (major-pinned), no build step. SQLite via sqlx for the AppView index, with a clean seam to Postgres.

jacquard for atproto (verified current as of mid-2026; note it is pre-1.0 and its maintainer warns of breaking changes between minors — pin versions and read the changelog on upgrade):

Crate Role
jacquard Agent, sessions, re-exports
jacquard-oauth OAuth + DPoP, auto token refresh, SessionRegistry
jacquard-derive #[lexicon] (adds extra_data for unknown fields), #[open_union], #[derive(IntoStatic)] — custom app.ideonomics.* record types as plain Rust structs
jacquard-identity handle→DID→PDS resolution chains
jacquard-axum server-side XRPC extractors, if we expose our own XRPC endpoints (note: was temporarily out of workspace during a redesign — check status)

Key jacquard idioms: types are generic over a backing string (S: BosStr, default SmolStr); use .into_output() for owned results; use AgentSessionExt::create_record / get_record with the Collection trait (const NSID) for typed record I/O; Data<S> (never serde_json::Value) for untyped atproto values. Custom record structs get #[lexicon] + a Collection impl with NSID = "app.ideonomics.completion" etc. Canonical lexicon JSON schemas should also be authored under lexicons/ and published via the standard DNS-based lexicon resolution for the app.ideonomics authority.

Alternative considered: atrium (atrium-api, atrium-oauth) — mature, wider adoption, but custom lexicons and OAuth involve substantially more boilerplate. Fallback if jacquard's pre-1.0 churn bites.

Jetstream consumer for ingestion (a small tokio task; the rocketman crate exists if hand-rolling the websocket is unappealing), filtered to wantedCollections=app.ideonomics.*, cursor persisted in SQLite.

Interpreter LLMs behind a trait (Glosser) with an HTTP implementation configured by env; ensemble = Vec. Never called in the request path (04).

Process shape #

One binary, three tokio task groups: (1) axum server, (2) Jetstream consumer + gloss worker (mpsc queue between them), (3) cron-like slow-loop scheduler. Vertical monolith until measurement says otherwise; the loops are decoupled by design (04), so splitting into services later is a deployment change, not an architecture change.

Routes #

GET  /                      landing; handle-input login
GET  /client-metadata.json  OAuth client metadata (public URL required)
POST /oauth/login           resolve handle → PAR → redirect to PDS auth
GET  /oauth/callback        code exchange; cookie session → jacquard SessionRegistry
POST /oauth/logout

GET  /session/next          HTMX fragment: next square (fast loop)
POST /session/complete      write completion to user PDS; return next fragment
POST /session/resonate      write resonance record; return fragment

GET  /sky/{handle}          full constellation page (SSR SVG)
GET  /sky/{handle}/term/{t} term-focus fragment
GET  /sky/{handle}/card.png OG/badge render (same renderer, card size)
GET  /compare/{a}/{b}       shared-term contrast (consent-gated)
GET  /atlas                 population view
GET  /export                the user's records + current derived state, JSON
POST /snapshot              explicit publish: write app.ideonomics.snapshot

Session cookies are opaque server-side IDs mapping to jacquard OAuth sessions (per-DID) in a SQLite-backed auth store; jacquard handles DPoP nonces and token refresh.

AppView schema (sketch) #

Tables: completions, squares, glosses, resonances (mirrors of records, keyed by AT-URI, indexed by DID); terms, edges (the per-user graph, edge = canonical relation with weight/sign/gloss-variance); posteriors (per-user mixture weights, updated by medium loop); quilts, discrimination (slow-loop outputs); layouts (versioned constellation cache); pairings (comparison consent); cursor (Jetstream).

Order of construction #

  1. OAuth flow + write/read one completion record end-to-end (thin slice through the riskiest dependency).
  2. Square bank generation offline; serve static-bank sessions with chaining (no inference — random-containing-your-last-term selection).
  3. Jetstream consumer + AppView mirror tables.
  4. Gloss worker + canonicalization; term-graph construction.
  5. Constellation SSR (renders honestly even from crude graphs).
  6. Hierarchical model + adaptive selection + evaluation harness (05) — last, because everything upstream produces data worth having even while selection is naive.

Caveats recorded during research #

Version facts checked July 2026: jacquard ~0.11–0.12 across crates, active weekly releases; atrium-api ~0.25.x; both ecosystems move fast — re-verify at implementation time. jacquard-axum's extractor redesign status should be checked before committing to server-side XRPC endpoints (we may not need any: the app's own HTTP surface is plain axum + HTMX, and XRPC is only needed if we expose the AppView to other clients — which the philosophy says we eventually should).