Developers
Developers
Everything you need to read, run and contribute to the Humanity Ledger code.
The Humanity Ledger source code is public. If you want to know what the product does, the repository is a better answer than any page we write, including this one. It is source available rather than open source: the licence allows reading and reporting problems, not redistribution. Security reports are welcome under Report a vulnerability.
State of the project
The stack
| Layer | Technology | Notes |
|---|---|---|
| Application | Next.js 15, React 19, TypeScript | App Router. The middleware runs on the Edge runtime and holds the identity perimeter. |
| Messaging | XMTP browser SDK, MLS | Direct messages are encrypted in the browser. A server fallback exists and is labelled. |
| Identity | Sign in with Ethereum, JWT | Sessions are cookies scripts cannot read. Identity is never taken from a header or a query parameter. |
| Payments | wagmi, viem; Aztec and Noir | Public payments are signed in the user's wallet. Private payments on Aztec are experimental. |
| Data | PostgreSQL with Prisma, Redis | Redis is optional in development. Rate limiting and nonce storage degrade gracefully without it. |
| Realtime | Server sent events, WebRTC | Some views still poll, which is the current scaling limit. |
| Testing | Vitest, Playwright | Unit tests in test/, end to end tests in e2e/. |
Run it locally
1. Clone and install
git clone https://github.com/humanityledger/Humanity-Ledger.git
cd Humanity-Ledger
npm installSome dependencies still declare React 18 as a peer while the project uses React 19. The overrides in package.json handle most cases. If npm still refuses, add legacy-peer-deps=true to your .npmrc.
2. Configure the environment
# Required, or the app will not start
DATABASE_URL=
DATABASE_URL_UNPOOLED=
JWT_SECRET=
NEXTAUTH_SECRET=
NEXTAUTH_URL=http://localhost:3000
ENCLAVE_PIN_PEPPER=
ENCLAVE_PIN_SECRET=Never regenerate the PIN pepper in production
ENCLAVE_PIN_PEPPER is part of every stored PIN hash. Changing it makes every existing PIN invalid.
3. Set up the database
npx prisma generate
npx prisma db pushDATABASE_URL is the pooled connection and DATABASE_URL_UNPOOLED the direct one. Prisma needs both, because migrations use session level features that a transaction pooler cannot carry. A free Neon project works well.
4. Start it
npm run devOpen http://localhost:3000. The messenger is at /chat.
Common questions
Why does signing in fail locally?
Usually one of these. The message must be a real EIP-4361 message, built with prepareMessage(), with the checksummed address. Its domain must match the server's, which ignores the port: sign for localhost, not localhost:3000. In production the domain comes from NEXT_PUBLIC_DOMAIN or NEXT_PUBLIC_APP_URL (the www. variant is accepted too, and SIWE_ALLOWED_DOMAINS adds others); if neither is set, every sign-in is refused. The nonce route, /api/siwe/nonce, must be reachable without a session, and the request to /api/siwe/verify must carry the siwe-nonce cookie it set. test/auth/siwe-routes.test.ts runs the whole flow with a real signature.
Where is the identity check?
In middleware.ts. It removes every identity header the client sent, on every request including public ones, then attaches the address from the verified session. Routes read it with requireWallet(req) or getSession(). Never send an identity header from a client: it is discarded.
Can I run it without Docker?
Yes. PostgreSQL can come from any provider and Redis is optional in development. docs/RUN-LOCAL.md in the repository covers both.
How do I report a vulnerability?
Privately, through the process on Report a vulnerability. Please do not open a public issue.
