Inspiration

Most "AI" you see is pointed at making something faster or flashier. We wanted to point it at one of the most human problems there is: a language goes silent somewhere on Earth about every two weeks, and the people losing theirs are overwhelmingly Indigenous and marginalized communities who already have the least support.

We kept picturing one person — a grandparent who still dreams in a language their grandchild was never taught, one of the last in the family who can speak it. Her language has no app and no course, not because no one cares, but because writing a language down and teaching it has always taken trained experts years, and most languages will never get that time. We wanted to put that power in the hands of the person who actually holds the words — not a researcher, not a company. So we built Lantern: AI that actually helps a person keep their own language alive, and that a non-expert can use on their phone.

The proof (live — anyone can verify it right now)

Every number below is recomputed on each request at https://lantern-cyan.vercel.app/api/metrics. It is not a screenshot.

  Hallucinated words that ever reached a learner
  0          zero. always. enforced in code by guardrail.check.ts
             |__________________________________________________

  Vocabulary with a real, cited source
  48 / 48    ##############################   100%

  Practice sentences passing the attestation gate
  7 / 7      ##############################   100%

How Lantern differs from a normal "AI tutor":

  +------------------------------+-------------+--------------------+
  |                              | Generic AI  |      LANTERN       |
  +------------------------------+-------------+--------------------+
  | Invents words to fill gaps   |    yes      | forbidden in code  |
  | Cites a real source per word |    no       | 48/48   (100%)     |
  | Works for ~50-speaker langs  |    barely   | built for it       |
  | Proves its honesty live      |    no       | /api/metrics       |
  | Demo locked behind sign-in   |    often    | never -- fully open|
  +------------------------------+-------------+--------------------+

What it does

Lantern is Duolingo for languages that are dying. Pick a language so endangered that no app, no textbook, and no online course exists for it. Lantern takes a handful of remembered phrases, works out the grammar hidden inside them, and builds a real course you can actually learn from:

  • flashcards with the word, the meaning, and pronunciation you can hear out loud;

  • the grammar it discovered, explained in plain language (for Māori, how a small word before the verb changes past, present, and future);

  • a vocabulary bank where every word shows the phrase it came from;

  • and a Contribute button: add one phrase you remember, like Ka pai ("good"), and the whole course rebuilds itself, richer, in seconds.

It comes pre-loaded with eight endangered languages; two of them — Māori and Cherokee — are fully learnable right now. The whole thing works on a phone, and a stranger understands what it does in one sentence.

Try it live at https://lantern-cyan.vercel.app — watch it learn Māori from 41 phrases, see the grammar it found, and take the course it built.

The one rule it never breaks is the most important part: it only ever teaches words a real speaker actually said. An AI can't truly know a language with fifty speakers, so instead of letting it make things up, Lantern only reorganizes and teaches the community's own words — and a check built into the code throws away any sentence containing a word nobody actually used.

How we built it

The anti-hallucination pipeline, end to end:

   community corpus (only real, cited phrases)
                |
                v
   tokenize + normalize         one canonical, case-folded pass
                |
                v
   induce grammar + vocab       Llama 3.3 70B on Groq
                |
                v
   [ GUARDRAIL: CITE or REJECT ]   is each candidate word attested?
          |              |
        yes|           no|----->  DISCARDED (never reaches a learner)
          v
   course: SRS flashcards + grammar notes + in-browser audio

If the model is ever unreachable, the engine fails soft to a hand-verified fixture, so Contribute never throws and the demo stays honest and online.

  • A web app in Next.js 16 + TypeScript, deployed on Vercel, so it runs on any phone or laptop with no install.

  • An AI induction engine that reads only the phrases it's given, lines up each word with its meaning, and spots grammar by comparing similar phrases. We force the AI to return clean, structured data and double-check it with Zod before anything is shown.

  • A no-hallucination guardrail written directly into the code: every sentence is split into words and each is checked against the real vocabulary; if even one word was never actually said, the sentence is deleted before any learner sees it.

  • A flashcard course on the SM-2 spaced-repetition algorithm with in-browser text-to-speech so you can hear the words.

  • Live AI runs an LLM — Llama 3.3 70B on Groq, driven through the Anthropic SDK — with a hand-verified backup so the demo always works.

Accomplishments we're proud of — and we measured it

We didn't just want to say it helps and that it's honest, so we measured it:

  FROM 41 MĀORI PHRASES, LANTERN BUILT
  ──────────────────────────────────────────────
    34   words you can learn, each one real and cited

     5   grammar patterns it discovered on its own

    12   flashcards on a smart review schedule
  ──────────────────────────────────────────────
     0   made-up words ever shown to a learner       ✓

  48/48  words backed by a real source               ✓

   7/7   practice sentences pass the honesty check   ✓

Reproducible live at GET /api/metrics. So: zero made-up words ever reach a learner, enforced by the code, not just a promise; it's genuinely clear; the interface is warm and easy so you never feel lost; and it's real and live, not a slideshow — the AI runs on the actual website.

And we built it to production quality, measured on the live site:

  Lighthouse (every page, production)
  Accessibility    ##########   100
  Best Practices   ##########   100
  SEO              ##########   100
  Performance      ##########   ~100    (LCP 241ms,  CLS 0.00)

Worked example -- the guardrail rejecting an invented word: a model asked about Maori water-spirits will gladly produce "taniwha" lessons. But if "taniwha" is not in THIS community's corpus, Lantern discards it on screen, live, in the induction demo. The learner only ever sees words a real speaker actually said.

Why this is "AI that actually helps people"

  • It solves a real, human problem for real people — not a demo, a tool a community can use today.

  • It's usable by a non-expert: a grandparent, a kid, anyone who remembers a few words; no AI knowledge required.

  • It's honest by design — the most respectful thing AI can do with someone's heritage is refuse to invent it. We made the AI do less, on purpose, and that's exactly what makes it trustworthy and genuinely helpful.

What we learned

That the most helpful AI is sometimes the one that does less. By refusing to invent, Lantern became something a whole community — and even a language expert — could trust, and trust is everything when you're handling someone's heritage. We also learned how much a clear, kind interface matters: the best technology does nothing if the person who needs it can't use it.

What's next

Real recordings from native speakers, a way for fluent speakers to review and approve lessons, printable booklets for communities without good internet, and controls so each community fully owns and governs its own words. The dream is a living library with a place for every endangered language, where anyone who still remembers can help bring theirs back — starting today.


Technical deep dive — the whole system, end to end

This is the complete engineering account of Lantern: every layer, every decision, every fail-safe, and the history of how it was built. The sections above are the "why." This is the "how," in full.

1. Architecture at a glance

Lantern is one Next.js 16 application (App Router, React Server Components, Turbopack) written end to end in TypeScript. There is no separate backend service: server logic lives in Server Components and Route Handlers on Vercel's Node runtime, right next to the UI that consumes them.

                      THE BROWSER (any phone or laptop)
  +-----------------------------------------------------------------+
  | React Server Components (streamed HTML) + small client islands  |
  | Hero . LiveInduction . Workspace . Flashcards . ContributeForm  |
  +----------------+--------------------------------+---------------+
                   | server-rendered                | fetch() /api/*
                   v                                 v
  +-------------------------+        +------------------------------+
  | Next.js 16 (Vercel Node)|        | Route Handlers (/api/*)      |
  | RSC data loading        |        | contribute induce audio      |
  | layout pages metadata   |        | metrics stats auth/[...]     |
  +-----------+-------------+        +--------------+---------------+
              |                                     |
              v                                     v
  +-----------------------------------------------------------------+
  |              THE INDUCTION ENGINE  (src/lib/engine)             |
  |  tokenize -> normalize -> induce(LLM) -> GUARDRAIL(cite|reject) |
  |  -> attestation gate -> lesson assembly                        |
  +----+-------------+-------------+----------------+---------------+
       v             v             v                v
   +-------+    +---------+   +----------+    +-------------+
   | Groq  |    | MongoDB |   | Firestore|    | Vercel Blob |
   | LLM   |    | Atlas   |   | users /  |    | pronunciation|
   |3.3 70B|    | corpus  |   | auth     |    | audio (CDN) |
   +-------+    +---------+   +----------+    +-------------+
       |             |             |                |
       v             v             v                v
   fixture       in-memory     graceful         503 fail-soft
   fallback      fallback      auth gating      (storage off)

  Every external dependency has a fail-soft path. Nothing here can take the
  demo down.

2. Request lifecycle (a learner opening a language)

  1. GET /lang/mi                (Server Component, Vercel Node, force-dynamic)
  2. getLanguageMeta("mi")       -> record; unknown id -> notFound() -> 404
  3. getStore()                  -> MongoDB Atlas (or in-memory if no URI)
  4. store.getPhrases("mi")      -> the cited corpus for this language
  5. <Workspace> hydrates; Learn tab calls the engine
  6. runInduction(corpus):
       tokenize + normalize every phrase (canonical, case-folded)
       LLM induces vocab + grammar FROM THOSE PHRASES ONLY
       Zod validates the LLM JSON (reject malformed shapes)
       GUARDRAIL drops any word not attested in the corpus
       assemble SRS cards + practice (attestation-gated)
  7. The learner sees only words a real speaker said. Always.

/api/metrics runs this exact path, so the honesty numbers are computed by the same code that builds lessons, not a separate flattering report.

3. The anti-hallucination guardrail, in full

A normal model on a tiny corpus invents plausible words to fill gaps. For a 50-speaker language that invented word can outlive the last elder. We made it structurally impossible.

  • Canonical tokenization: one tokenizer, one normalizer, case-folded, no stale copy anywhere (a project invariant). Two strings are "the same word" only after both pass through it.
  • Cite-or-reject: every candidate word the model proposes is checked against the tokens that actually appear in the community's phrases.
  for each candidate word w:
     if normalize(w) in corpusTokens:   KEEP   (record its citation)
     else:                              REJECT (never reaches a learner)

No confidence threshold, no "probably fine." 100% citation coverage is not a metric we hope for; it is an invariant the code refuses to violate.

  • Attestation gate on generated sentences: practice sentences are made by recombining known words; each is re-checked word by word, and if one token was never said, the whole sentence is deleted. That is why "practice sentences failing attestation" is always 0.
  Worked example -- rejecting "taniwha"
  corpus: 41 attested Maori phrases (no "taniwha")
  model wants to teach "taniwha"
  guardrail: normalize("taniwha") in corpusTokens? NO -> DISCARDED on screen
  learner sees only the 34 words the 41 phrases actually contain

guardrail.check.ts runs the full induction over the shipped corpora and asserts 0 hallucinations and 100% citation, or it fails the build ritual.

4. Frontend

  • Next.js 16 App Router with React Server Components: the hero and story stream as server HTML so there is no empty-void flash; only the interactive parts (Workspace tabs, Flashcards, ContributeForm, LiveInduction) hydrate as client islands.
  • A deliberate type system carries the argument: Fraunces (a warm serif) for emotion, IBM Plex Mono for the machine-checked artifacts (cited words, tense particles, live metrics) so "verified in code" is legible in the typography itself, and Hanken Grotesk for body, chosen specifically to avoid the Inter/Geist defaults every AI build ships.
  • Palette: deep warm ink, a single lantern-amber (ember) light source, and pounamu (greenstone) jade for life and revival. The hero fade-rise is a CSS animation that runs on first paint, not a JS entrance that flashes empty.
  • The LiveInduction demo animates the real pipeline: corpus resolving into cited vocabulary with the guardrail visibly discarding an invented word. It is the one thing a competitor cannot copy.
  • Flashcards run the SM-2 spaced-repetition schedule with in-browser text-to-speech (Web Speech API), so audio never leaves the device.
  • Measured quality: Lighthouse 100 Accessibility / 100 Best Practices / 100 SEO and roughly 100 Performance on every page (LCP 241ms, CLS 0.00), with a semantic single-main-landmark and sequential heading order.

5. Backend

  • The induction engine (src/lib/engine) orchestrates tokenize -> induce -> guardrail -> assemble. The LLM call (src/lib/llm.ts) speaks the OpenAI-compatible protocol to Groq, model llama-3.3-70b-versatile, and the response is parsed and validated with Zod before anything is trusted.
  • Route Handlers: /api/contribute (Zod-validated phrase intake), /api/induce, /api/audio (multipart upload), /api/metrics and /api/stats (the live proof), and /api/auth/[...nextauth].
  • Fail-soft is a first-class design value, not an afterthought (see section 9).

6. Data layer

  • MongoDB Atlas (M0 free tier, AWS us-east-1) stores the growing corpus and stats. The store (src/lib/store.ts) is an interface with two implementations: a Mongo-backed store when MONGODB_URI is present, and an in-memory store when it is not, so the app boots and the demo works with zero configuration. Round-trip verified by scripts/mongo.check.ts (connect + insert + read + delete).
  • The Phrase model carries the text, meaning, category, source citation, and an optional audioUrl. Corpora in src/lib/seed are ground truth and never contain a word that cannot be attributed to a real source.
  • Firestore (firebase-admin) stores user records for auth: id, email, name, bcrypt passwordHash, provider, createdAt, emailVerified. Round-trip verified by scripts/firestore.check.ts.
  • Vercel Blob (public CDN) stores speaker-pronunciation clips. The upload path enforces a 5MB cap, an audio content-type allow-list, and a sanitized key (no path traversal), with addRandomSuffix to prevent collisions. Round-trip verified by scripts/blob.check.ts. Note: this replaced Firebase Storage, which began requiring a billing card; we kept the entire stack on free tiers.

7. Authentication

  • Auth.js v5 (NextAuth) with three providers: Google, GitHub, and email/password (Credentials). Passwords are hashed with bcrypt; sessions are JWT.
  • Providers register only when their env is present, so an empty environment still boots the whole app. Auth is strictly additive: signing in adds synced progress and attribution but never gates the demo.
  • Fail-soft gating: a single authConfigured() flag (AUTH_SECRET present) decides whether any auth UI renders. Without it, the session provider is not mounted, the sign-in control is hidden, and /signin shows a graceful "accounts not switched on" panel instead of a form that would error. So an unconfigured deploy is clean, with no dead buttons and no console errors.
  • Includes forgot-password and email-verification flows.

8. Infrastructure and hosting

  • Hosted on Vercel; push to main auto-deploys. Production is lantern-cyan.vercel.app.
  • Twelve environment keys configure the full backend: AUTH_SECRET, AUTH_URL, GOOGLE_CLIENT_ID/SECRET, GITHUB_ID/SECRET, GROQ_API_KEY, FIREBASE_PROJECT_ID/ CLIENT_EMAIL/PRIVATE_KEY, MONGODB_URI, BLOB_READ_WRITE_TOKEN.
  • The OAuth callback URLs are pre-registered for the production domain, so turning the backend on is a paste-and-redeploy with zero extra configuration.
  • Free-tier discipline throughout: MongoDB Atlas M0, Firestore Spark, Vercel Blob Hobby, Groq, GitHub/Google OAuth -- no credit card anywhere.

9. Reliability engineering

  Failure                         Lantern's response
  ------------------------------  -------------------------------------
  LLM unreachable / bad key       fall back to a hand-verified fixture
  Mongo unreachable / no URI      fall back to an in-memory store
  Auth not configured             hide auth UI, no dead buttons/errors
  Blob storage off                recorder not offered; /api/audio 503
  Malformed API input             Zod -> clean 400, never a 500
  Unknown language id             branded not-found page

Five self-checking scripts encode the invariants and are run before deploy: guardrail.check (0 hallucinations, 100% citation), failsoft.check (Contribute never throws), mongo.check, blob.check, firestore.check. The guardrail check is the one that can fail a release: the honesty promise cannot quietly regress.

10. The build history (how we got here)

  Slice 1   additive auth (Auth.js v5: Google/GitHub/email) + account menu
  PHASE 0   lint green + fail-soft regression test
  Auth      forgot-password + email verification
  Stage 1   de-vibe-code the hero, fix silent TTS
  Stage 2   make the landing "hear it" button actually speak
  Stage 3   FAQ page
  PHASE 4   speaker pronunciation audio on Vercel Blob
  Hero      kill the empty-void first paint; warmth + a loud proof band
  Item 4    the live induction demo (the thing a competitor cannot copy)
  Auth      fail-soft UI when AUTH_SECRET is absent (clean prod degradation)
  a11y      homepage <main> landmark; sequential heading order -> 100s
  Deploy    re-authored under the owner for the production Vercel

The through-line: take a real but rough build, harden it slice by slice, verify every change against the live app and the guardrail, and never ship a regression.

11. Engineering challenges and how we solved them

  • Making an LLM useful on a tiny corpus without letting it invent: solved by inverting the usual prompt into cite-or-discard, enforced by a code-level check rather than a prompt plea.
  • Staying honest and online when the model fails: solved with verified fixtures so Contribute never throws and the demo never breaks.
  • Keeping a 100% citation invariant as the corpus grows: a self-test that fails the release if a single uncited word ever slips through.
  • A production deploy with no environment: solved by making auth, the LLM, and the database all degrade gracefully, so the app is presentable and honest even before a single key is set.

12. Ethics and community data sovereignty

Endangered-language data carries real cultural ownership. Our position: the community owns and governs its corpus, and Lantern is a tool used on that data, never an extraction pipeline. We cite the source of every seed phrase, and we cite every photo of every community on the site. Pronunciation is generated in the browser, so no learner audio is sent to us. Data-sovereignty controls (deciding who can view, download, and contribute to a language) are an active part of the roadmap.

13. Tech stack

  Layer            Choice
  ---------------  ------------------------------------------------------
  Framework        Next.js 16 (App Router, RSC, Turbopack)
  Language         TypeScript
  AI               Groq -- llama-3.3-70b-versatile (OpenAI-compatible)
  Validation       Zod
  Auth             Auth.js v5 -- Google, GitHub, email/password (bcrypt)
  User store       Firestore (firebase-admin)
  App data         MongoDB Atlas (M0) + in-memory fallback
  Audio storage    Vercel Blob (public CDN)
  Pronunciation    Web Speech API (in-browser)
  Type / design    Fraunces, IBM Plex Mono, Hanken Grotesk; ember + pounamu
  Hosting          Vercel (push-to-deploy)

14. Verify every claim yourself

  Honesty metrics    GET https://lantern-cyan.vercel.app/api/metrics
  Live app           https://lantern-cyan.vercel.app
  Guardrail invariant  npx tsx src/lib/engine/guardrail.check.ts
  Contribute fail-soft npx tsx scripts/failsoft.check.ts
  Mongo round-trip   npx tsx --env-file=.env.local scripts/mongo.check.ts
  Blob round-trip    npx tsx --env-file=.env.local scripts/blob.check.ts
  Firestore round-trip npx tsx --env-file=.env.local scripts/firestore.check.ts

Lantern is a small app with one very large promise, and it keeps that promise in code: it never teaches a word a real speaker did not say. That is the entire point, and it is verifiable, live, right now.

Built With

Share this project:

Updates