Live: https://sumdeting.246labs.cloud
A Bajan AI math tutor that refuses to just give you the answer. SumDeTing uses Claude on Amazon Bedrock with a Socratic system prompt: it asks what you've tried, points at where your work went wrong, and guides you to the solution one hint at a time. Math renders properly with KaTeX.
Built for Barbadian students: the tutor knows the local journey from Common Entrance (BSSEE) through CSEC/CXC Mathematics to CAPE, encourages in a warm Bajan voice while keeping every mathematical step in clear standard English, and uses everyday Barbadian contexts (BBD prices, bus fares, cricket) in its examples.
Stack: Claude (Amazon Bedrock Converse API) · AWS Lambda (Function URLs) · S3 + CloudFront (OAC) · AWS WAF · DynamoDB · Amazon SES · OpenTofu (in tofu-or-not-tofu) · Docker (hardened local runtime)
One codebase, two runtimes: backend/tutor.mjs powers both the serverless Lambda deployment and a hardened Docker container for local development.
The full AWS diagram above is generated from the tofu stack. Its source is docs/sumdeting-architecture.drawio, and the PNG embeds it too, so either opens for editing in draw.io. The request flow in short:
flowchart LR
U[Student's browser] -->|HTTPS| CF[CloudFront · AWS WAF]
CF --> S3[(S3: static web app)]
CF -->|POST /api · SigV4 via OAC| FU[Lambda Function URL · IAM auth]
FU --> L[Lambda: tutor handler]
L -->|Converse API| B[Claude on Amazon Bedrock]
CF -->|POST /api/account/* · SigV4 via OAC| AU[Lambda Function URL · account]
AU --> AL[Lambda: account handler]
AL --> D[(DynamoDB: accounts and progress)]
AL -->|sign-in and consent emails| SES[Amazon SES]
EB[EventBridge Scheduler] -->|daily| J[Lambda: janitor]
J --> D
J --> SES
The frontend is a single HTML page. KaTeX, marked, DOMPurify and the brand fonts are self-hosted under web/vendor/, so the CSP allows only the site's own origin. The backend is one Node.js Lambda that validates the conversation, applies the Socratic tutoring prompt, and calls the Bedrock Converse API. Chats are never stored server-side: the conversation lives in the student's browser (localStorage, 24h) so a refresh doesn't lose their session. Accounts are optional. A signed-in student's practice progress (topics, problems solved, streak, preferences) and profile details are stored in DynamoDB so it follows them across devices; an account Lambda handles sign-in (emailed links and codes via SES), parent and guardian access and under-13 parental consent, and an EventBridge Scheduler janitor deletes expired and inactive data. See the privacy notice draft and the launch runbook.
Tutoring tools that hand out answers optimize for homework completion, not learning. The system prompt enforces pedagogy: never reveal the final answer, one hint per turn, locate the student's error and ask a question about it, confirm and recap only once the student gets there themselves. Prompt design is the core of this project; see SYSTEM_PROMPT in backend/tutor.mjs.
Most AI tutors default to US curricula and contexts. SumDeTing is syllabus-aware for the Caribbean (CXC methods for CSEC students, not whatever a generic model assumes) and meets students in a familiar voice. The persona rules are deliberate: dialect for warmth and encouragement only, standard English for every mathematical statement, and no caricature.
The same tutor logic (backend/tutor.mjs) runs in a hardened container for local development and demos. You need AWS credentials with Bedrock access (via ~/.aws or environment variables). Use Docker Engine 28.0.0 or later: the port is published on 127.0.0.1 only, and older Linux engines don't fully isolate localhost-published ports from the local network.
docker compose up --build
# open http://localhost:8080The image follows container-hardening practice: multi-stage build, non-root node user, read-only filesystem, all capabilities dropped, no-new-privileges, and a healthcheck on /healthz.
ACCOUNT_DEV=1 node server.mjs
# or, in the container:
ACCOUNT_DEV=1 docker compose up --buildACCOUNT_DEV=1 swaps DynamoDB and SES for an in-memory store and a dev outbox. Emails the app would send (sign-in links, codes, consent requests) are readable at http://localhost:8080/api/account/_dev/outbox. Data is lost on restart.
Dev mode never sends real email, and refuses to start with EMAIL_MODE=ses or TABLE_NAME. Without ACCOUNT_DEV=1, sign-in emails need SES: set EMAIL_MODE=ses, EMAIL_FROM (an address on a verified SES identity), SITE_URL (for example http://localhost:8080) and credentials allowed ses:SendEmail; TABLE_NAME optionally points at DynamoDB (never together with ACCOUNT_DEV=1, which refuses to start). With neither, sign-in answers "not available here" rather than pretending to send.
cd backend && npm test # account backend unit tests
cd e2e && npm ci && npx playwright install chromium && npx playwright test # browser testsThe end-to-end suite starts its own server with ACCOUNT_DEV=1 on port 8099. Before a public launch, work through docs/launch-runbook.md.
Infrastructure isn't in this repo. It is managed in chrisfixesit/tofu-or-not-tofu under accounts/eportfolio-prod/sumdeting, and that repo's docs/stacks/eportfolio-prod--sumdeting.md describes every resource and why it is built that way. Changes go through a PR there; merging it applies. The 246labs.cloud DNS records (the site alias, certificate validation, SES DKIM) live in that repo's management account, not in the stack.
Code and site files ship through .github/workflows/deploy.yml on merge to main (behind the production environment approval). It updates the three Lambdas from one zip, uploads the site, and invalidates CloudFront. It refuses to deploy while web/privacy.html is still a draft (see the launch runbook).
Cost: serverless throughout; a few cents per study session with Claude Haiku 4.5. The WAF adds about $6 a month. An account-wide AWS budget of $30 a month emails at 80% of actual spend and when the forecast passes 100%; it alerts, it doesn't stop anything.
- Never gives the answer. The golden rule: when a student is stuck, the tutor fully works a parallel example with different numbers, then lets them apply the method to their own problem.
- Photo homework input. Snap the problem; images are downscaled client-side and sent to Claude's vision via Bedrock Converse image blocks.
- Practice mode. Pick a level (Common Entrance through CAPE) and topic; the tutor generates one Bajan-context problem at a time.
- Streaming replies. Tokens render as they arrive: ConverseStream in the container, Lambda response streaming (
RESPONSE_STREAMFunction URL) in AWS, with a buffered fallback. - Session recap. One click produces a printable, KaTeX-rendered study sheet of the session: methods used, mistakes to watch, fresh practice problems.
- CSEC past papers. A built-in drawer links to 47 real CSEC Mathematics papers (2004–2025, hosted by CSEC Math Tutor). Open one, screenshot a question, send it with the photo button, and the tutor works it with you.
- The Lambda Function URL requires IAM auth; only this CloudFront distribution can call it (Origin Access Control, SigV4).
- The Lambda role can invoke one Bedrock model and nothing else. The model ID comes from configuration, never from the request.
- Every request is size-capped (turns, characters, photos) before the model is called, and model replies are sanitized with DOMPurify before they reach the page.
- Security headers (CSP locked to
'self', HSTS,nosniff,frame-ancestors 'none') are set at CloudFront, on the site and on/api. - AWS WAF watches the tutor's request rate against a limit of 500 requests to
/apiper 5 minutes per IP, enough for a busy class behind one school address. The rule decodes and normalizes the path first, so encoded variants like/%61pistill count. It is in count mode for now: it records IPs that go over the limit but doesn't block them. Blocking will be switched on after a week of real classes shows no school gets close. - CloudWatch alarms (Lambda errors and throttles, Bedrock failures, account API failures, a stopped janitor) email the maintainer through SNS.
- The code is audited with Cloudflare's security-audit skill; findings are fixed in the open.
Found a vulnerability? Please open a private security advisory rather than a public issue.
- Voice input for younger students
- Teacher view with class-level progress summaries
MIT — built by Christopher Corbin
