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 _activity which 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:

  1. 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).

  2. 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 in frontend/lib/server/).

  3. 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

Share this project:

Updates