# GleamView A **lexicon-driven ATProto AppView** written in [Gleam](https://gleam.run) — a port of [HappyView](https://tangled.org/gamesgamesgamesgames.games/happyview). Upload lexicon schemas and get XRPC endpoints, record storage, Jetstream indexing, and an admin API without hand-wiring AppView plumbing. > **Status:** MVP core. HappyView is ~50k+ lines of Rust (Lua scripts, OAuth/DPoP, WASM plugins, spaces, full dashboard). GleamView implements the schema-first path that makes HappyView useful on day one. ## What works | Area | Status | |------|--------| | SQLite storage (`happyview_*` tables) | ✅ | | Lexicon upload / registry (runtime) | ✅ | | XRPC queries (list + get by `uri`) | ✅ | | XRPC procedures (local index mode) | ✅ (dev) | | API client keys (`X-Client-Key`) | ✅ | | Admin API (lexicons, records, clients, stats) | ✅ | | Jetstream consumer | ✅ (basic) | | DID/PDS resolve helpers | ✅ | | OAuth + DPoP PDS write proxy | ⏳ planned | | Lua / script hooks | ⏳ planned | | WASM plugins, spaces, backfill jobs | ⏳ planned | | Next.js admin dashboard | ⏳ planned | ## Quick start Requires [Gleam](https://gleam.run/getting-started/) and Erlang/OTP on `PATH`. ```bash cd gleamview gleam deps download gleam test gleam run ``` Server listens on `http://127.0.0.1:3000` by default. On first start it prints a **bootstrap admin API key** (Bearer token for `/admin/*`). ```bash # optional cp .env.example .env # DATABASE_URL=sqlite:gleamview.db # JETSTREAM_ENABLED=0 # disable live network sync while developing ``` ## Walkthrough ### 1. Create an API client ```bash ADMIN=gva_… # bootstrap key from startup logs curl -s -X POST http://127.0.0.1:3000/admin/api-clients \ -H "Authorization: Bearer $ADMIN" \ -H "Content-Type: application/json" \ -d '{"name":"demo"}' # → { "clientKey": "hvc_…", "clientSecret": "hvs_…", … } ``` ### 2. Upload a record lexicon + query ```bash # Record type curl -s -X POST 'http://127.0.0.1:3000/admin/lexicons' \ -H "Authorization: Bearer $ADMIN" \ -H "Content-Type: application/json" \ -d '{ "lexicon": 1, "id": "com.example.post", "defs": { "main": { "type": "record", "key": "tid", "record": { "type": "object", "required": ["text"], "properties": { "text": { "type": "string" } } } } } }' # Query that lists that collection curl -s -X POST 'http://127.0.0.1:3000/admin/lexicons?targetCollection=com.example.post' \ -H "Authorization: Bearer $ADMIN" \ -H "Content-Type: application/json" \ -d '{ "lexicon": 1, "id": "com.example.getPosts", "defs": { "main": { "type": "query", "parameters": { "type": "params", "properties": { "limit": { "type": "integer" }, "cursor": { "type": "string" }, "did": { "type": "string" }, "uri": { "type": "string" } } }, "output": { "encoding": "application/json" } } } }' ``` ### 3. Index a record (local write mode) and query it ```bash CLIENT=hvc_… # Procedure lexicon (create) curl -s -X POST 'http://127.0.0.1:3000/admin/lexicons?targetCollection=com.example.post&action=create' \ -H "Authorization: Bearer $ADMIN" \ -H "Content-Type: application/json" \ -d '{ "lexicon": 1, "id": "com.example.createPost", "defs": { "main": { "type": "procedure", "input": { "encoding": "application/json" }, "output": { "encoding": "application/json" } } } }' curl -s -X POST http://127.0.0.1:3000/xrpc/com.example.createPost \ -H "X-Client-Key: $CLIENT" \ -H "X-Gleamview-Did: did:plc:alice" \ -H "Content-Type: application/json" \ -d '{"text":"hello gleamview"}' curl -s 'http://127.0.0.1:3000/xrpc/com.example.getPosts?limit=10' \ -H "X-Client-Key: $CLIENT" ``` `ALLOW_LOCAL_WRITES=1` (default) indexes procedures into SQLite without proxying to a PDS. HappyView always writes through the user's PDS via OAuth/DPoP — that path is next on the port list. ## Architecture Mirrors HappyView's core flow: ```text Client ──GET /xrpc/{nsid}──► Query handler ──► SQLite records Client ──POST /xrpc/{nsid}─► Procedure ──────► (local index | PDS proxy*) Jetstream WS ──────────────► Record handler ─► SQLite records Admin API ─────────────────► Lexicon registry (runtime) ``` | Module | Role | |--------|------| | `gleamview/lexicon` | Parse + in-memory registry + DB persistence | | `gleamview/xrpc` | Catch-all XRPC router | | `gleamview/xrpc/query` | Default list / get | | `gleamview/xrpc/procedure` | Default create / update / delete (local) | | `gleamview/record_handler` | Jetstream / write indexing | | `gleamview/jetstream` | WebSocket consumer (stratus) | | `gleamview/admin` | Admin REST surface | | `gleamview/auth` | API clients + admin keys | | `gleamview/db` | SQLite actor | | `gleamview/resolve` | PLC / did:web helpers | ## Config See [`.env.example`](.env.example). HappyView-compatible names where practical: `DATABASE_URL`, `PUBLIC_URL`, `JETSTREAM_URL`, `PLC_URL`, `PORT`, … ## Source Port of [gamesgamesgamesgames.games/happyview](https://tangled.org/gamesgamesgamesgames.games/happyview) · docs at [happyview.dev](https://happyview.dev). ## License MIT