News that ends. Understanding that doesn't.
Open the live app · Create an account · Sign in · Evaluation guide · Architecture
Finite is a working, account-based news application for readers who want to understand the news without an endless feed. It turns English and Malayalam coverage into a bounded, personalised briefing, then lets readers explore the context, different perspectives, history and connections behind a story. Each reading session and two-level exploration has an explicit ending. Saved editions remain available rather than disappearing when a newer edition arrives.
Built from scratch during Devin Hacks Kochi, 24–25 October 2026, by Akash A (akashanil-dev), with Devin and subsequent Codex assistance. AI_USAGE.md records actual contributions and validation; RULES.md records event requirements.
News products make it easy to keep consuming headlines but harder to know when to stop or how to understand a story. Finite combines three product choices:
- A finite commitment: a chosen number of stories, saved read progress, and an ending instead of automatic infinite loading.
- Understanding beyond the headline: summaries at three depths, cited facts, gaps in the coverage, perspectives, chronology and bounded follow-up exploration.
- Visible evidence and limits: publisher attribution, inspectable citations, explicit extractive fallbacks and empty states when support is insufficient.
The intended audience is readers seeking an India-first, English/Malayalam news briefing with context. Finite is a hackathon implementation, not an independently fact-checked news service. Its distinctive behavior is implemented in the application, not just described in this README.
The hosted app requires only a modern browser. No local installation or API key is needed to try the hosted version.
| Destination | Link | Access |
|---|---|---|
| Product introduction | finite.akashanil.dev | Public |
| Create account | Sign up | Public; choose your own credentials |
| Existing account | Log in | Public entry |
| Saved edition library | Briefing | Sign-in required |
| Saved explorations | Knowledge Passport | Sign-in required |
| Publisher methodology | Sources | Sign-in required |
| Interests and delivery preferences | Settings | Sign-in required |
| Service health | API health | Public JSON |
| Build and deployment history | GitHub Actions | Public |
Production runs as two ARM64 containers on Oracle Ampere behind Caddy HTTPS. AI has been activated with a separate $15 cumulative application allowance and a 100-request ceiling. This is shared installation capacity, not a per-user allowance or monthly billing subscription. When capacity is unavailable, supported features fall back to source excerpts. Production scheduling is enabled after explicit owner approval; its first ingestion completed successfully. A real clock-triggered edition has not yet been observed. Activation evidence records that boundary.
- Create an account, choose languages and interests, and set priority levels. Exclude topics or publishers if desired.
- Open Briefing. Prepare your first edition if the library is empty, then open its card.
- Read Quick, Standard or Deep. Open a citation to inspect the source. Mark a story read; progress belongs to your account.
- Swipe vertically between news cards. Swipe horizontally, or use the visible tabs, to switch between Brief, Different views and Explore. Desktop users can use buttons and arrow keys.
- In Different views, open Perspective Lens, Time Machine or Connection Explorer. For the knowledge map, select a branch and compare the evidence on both stories.
- In Explore, choose a question or a Missing Piece. Follow a connection, optionally make a Curiosity Challenge prediction, and reach the second-level ending. Save the expedition to your Passport.
- Return to Briefing: the original card remains. Each original/scheduled edition offers one New edition attempt. Once pressed, that action disappears; extra editions cannot create an unlimited refresh chain.
Coverage-dependent features may legitimately show one publisher, no meaningful connection, no dated passage, or insufficient background evidence. Those states are intentional; the evaluation guide explains what can be verified without relying on a particular changing headline.
The reader uses the screen for the article: edition date and position sit inside its card, and the headline scrolls with the body. Quick/Standard/Deep controls have room for their labels. A compact 56px fixed mobile dock keeps navigation available without the former large footer. Longer content scrolls inside a panel; the next-story button also provides a direct way forward. Desktop uses a navigation rail.
| Compact reader | Saved edition library |
|---|---|
![]() |
![]() |
Screenshots are from the local production build against isolated copied news data, not staged illustrations or proof of physical-device testing. The underlying reporting remains attributed to its publishers. Mobile QA records viewport, persistence and interaction checks.
| Feature | What the application does | Implementation / evidence |
|---|---|---|
| Public landing page | Explains the product and offers separate signup/login entry points | Homepage, design QA |
| Accounts and sessions | Username/password registration, login, logout, revocable cookie sessions; account-owned profiles, briefings and journeys | Auth backend, auth tests |
| Curiosity profile | Weighted subject priorities, English/Malayalam preferences, source/topic exclusions, story count, lookback window, delivery time and timezone | Profile editor, taxonomy |
| News ingestion | Reads 29 configured publisher feeds; classifies articles and lazily extracts full text | Ingestion, source registry |
| Personalised briefing builder | Ranks by interest, source weight and freshness; groups similar coverage, prefers topic diversity, fills remaining slots with ranked eligible news and skips previously delivered articles | Builder |
| Saved edition library | Keeps multiple editions and earlier days, with pagination, dates and read counts | Library UI, history tests |
| One-time extra edition | Atomic server-side claim; original retained, child cannot chain, empty attempts do not create empty cards | API, history tests |
| Scheduled preparation | Background ingestion and preparation 30 minutes before a local delivery time; unique user/date prevents duplicate scheduled editions, including midnight boundaries | Scheduler; unit-tested, real clock-triggered delivery verification remains separate |
| Swipe reader | Finite vertical story cards; horizontal Brief / Different views / Explore panels; buttons and keyboard alternatives; saved read progress and ending | Deck, panels |
| Three reading depths | Quick, Standard and Deep summaries with cited facts and distinct publication/update metadata | Analysis, reader |
| The Missing Piece | Questions about what the retrieved coverage leaves unexplained | Analysis, real-key audit |
| Rabbit holes | Source-backed contextual exploration capped at two levels; claim support checks, evidence gaps and explicit ending | Exploration, retrieval |
| Ask your own | Custom questions scoped to the story; unrelated questions rejected | Exploration API, AI audit |
| Curiosity Challenges | Optional ungraded prediction before level two; cited reveal and reflection, saved with the journey | Challenge UI |
| Perspective Lens | Up to eight distinct publishers, attributed excerpts and checked AI comparison claims; single-publisher and insufficient-evidence states | Perspectives, tests |
| Time Machine | Dated source quotations and a separate publication/update coverage history; unclear dates are not invented | Timeline, tests |
| Connection Explorer | Up to three related stored stories with shared-subject/headline relevance checks and evidence on both sides | Connections, regression tests |
| Interactive knowledge map | Select finite branches to inspect source evidence; switch to list view | Map/list UI |
| Knowledge Passport | Saved journeys, explored concepts and challenge predictions; no streak mechanic | Passport |
| Malayalam support | Malayalam feeds and cached AI translation; original content retained when translation cannot be supported | Translation |
| Source methodology | Outlet tiers, editorial confidence weights and rationales, visible with citations | Sources page, registry |
| Persistent AI guard | Atomic pre-request reservations, bounded model/output sizes, cache reuse, 100-call ceiling and extractive fallback | Budget, tests |
| Container delivery | Checks, ARM64 image build/smoke/publish and automatic SHA-based Oracle deployment on main | Workflow, deployment |
AI is an optional part of the backend, not a requirement for account creation or all features.
- RSS articles are classified and grouped into stories. With AI enabled, embeddings assist grouping; without it, keyword classification and TF-IDF similarity provide an approximate fallback.
- Story analysis uses retrieved publisher passages to produce summaries, facts, questions and concepts. A separate support check evaluates summary assertions, factual claims and question premises. Rejected summaries become literal excerpts, and unsupported items are removed.
- Rabbit holes retrieve story passages and Wikipedia background, write cited context, and check claims against the retrieved evidence. Gaps remain visible; the system does not invent follow-up questions just to fill a branch.
- Perspective comparisons use distinct publishers and an explicit support check. Multiple syndicated or related pages are not automatically proof of independent corroboration.
- Versioned/mode-aware caches reduce repeated calls and avoid retaining obsolete extractive analyses after AI activation.
| Mode | Available behavior |
|---|---|
| With an approved key and remaining allowance | AI classification/grouping, checked analysis and exploration, supported comparison claims, cached Malayalam translation |
| Without a key or when a request is blocked | Extractive summaries/context, source attribution, deterministic connections/map/timeline, accounts, saved editions and Passport; translation may be unavailable |
Default configured models are gpt-6-luna, gpt-6.1-sol and text-embedding-3-small. These names describe the implementation configuration, not a promise of model access for every developer account. Unknown model overrides are blocked by the allowance guard until their rates are reviewed.
Grounding is not a factual guarantee. Retrieval can miss context, sources can be wrong, and model verification can fail. No percentage accuracy or comprehensive editorial validation is claimed. AI_UX_QA.md documents the real-key cases, fallbacks and limits.
FINITE_AI_BUDGET_USDdefaults to $1, accepts $0–$15; this project uses separate development $5 and approved production $15 installations.ai_budget.sqlite3persists conservative reservations across restarts. It is neither actual billed usage nor a monthly reset.- Every uncached call reserves capacity before contacting the provider. Failed calls retain reservations; there are no automatic SDK retries.
- A separate 100-request installation ceiling can stop AI before the dollar allowance is exhausted.
- Cache hits require no reservation. Requests that cannot fit fall back where supported.
- Do not delete/reset the ledger to recover capacity. This guard does not cover other applications or guarantee an account-wide provider billing cap.
flowchart LR
Reader[Browser: Next.js / React] --> Caddy[Caddy HTTPS]
Caddy --> Web[Frontend ARM64 container]
Caddy --> API[FastAPI ARM64 container]
API --> DB[(SQLite: accounts, stories, editions, journeys)]
API --> Cache[(LLM cache + reservation ledger)]
API --> Feeds[Publisher RSS / source articles]
API --> Wiki[Wikipedia background passages]
API --> AI[Optional OpenAI API]
Scheduler[Optional scheduler thread] --> API
Stack: Python 3.13+, FastAPI, SQLite WAL, feedparser, trafilatura, NumPy/scikit-learn, OpenAI SDK; Next.js 16, React 19, TypeScript and Tailwind CSS 4. Production uses same-origin /api routing. The web manifest supports standalone display; offline reading is not implemented.
See ARCHITECTURE.md for data ownership, ranking, API routes, persistence and deployment flow.
Use Python 3.13+, uv, Node.js 24 (matches CI), npm and network access to publisher feeds. Commands below start from the repository root.
git clone https://github.com/akashanil-dev/Finite.git
cd Finite
cp backend/.env.example backend/.envFor a no-spend local evaluation, leave OPENAI_API_KEY empty and set FINITE_SCHEDULER_ENABLED=false in backend/.env. Preparing the first briefing still fetches source feeds on demand. Do not put keys in browser code or commit .env.
Terminal 1:
cd backend
uv sync --frozen
uv run uvicorn app.main:app --host 127.0.0.1 --port 8000Terminal 2, from the repository root:
cd frontend
npm ci
npm run dev -- -H 127.0.0.1 -p 3000Open localhost:3000, register, complete onboarding and prepare an edition. A fresh checkout has no bundled user database or fake news dataset. Publisher availability and your interest choices determine whether stories can be prepared.
For LAN testing, bind both services to 0.0.0.0 and add the exact http://<laptop-ip>:3000 to FINITE_ALLOWED_ORIGINS; the browser defaults to API port 8000 on the same hostname. NEXT_PUBLIC_API_URL can override that address. Production builds use same-origin and proxy /api through Caddy.
| Variable | Meaning |
|---|---|
OPENAI_API_KEY |
Backend-only optional key; empty means extractive mode |
FINITE_AI_BUDGET_USD |
Persistent installation allowance, default 1, maximum 15 |
FINITE_FAST_MODEL / FINITE_SMART_MODEL / FINITE_EMBED_MODEL |
Configured model names; overrides must also be approved in budget rates |
FINITE_ALLOWED_ORIGINS |
Comma-separated exact frontend origins for browser requests |
FINITE_SECURE_COOKIES |
true behind production HTTPS; false for local HTTP |
FINITE_SCHEDULER_ENABLED |
Start background scheduling; use false for controlled evaluation |
FINITE_INGEST_INTERVAL_MIN |
Scheduled ingestion interval, default 60 minutes |
FINITE_DATA_DIR |
Persistent directory, default backend/data; parent must exist |
NEXT_PUBLIC_API_URL |
Frontend build-time API location; same-origin in production |
Sanitized templates: local, production.
Run each subshell from the repository root:
(cd backend && uv sync --frozen && OPENAI_API_KEY='' FINITE_SCHEDULER_ENABLED=false uv run python -m unittest discover -s tests -v)
(cd backend && OPENAI_API_KEY='' FINITE_SCHEDULER_ENABLED=false uv run python -c 'import app.main')
(cd frontend && npm ci && npx next typegen && npx tsc --noEmit && npx eslint src --max-warnings=0 && npm run build)
bash -n deploy/deploy.sh deploy/ssh-dispatch.shLatest implementation checkpoint: 51 backend tests pass, frontend TypeScript/ESLint/production build pass, and 320px/390px plus desktop browser checks pass. Automated tests do not make paid AI calls. Real-key sample checks and production browser evidence are recorded separately:
- Mobile/history QA
- Real AI and exploration audit
- Live account and feature QA
- Design and connection-regression QA
If a restricted environment prevents Turbopack from binding a local port, run the build with normal local-port permissions. Do not replace framework code to hide a sandbox restriction. Avoid rebuilding .next while serving that same production build.
Usernames are case-insensitive, 3–32 letters/numbers/underscores. Passwords are 8–128 characters without composition rules. Password reset and email verification are intentionally absent from this version.
Passwords use salted scrypt. Opaque session tokens are stored as hashes; cookies are HttpOnly, SameSite=Lax, and Secure in production, with a 30-day expiry. Logout revokes the session. Persistent username/IP attempt buckets throttle authentication. Browser writes require an allowed origin and X-Finite-Request: 1; administrative ingestion and budget inspection require an explicit admin role.
Profiles, briefing history/read progress and saved journeys are account-owned. News articles, source analysis and exploration caches are shared content. User questions can be sent to OpenAI when AI mode is enabled; do not enter secrets into exploration prompts. No claim of a full independent security audit is made.
State lives outside Git in backend/data/ locally and /opt/finite/data on the server. Existing legacy data is never automatically assigned to the first account. See architecture and administration for explicit migration/admin commands. Do not delete the database, WAL files or budget ledger as a troubleshooting shortcut.
The CI/CD workflow checks all branches/PRs without AI. Main builds and smoke-tests native ARM64 frontend/backend images, publishes full-commit-SHA tags to GHCR and deploys over restricted SSH when FINITE_DEPLOY_ENABLED=true.
The server pulls images; it does not clone or build the repository. Deployment code and Compose configuration are root-owned. Before changing containers, the deployment script makes integrity-checked SQLite online backups as the app user, avoiding WAL ownership failures. A failed update attempts to restore previous images; this is not an automatic database-schema rollback. Persistent data and budget reservations survive releases.
Deployment guide · server handoff and approval boundary · Compose · deployment script
- AI output is source-checked but not guaranteed correct. Broader editorial evaluation and human Malayalam review remain necessary.
- Some RSS/full-text sources block automated access. No paywall bypass or complete news/archive coverage is promised.
- Without AI, classification and grouping are approximate. One event can appear as more than one story.
- Connection matching is deliberately conservative: weak links are omitted, while synonyms, translations and body-only relationships can be missed. The graph is a bounded navigation view, not a comprehensive semantic knowledge base.
- Time Machine recognizes English date expressions in stored text. Coverage timestamps are not treated as event dates; it is not a complete historical archive.
- Source credibility weights are editorial methodology v0, not an objective truth score. Multi-outlet coverage does not necessarily mean independent reporting.
- Schedules require the server scheduler to be enabled, matching new stories and available resources. Unit-tested timing does not prove a real future delivery has occurred.
- An extra-edition attempt is consumed even if no new matching stories are available. A process crash during first-ever creation can leave a request claim requiring operator inspection.
- Mobile viewport checks passed; physical touch devices, Safari, full accessibility, load testing and disaster recovery drills remain open.
- No password recovery, email verification, offline article cache, push notifications or analytics dashboard is implemented.
- Publisher registry: RSS URLs, outlet names, tiers and rationales. Publisher content remains attributed and links to original reporting; it is not relicensed by this repository.
- Wikipedia API: background passages with source links; Wikipedia text is subject to its applicable attribution/share-alike terms.
- Python dependency lock and npm lock: reproducible library versions. Libraries retain their respective licenses.
- Fonts: Inter, Newsreader and Noto Sans Malayalam. The design references informed palette/composition; no Pinterest artwork was copied.
- AI-assisted development and product AI usage: AI_USAGE.md.
| Document | Purpose |
|---|---|
| Evaluation guide | Demo route, evidence matrix, expected fallbacks and limitations |
| Architecture | Modules, data flow, APIs, ownership and operations |
| Submission | Ready-to-use project description, production/repository links and submission checklist |
| Project context | Canonical detailed decisions, feature history, roadmap and handoff |
| Changelog | Timestamped implementation and validation record |
| AI usage | Devin/Codex contributions, human review and validation boundaries |
| Agent guide / event rules | Development workflow and event requirements |
This README describes implemented behavior and separately labels unverified or planned work. Final organizer submission and final freeze SHA are tracked in SUBMISSION.md.
Each library card is an edition containing multiple stories. Settings changes require Save changes and apply only to future editions; historical editions remain frozen. The requested story count is a target: insufficient eligible, unseen news can produce a shorter edition. The local builder now treats topic diversity as a preference and backfills remaining slots without relaxing exclusions, language, lookback or previously-delivered filters. Library cards show the original requested count for short editions. This correction is uncommitted and has not been pushed or deployed at the owner’s request.
Local edition-library styling now shows a stack of cards with a gentle hover/focus fan-out and reduced-motion support; this design is not deployed.
Local briefing cards now use per-account Brief # numbering and titles summarising their dominant stored topics, rather than the first headline. Opened cards are muted with a persistent Opened badge; opening does not mark stories read. Completed editions offer Read again. Titles are deterministic topic labels, not AI-written factual summaries. These changes remain uncommitted and undeployed.
Local title behavior supersedes the earlier category-label design: covers use news-specific editorial lines cached against their actual headlines. AI-enabled edition creation can generate one line through the existing budget guard; library reads never spend credits. Without AI, uncached editions use source-headline excerpts. Existing akash_a editions have assistant-reviewed, headline-grounded lines prepared locally.
Local reader cards now support marking the entire briefing read/unread from the header and toggling individual stories in the footer. Unread clears completion; opening history remains separate. Changes persist only after server confirmation.
The local stacked-card design, numbering, opened styling and read/unread controls apply to every account and every briefing type, including scheduled and extra editions. They are not restricted to seeded data. Generated editorial titles require AI availability at creation; uncached editions otherwise use headline excerpts.
Passport records exploration rather than mastery: idea counts are saved expeditions containing each label; topic bars compare expedition frequency, with a journey potentially counted under multiple topics. Question steps include repeats, and completed briefings reflect stories marked read.
Hacker News is a discovery source: Finite retrieves the linked page (or supported GitLab README) before summarizing. Unreadable destinations show a preview-unavailable state, with original/discussion links, rather than RSS metadata. Failed source fetches retry after one hour. To repair an existing local database, run cd backend && OPENAI_API_KEY='' FINITE_SCHEDULER_ENABLED=false uv run python scripts/repair_linked_articles.py. This creates an online SQLite backup and refreshes HN content and derived story caches, preserving user records.
The opened briefing uses decorative sheets beneath story cards to indicate remaining stories: two, then one, then no stack on the final story.

