Inspiration

Most of us can name at least one person in our lives whose stories nobody has ever written down — a grandparent, a great-aunt, a neighbor. People whose memories are vivid and specific, but who have never been asked to record them anywhere. When they're gone, the stories go with them. Immigrant families lose their native language within a single generation. Neighborhoods change so quickly that the histories that defined them get bulldozed alongside the buildings. And the tools that exist today — institutional archives, family-tree websites, voice memos sitting on a phone — aren't really built for the communities they're meant to serve.

We kept coming back to the same question: what would it look like to build a tool actually meant for these families? Something where the act of preserving a memory feels as natural as talking. Something that respects the storyteller enough to keep their words, in their voice, in whatever language they choose.

That's what we built. Roots is built with gratitude — for the stories that shaped us, and for the people who deserve to be remembered in their own voice, for the lives they made possible for us.

What it does

Roots is a voice-first community archive. Anyone — an elder, a family member, a community organizer — can open the app, tap record, and speak. In any language, on any device with a microphone.

Behind the scenes, Roots transcribes the recording, translates it to English, extracts structured metadata (narrator name, year, location, theme, summary, preview quote), and indexes the new story into a Bedrock-powered knowledge base. The whole pipeline runs automatically in about thirty seconds.

On the other side, anyone can ask the archive a question in plain English — "What did grandma say about coming to America?" — and get an answer in the storyteller's own voice, with citations linking back to the source. Real retrieval, real voices, real lineage.

How we built it

The frontend is React with Vite, kept intentionally minimal so the experience feels calm rather than clinical.

The backend is fully serverless on AWS. A chain of Lambda functions orchestrates the recording pipeline, an API Gateway exposes the chatbot, and the heart of the system is Amazon Bedrock — running Claude Sonnet for the chatbot, Claude again for translation and metadata extraction, and Titan Embeddings to power a Bedrock Knowledge Base backed by OpenSearch Serverless for vector retrieval. Audio files live in S3, story metadata lives in DynamoDB, and AssemblyAI handles transcription.

Each recording flows through four chained Lambdas:

  1. The S3 upload triggers the first Lambda, which hands the audio to AssemblyAI
  2. A second Lambda polls AssemblyAI until the transcript is ready
  3. A third Lambda calls Claude on Bedrock to translate the result to English — auto-detecting the source language
  4. A fourth Lambda calls Claude again to extract structured metadata, then writes to DynamoDB, uploads a formatted prose version to S3, and triggers a Bedrock Knowledge Base ingestion job

The chatbot uses Bedrock's RetrieveAndGenerate API with a custom prompt template we wrote that asks Claude to answer in the storytellers' own voice, never to fabricate, and to cite which story each fact came from.

Challenges we ran into

The bridge between recording and querying was the hardest part. Originally we had a manual sync script that copied stories from DynamoDB into S3 — which worked, but meant the chatbot didn't know about new stories until we ran the script. We rebuilt this so the final Lambda in the recording chain handles indexing automatically, including triggering a Bedrock Knowledge Base ingestion job. Getting the IAM permissions exactly right for cross-service calls — Lambda writing to S3, Lambda invoking Bedrock, the knowledge base reading from S3, all scoped narrowly — took longer than the code itself.

Schema mismatches were a quiet bug. When we swapped AWS Transcribe and Translate out for AssemblyAI and Claude, the field names in DynamoDB changed (name became narratorName, transcript became translatedText), which silently broke the sync until we caught it.

Citation cleanup. Our S3 keys were originally UUID-based, which made the chatbot's citation pills read like meaningless hex strings. We added a slugify step so files are stored as narrator-name-shortid.txt and the citations render as human names like "Rosa Mendez".

Browser-side AWS credentials. Our recording page uses scoped IAM keys passed through Vite environment variables, which works for the hackathon but isn't production-safe. We're calling this out honestly in our roadmap rather than hiding it.

Accomplishments that we're proud of

A complete, working RAG pipeline that's truly end-to-end. Record a story in any language, wait thirty seconds, and the chatbot can answer questions about it — with citations. Most hackathon "AI" demos are smoke and mirrors. Ours retrieves real chunks from a real vector store and grounds every answer in a real source.

The custom prompt template was a quiet win. The default Bedrock RetrieveAndGenerate output reads like a Wikipedia summary. We rewrote the prompt so the chatbot responds as a warm guide through a family's archive — citing specific storytellers, pulling sensory details, and never inventing what isn't in the source. That single change is what makes the demo feel personal instead of generic.

Fully serverless, fully automatic. The whole system scales from zero to many users without us changing anything, and the recording-to-queryable pipeline runs without any human in the loop.

What we learned

Bedrock Knowledge Bases are deceptively powerful. They handle chunking, embedding, retrieval, and generation in a single API call — but the quality of the output depends almost entirely on how you prompt. The boilerplate Bedrock setup gave us generic Q&A; an hour spent on the prompt template made it feel human.

Most of building a real AWS pipeline is integration glue. The headline services (Bedrock, Lambda) were the easy part. The IAM policies, the schema contracts between Lambdas, the CORS configuration, the env vars in the right places — that's where the hours actually went.

What's next for Roots

  • Cognito for browser-side auth, replacing our current Vite-env-var approach so Roots can actually be deployed publicly
  • DynamoDB Streams for fully event-driven indexing — currently the trigger lives inside our extraction Lambda, but Streams-based decoupling would be the cleaner architecture
  • Map view and timeline view — every story already has location and year metadata, so the underlying data supports it; we just didn't build the UI in 24 hours
  • Legacy profiles with a per-elder chatbot — "Ask Grandma" — trained only on a single storyteller's recordings
  • Privacy tiers so families can keep some stories private and only share others with the wider community

The vision is for Roots to be the missing piece in family and community memory — a calm, durable archive where voices get to keep speaking long after they're gone.

Built With

  • amazon-dynamodb
  • assemblyai
  • bedrock
  • claude
  • knowledgebase
  • lambda
  • opensearch
  • s3
  • titanembeddings
Share this project:

Updates