Khas Tracker - A Private, Encrypted Life Tracker
CASE-12Try the demo: khas.tracker.muhammad-khas.com. Tap Try the demo on the sign-in screen. It runs on about 400 days of made-up data that never leaves your browser. Real accounts are invite-only.
Why I built it
I wanted one app for everything I try to keep up with: home and relationship check-ins, wellbeing, make-up (qadha) prayers and fasts, food, training, weight and blood pressure, and goals. Existing apps each cover a slice, and almost all of them keep your data in plain text on someone else's server. So I set three rules first:
- Private by design. Not "we promise not to look." The server should be unable to look.
- Cheap to run. Pay for what I use, with alarms if something misbehaves.
- Editable by anyone. Every section optional; every list, color, and program changeable.
Up front: I'm not a developer. I built this by describing what I wanted to Claude Code, testing what came back, and pushing back until it was right. The decisions were mine; the typing mostly wasn't.
The security model
- Everything is encrypted on the phone before it's sent anywhere, with AES-256-GCM and a random 256-bit data key. Each record is bound to its user and its own name, so records can't be swapped between accounts or slots.
- The data key is wrapped twice: once by your passphrase (PBKDF2-SHA256, 600,000 iterations) and once by a recovery code. Lose both and the data is gone. Nobody can recover it, including me and AWS.
- Keys in the browser are non-extractable, and the app auto-locks when idle.
- Accounts are invite-only, an authenticator-app code (TOTP, no SMS) is mandatory, and there are no third-party scripts or trackers, enforced by a strict Content Security Policy.
- The API takes your identity only from the verified sign-in token, never from the request. Automated tests check that before every deploy.
As the admin, I can see an account's email and when it signed in. I can't see anything inside it.
What runs on AWS
Everything is defined as code with the AWS CDK (TypeScript) in one stack:
| Piece | What it does |
|---|---|
| S3 + CloudFront | Private bucket for the app files, behind a CDN that also forwards /api/* and adds security headers. |
| Route 53 + ACM | The domain and its HTTPS certificate. |
| Cognito | Invite-only sign-in with required authenticator codes. |
| API Gateway + Lambda | A tiny four-route sync API with a JWT check, throttled low so abuse can't run up the bill. |
| DynamoDB | Holds only encrypted blobs, with point-in-time recovery. |
| AWS Backup | Daily backups for 35 days, plus a copy in a second region for 3 years. |
| Budgets | Emails me if spending heads past a small monthly limit. |
| IAM + GitHub OIDC | GitHub deploys with short-lived tokens, so no AWS keys are stored in GitHub. |
The front end is plain HTML, CSS, and JavaScript, works offline, and syncs when there's a connection. Every push to main tests and deploys in about five minutes.
How it got built
I wrote a spec and built it in small phases, each in its own fresh session, testing each piece in a local demo mode that fakes sign-in and the API. Two lessons: build small and test each piece, and read every "done" report skeptically. A few sessions said features were tested when they hadn't actually been clicked through, so I started checking every tab myself.
Hiccups worth sharing
- GitHub couldn't sign in to AWS. My OIDC trust rule allowed
owner/repo, but GitHub identified the repo with ID numbers added. Fixed by accepting both forms, still locked to one repo'smainbranch. - "Access denied" on Billing. I'd locked the account down properly (root MFA, admin user for daily work), but AWS hides billing from IAM users by default. One root sign-in to enable IAM billing access fixed it.
- A surprise load balancer bill. Elastic Load Balancing was my biggest cost, on an app that doesn't use one. The likely cause was a leftover from an old tutorial. Lesson: delete tutorial resources, and set a budget alarm on day one.
- A bug that ate my checkmarks. Default checklist items got new ids on every reload, so past ticks vanished. Defaults are now saved on first use.
What I'd do differently
- Set up billing access and a budget before anything else.
- Test on a real phone sooner.
- Write the deploy guide as I go. It ended up being one of the most valuable parts of the project.