-
-
Faqs
-
The total circles you are in
-
Select number of candidates you want to include
-
About the Website
-
select number of members you want to add
-
See your requests
-
total discription abou the circle you created
-
Start creating your circle
-
-
Select the time when you want to start the circle
-
Send requests to the other circles
-
footer
-
Hero Section
Inspiration
What it does
How we built it
Challenges we ran into
Accomplishments that we're proud of
What we learned
What's next for Circle Safe## About CircleSafe
### Inspiration
Across West Africa, millions of people rely on rotating savings circles — known as Ajo in Nigeria, Esusu in Ghana, Susu in Sierra Leone — to save money and access lump sums they wouldn't otherwise have access to. Ten traders each contribute ₦5,000 a week; each week the ₦50,000 pot goes to one member, until everyone has taken a turn. But this system — which moves enormous amounts of money every week — runs on paper and trust. The collector disappears. The notebook gets lost. Someone forgets who paid. And years of reliable savings produce zero credit history a bank will accept.
CircleSafe was built to digitize the tradition without breaking it — keeping the human trust while adding cryptographic accountability.
### What I Learned
Building CircleSafe was a deep dive into:
- Declarative backend design — Modeling every action as a resource with typed validation, rate limits, authorization, and audit logging, all defined in JSON
- Two-party confirmation — No single person can move money in the record: contributions require a separate "confirm" action by a treasurer or owner
- Append-only ledgers — The audit trail isn't a feature bolted on; it is the data model. Every action writes to
_activitywhich is INSERT-only by construction - Trust without identity — No KYC, no government ID. A WhatsApp number verified via OTP was enough to vouch for a person in their circle
- Next.js API routes as a backend — The same 23 resources run against PostgreSQL via Next.js route handlers, with a mock fallback that needs zero infrastructure
### How I Built It
The architecture has three layers:
Frontend — Next.js 16 (App Router) with TypeScript, Tailwind CSS, Recharts for dashboards, and a WebGL animated hero. Every page is built around panels that refetch on changes via WebSockets (SSE in the self-hosted runtime).
Backend — 39 resources over 11 PostgreSQL tables. Each resource is a typed spec with validation, authorization, and rate limiting. The same design works declaratively (via Sub0 JSON definitions in
backend/) or imperatively (via Next.js route handlers infrontend/lib/server/).Authentication — Email/password with BCRYPT-hashed passwords, plus Google Sign-In via OAuth 2.0. JWTs (HS256) carry the user identity through every request.
The development cycle was fast because of the mock mode: with no database configured, the entire app runs against an in-memory store that enforces the same rules, so the full lifecycle — invite → drag payout order → start → contribute → confirm → payout → confirm receipt — was clickable from day one.
### Challenges I Faced
The environment variable trap. NEXT_PUBLIC_* variables are inlined at build time on Vercel. Setting them after the
build does nothing — the app silently falls back to demo mode. This caused repeated "why isn't Google Sign-In showing?"
moments until I understood the build vs. runtime distinction.
Removing WhatsApp OTP. The original design used phone number verification via WhatsApp OTP as the primary identity signal. But deploying it required a WhatsApp Business API provider and a phone number for every user — a barrier for a hackathon demo. Extracting it cleanly meant removing verification gates from 4 endpoints, the phone registration UI, the OTP token table from the database schema, and the admin dashboard column, all while keeping the database migrations backward-compatible.
Google Sign-In 500. The server-side GOOGLE_CLIENT_ID (without NEXT_PUBLIC_ prefix) was missing in the deployment
environment. The error message was generic — "Something went wrong on the server" — making it look like a code bug when
it was just a missing environment variable. Fixing the error handler to return the actual message saved hours of
debugging.
Cross-Origin-Opener-Policy. Google Sign-In uses postMessage() between its popup and the parent window, which was
blocked by the browser's default COOP policy. A single header in next.config.mjs fixed it.
### The Result
CircleSafe is live at circle-safe.vercel.app. Create an account or sign in with Google, start a savings circle, invite members, and run the full contribution-to-payout lifecycle. Every action is recorded, every number is live, and no money moves without two people confirming it.
Built With
- 2.0
- bcrypt
- css
- github
- jwt
- neon
- next.js
- oauth
- postgresql
- react
- recharts
- tailwind
- typescript
- vercel
- webgl


Log in or sign up for Devpost to join the conversation.