Your local government, decoded. The Civic Decoder turns dense, jargon-filled city-council records into a plain-language, personalized newspaper — and lets you ask anything and get an answer grounded in the real source documents, with citations.
Built for H0: Hack the Zero Stack (AWS + Vercel). Next.js on Vercel + Amazon Aurora PostgreSQL (pgvector) + Amazon Bedrock (Claude + Titan) + Clerk auth. Live: https://civic-decoder.vercel.app
When local journalism disappears, city hall stops being watched. Over 1,800 U.S. communities are now "news deserts." We focus on San Leandro, CA — a real East Bay city whose local coverage has been hollowed out — and the front page proves the point: police-oversight reports, tenant-fee rules, and budget decisions that residents would otherwise never hear about. The records are public; they're just unreadable.
- Pulls real, live records from the Legistar civic-data API (the system most US councils run on), filtered to substantive items.
- Decodes each one with Claude into a plain-language story: what it means, who it affects, the council district, status, category, and concrete actions.
- Personalizes the front page — ranked in Aurora by your interests + district, with an explainable "why this surfaced" line on every story.
- Ask the Desk — semantic pgvector retrieval feeds Claude, which streams a plain-language answer with inline footnote citations to the source records.
- Real documents — each story links the actual Legistar attachments (staff reports, exhibits, resolutions).
Not CRUD-over-a-database — Aurora is the core of three features:
- Personalized ranking is a SQL problem — interest/district boosts + recency + a
procedural-item penalty, computed in the query (
src/lib/civic.ts → getFeed). - "Ask the Desk" and story search are pgvector problems — cosine similarity over a
1024-dim
vectorcolumn with an HNSW index (embedding <=> query). - Accounts — signed-in users' edition prefs and saved stories live in Aurora for cross-device sync.
Embeddings: Amazon Titan v2. Reasoning: Claude (Bedrock). See ARCHITECTURE.md.
- Multi-city, dynamic — a location search switches cities instantly, and an "add your city" box probes Legistar live and sets up any city's paper on the fly.
- Accounts (Clerk) — sign in for cross-device sync + saved/followed stories. No login wall; anonymous reading works fully.
- Daily auto-refresh — a Vercel Cron re-ingests new matters for every loaded city.
- Rate limiting — the Bedrock-calling endpoints are per-IP rate-limited (Aurora-backed).
- Story search + last-updated indicator + weekly-digest waitlist.
# 1. Postgres + pgvector
docker run -d --name civic-pg -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=civic \
-p 5439:5432 pgvector/pgvector:pg16
# 2. env
cp .env.example .env.local # set DATABASE_URL; LLM/EMBEDDINGS=mock to run offline
# 3. schema + seed + run
npm install
npm run db:setup # pgvector + migrations
npm run seed # live San Leandro data (or --client=stockton, etc.)
npm run dev # http://localhost:3000Real AI: set LLM_PROVIDER=bedrock + EMBEDDINGS_PROVIDER=bedrock (or anthropic).
Full cloud setup (Aurora, Bedrock, Clerk, Vercel) → SETUP.md.
| Path | Purpose |
|---|---|
src/lib/city.ts |
client-safe city helpers; the app is config-driven per city |
src/lib/civic.ts |
getFeed (SQL ranking) · searchStories/searchChunks (pgvector) · saved/city helpers |
src/lib/ingest.ts |
extract → chunk → embed → upsert (+ attachments) |
src/lib/sources/legistar.ts |
live civic data + document attachments |
src/app/api/ask |
streaming RAG · add-city · refresh (cron) · save · subscribe |
src/app/{feed,items/[id],saved,onboarding} |
the product |
src/middleware.ts |
Clerk |
- Technical: deliberate relational + vector data model; HNSW pgvector retrieval; live data pipeline; streaming RAG; accounts + rate limiting on AWS.
- Design: an editorial broadsheet, not a SaaS template; cited, streamed answers.
- Impact: a documented news-desert city, decoded with real, citable records.
- Originality: no mainstream product decodes hyperlocal government for an individual.