Inspiration

Every day I ask AI assistants valuable questions and get great answers — then lose them forever. Chat history is linear, unsearchable, and ephemeral. I kept re-Googling the same questions, knowing I had already found the answer somewhere but unable to find it again.

The insight was simple: AI answers are valuable knowledge. They deserve to be saved, organized, and searchable — not lost in an endless scroll of chat history. That's why I built ChatScroll.

What it does

ChatScroll transforms AI conversations into a personal knowledge library:

  • Chat with Gemini 2.5 Flash AI on any topic
  • Save the best answers as "Scrolls" with one click — AI automatically suggests the right folder
  • Search semantically — find "blood thinner medication" and discover your warfarin scroll even if those exact words aren't in it, powered by Amazon Aurora pgvector
  • Study Mode — review your knowledge library as flashcards with keyboard navigation
  • Write your own Scrolls from books, articles, or your own expertise — not just AI answers
  • Share any Scroll via public link — others can import it directly to their own library
  • Export scrolls as PDF (browser-native print) or Markdown for portability
  • Related Scrolls — open any scroll and see semantically similar ones from the same category, powered by pgvector cosine similarity scoped to the same folder subtree

How I built it

Frontend: Next.js 14 App Router with TypeScript and Tailwind CSS, deployed on Vercel. AWS Amplify handles Cognito authentication with JWT tokens. Anonymous sessions use a browser-generated UUID that automatically migrates to a real account on sign-up — no data is ever lost.

Backend: ASP.NET Core 9 API running on AWS ECS Fargate (containerized, no EC2 management). Docker images are built via GitHub Actions, pushed to Amazon ECR, and deployed with an automatic ECS task definition registration on every push to master.

Amazon Aurora PostgreSQL — the knowledge layer: Aurora Serverless v2 with three PostgreSQL extensions working together:

Extension What I use it for
pgvector 3072-dim embeddings per Scroll via gemini-embedding-001; cosine similarity search with threshold; Related Scrolls via nearest-neighbour scoped to folder subtree
ltree Folder paths as dot-separated label trees (programming.containers); subtree queries without recursive CTEs
tsvector Full-text search index; ranked with ts_rank; hybrid search combining semantic + keyword results

Aurora also stores conversation metadata, user records keyed by Cognito sub, and the full folder hierarchy.

Amazon DynamoDB — the chat layer: DynamoDB stores the high-volume chat message stream:

  • PK: conversationId — all messages in one partition
  • SK: {timestamp}#{messageId} — chronological reads and time-range queries
  • TTL: expiresAt (90 days) — messages auto-expire with zero infrastructure, no cron jobs needed
  • PAY_PER_REQUEST — scales to millions of messages

This dual-database pattern keeps Aurora lean (structure + search) while DynamoDB absorbs the write volume of real-time chat.

Challenges I ran into

1. AWS Bedrock/Titan access restrictions My original plan used Amazon Titan (via AWS Bedrock) for both chat and embeddings — BedrockAiService.cs targeting amazon.titan-text-premier-v1:0 and amazon.titan-embed-text-v2:0. Every call returned ValidationException: Operation not allowed despite correct IAM roles and task policies. After exhausting every configuration option, I discovered this was an account/region-level restriction requiring an AWS Support case to resolve — not fixable via console. I pivoted to Google Gemini 2.5 Flash for chat and gemini-embedding-001 for embeddings, which proved more capable for this use case.

2. ECS deployment pipeline — stale task definitions For a while, --force-new-deployment was restarting ECS using the old task definition instead of picking up newly pushed Docker images. I discovered that force-new-deployment alone only restarts the service with the existing task definition — it does NOT update which image is running. I rewrote the GitHub Actions workflow to: fetch the current task definition, update the container image to the new SHA-tagged ECR image, register a new task definition revision, then explicitly pass the new ARN to update-service. Every deploy now correctly runs the new image.

3. Scoping semantic search to a category Early versions of Related Scrolls returned completely unrelated results — a scroll about warfarin would surface Docker and OOP notes. I fixed this by resolving the root parent folder of the current scroll, collecting all descendant folder IDs, and filtering with AND folder_id = ANY($allowedIds::uuid[]). Searching "blood thinner medication" now correctly returns only medicine scrolls, ranked by cosine similarity.

4. pgvector at 3072 dimensions exceeds Aurora's index limits After migrating to gemini-embedding-001 (3072 dimensions), I discovered Aurora's pgvector version caps both ivfflat AND hnsw indexes at 2000 dimensions. I dropped the existing ivfflat index and use exact search instead — which works perfectly at personal knowledge base scale and is simpler to maintain.

5. Anonymous-to-authenticated data migration When a user signs up after an anonymous session, all their Aurora data (scrolls, folders, conversations) needs to re-parent to their Cognito sub. I built a POST /api/users/me/migrate-anonymous endpoint that runs four UPDATE statements atomically in a transaction, with safety guards to prevent misuse. The frontend calls it once on first login and removes the anonymous UUID from localStorage — users never lose their data when they create an account.

Accomplishments that I'm proud of

  • Three PostgreSQL extensions working together — pgvector, ltree, and tsvector in a single Aurora schema, each solving a distinct problem the others can't
  • Semantic search that actually works — searching "containerization technology" correctly surfaces the Docker scroll with zero medicine results contaminating it
  • Deliberate dual-database architecture — Aurora for structured relational data needing complex queries and vector search; DynamoDB for high-volume time-series chat data needing infinite scale
  • Full CI/CD pipeline — every git push automatically builds, pushes to ECR, registers a new ECS task definition revision, and deploys with zero manual steps
  • Anonymous-first UX — users can chat and save Scrolls before ever creating an account; their full history follows them when they sign up
  • Branded Cognito email — verification emails use ChatScroll branding with the real logo and amber design

What I learned

  • Choose databases for their strengths. Aurora's pgvector + ltree + tsvector combination is uniquely powerful for knowledge management. DynamoDB's composite keys and TTL are perfect for chat history. The right tool for the right job makes everything simpler.
  • ECS task definitions are immutable. force-new-deployment alone won't pick up new images — you must register a new task definition revision and explicitly pass its ARN to update-service.
  • Always log AI service failures explicitly. Silent error swallowing in embedding generation meant all embeddings were NULL for a long time with no visible error — smart search appeared to work but was actually falling back to keyword search silently.
  • pgvector at high dimensions has index limits on Aurora — both ivfflat and hnsw cap at 2000 dims. At personal knowledge base scale, exact search is the right choice.

What's next for ChatScroll

  • Spaced repetition — surface scrolls you haven't reviewed in a while, turning the library into an active learning tool
  • Team libraries — shared scroll collections for organizations and study groups
  • Browser extension — save any webpage or article as a Scroll directly from the browser
  • Multi-model support — let users choose their AI backend per conversation
  • Community scrolls — discover and import top-rated scrolls from the community on any topic
  • Real Cognito multi-tenancy — enterprise SSO and team-level permissions

Built With

  • amazon-aurora-postgresql
  • amazon-dynamodb
  • amazon-ecr
  • amazon-ecs
  • asp.net-core
  • aws-cloudwatch
  • aws-cognito
  • c#
  • docker
  • github-actions
  • google-gemini-api
  • next.js
  • pgvector
  • tailwind-css
  • typescript
  • vercel
Share this project:

Updates

Submission history