AWS Builder Center

I Built a "Google Drive with a Study Brain" on AWS Serverless: CloudVault AI, and the 6 Bugs That Almost Broke It

my first project

Why I built this

Every student I know has the same problem: a phone and laptop full of PDFs, lecture photos and notes they never open again. Google Drive stores them. It doesn't help you learn from them.
So for my final-year capstone I built CloudVault AI: upload any file, and the platform summarizes it, lets you chat with it, and turns it into MCQs and flashcards. It runs fully serverless on AWS, with no servers to patch and nothing running when nobody is using it.
The first working version (upload → process → store) took me 3 days. The rest of this post covers how it fits together and, more usefully, the bugs that cost me hours so they don't cost you any.

What it does

  • Sign up, verify by email, and sign in with a custom-built UI
  • Drag-and-drop upload of any file type
  • Automatic AI summaries for text, PDFs and images
  • Chat with a document: answers grounded only in that file's content
  • Study Assistant: 5 MCQs + 5 flashcards per document, cached after first generation
  • Folders, favorites, search, dark/light theme
  • Shareable links with 1-hour, 24-hour or 7-day expiry, viewable without logging in
  • An AI assistant that answers questions across your whole library and cites filenames

Architecture

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Browser (static site on S3)
│
▼
API Gateway (HTTP API) ── JWT authorizer ◄── Amazon Cognito
│
▼
AWS Lambda (Python 3.12) ──► DynamoDB (documents, folders, shares)
│
├──► S3 (private bucket, per-user key prefix)
│ │ S3 event
│ ▼
│ Upload-handler Lambda
│ ├─ text → read directly
│ ├─ PDF → Amazon Textract
│ └─ image → Bedrock (Nova Lite, vision)
│
└──► Amazon Bedrock (Nova Lite): summaries, chat, MCQs/flashcards
The design choices that mattered most:
1. Uploads never pass through Lambda. The frontend asks an API for a presigned URL and uploads straight to S3. That avoids payload limits and keeps Lambda cheap.
2. S3 events drive the AI pipeline. When a file lands in the bucket, an event triggers the processing Lambda, which branches by file type. Upload stays fast; AI work happens in the background.
3. Security is enforced on the server, not in the UI. Every route except the public share link sits behind a Cognito JWT authorizer. Each file lives under a key prefix of the user's Cognito sub, and every Lambda checks ownership before touching a document. I tested this with two separate accounts, because a hidden button is not access control.

The 6 bugs that cost me the most time

1. S3 event filenames are URL-encoded

A file called my notes.pdf arrived in my Lambda as my+notes.pdf, so get_object failed with "key not found."
python
1
2
3
from urllib.parse import unquote_plus

key = unquote_plus(record["s3"]["object"]["key"])

2. SignatureDoesNotMatch on presigned uploads

A presigned PUT URL is signed against the ContentType you used when generating it. If the browser sends a different Content-Type, S3 rejects the upload.
python
1
2
3
4
5
6
# Lambda
url = s3.generate_presigned_url(
"put_object",
Params={"Bucket": BUCKET, "Key": key, "ContentType": content_type},
ExpiresIn=300,
)
javascript
1
2
// Browser: send the exact same type
await fetch(url, { method: "PUT", headers: { "Content-Type": file.type }, body: file });

3. Duplicate CORS headers break successful responses

I had set CORS in both my Lambda responses and API Gateway. The browser received two Access-Control-Allow-Origin headers and blocked the response, even though the request had succeeded. Pick one place for CORS. I kept it in API Gateway.

4. Opening the page from file:// never works with CORS

Browsers send Origin: null from local files, which a wildcard CORS rule doesn't match. I only fixed this by deploying the frontend to real S3 static website hosting. If your API "works in Postman but not in the browser," check the origin first.

5. New Lambdas get their own execution role

Every time I created a function, it silently used an auto-generated role instead of my shared one, which produced AccessDeniedException errors that looked like policy bugs. The fix was simply to switch the function to the correct role. Check the role first next time.

6. A region mismatch hidden inside an IAM policy

My policy's DynamoDB ARN pointed at a different region than my table. The permission looked correct and still failed. When IAM says "denied" and you're sure it's allowed, read the ARN character by character.

A decision worth sharing: Claude Haiku → Amazon Nova Lite

I started with Anthropic's Claude Haiku 4.5 on Bedrock, then hit an AWS Marketplace payment-instrument requirement that blocked me as a student. I switched to Amazon Nova Lite (amazon.nova-lite-v1:0), which is billed directly by AWS. It handles summaries, vision on images, chat and MCQ generation well enough for this project. Because everything goes through Bedrock, swapping models later is a small change, not a rewrite.

Honest limitations

  • Textract's synchronous API only handles single-page PDFs. Multi-page needs the async job API, which is next on my list.
  • Videos are stored but not AI-processed yet.
  • HEIC/HEIF photos get a friendly "please convert to JPEG/PNG" message.
  • Duplicate filenames overwrite each other, since the S3 key is the same.

What I learned

  1. Make it work end to end first. My Day-1 chain (S3 → Lambda → DynamoDB) gave me something real to build on.
  2. Serverless debugging is mostly permissions and config, not code. CloudWatch Logs are your best friend.
  3. Secure on the server. JWT authorizer + ownership checks + per-user key prefixes beats any UI trick.
  4. Cache AI output. Generating study material once and storing it in DynamoDB saves both money and wait time.
  5. Writing down your limitations makes a project more credible, not less.

What's next

Async Textract for multi-page PDFs, video transcription, CloudFront in front of the frontend, WAF, malware scanning on uploads, and CloudTrail auditing.
Source code: github.com/jairajput12/cloudvault-ai_  (the repo name ends with an underscore)
If you're building something similar, or hit one of these bugs yourself, drop a comment. I'd love to compare notes. And if this saved you an afternoon of debugging, a like helps other builders find it. 🙌
— Jaikeshav Rajput, final-year B.Tech CSE (Cloud Computing), GLA University
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article