Social.Income.explained.mp4
Social Income is a radically simple solution in the fight against poverty. The global open-source initiative converts donations into an unconditional basic income, which is sent directly to the mobile phones of people living in poverty in the Global South.
Welcome to the Social Income monorepo.
/
├─ recipients_app/ → Mobile app for recipients
├─ recipients_selection/ → Verifiable draw process for selecting recipients
├─ seed/ → Firebase emulator seed data
├─ ui/ → Legacy Storybook component library
└─ website/ → Next.js (public site, portal, dashboard, infra, backend services)
Mobile app where recipients can:
- Log in
- View payment history
- Complete surveys
See /recipients_app/README.md for platform‑specific setup.
Implements the cryptographically verifiable and bias‑proof recipient draw:
- Raw recipient lists → salted → hashed → committed to
/lists - GitHub Action triggers
draw.sh - Draw randomness comes from drand → https://drand.love
- Results written to
/draws - Draws are reproducible using dchoose → https://github.com/drand/dchoose
- Full transparency explanation: https://socialincome.org/transparency/recipient-selection
Contains seed data for the local development environment:
Automatically imported when running:
mise dev
Local development DB can be filled with sample data via:
npm run db:seed
This populates the local PostgreSQL instance with representative example data.
Legacy UI component package using:
- Tailwind CSS
- shadcn/ui
📘 Storybook preview: http://design.socialincome.org
The long‑term plan is to phase this out and maintain all components
inside /website.
A Next.js project containing:
- Currently partly hardcoded
- Migration underway → Storyblok CMS
- Journal already uses Storyblok Docs: https://www.storyblok.com/docs
Internal operations tool:
- Program management
- Payments & transfers
- Recipients & contributor tools
- Admin functions
Contributor self‑service area:
- View payments
- Manage subscriptions
- Update personal details
Local Partner self‑service area:
- View own recipients / candidates
- Update personal details
We use hardcoded deterministic seed data for local development and tests. All local login users come from Firebase Auth emulator seed data.
To see which users can log in locally, open:
http://localhost:4000/auth
The recipients_app communicates with Next.js API routes via
OpenAPI‑documented endpoints:
📘 API docs:
https://socialincome.org/v1/api-docs
Infrastructure-as-code via Terraform:
- GCP Cloud Run
- GCP Cloud SQL (PostgreSQL)
- Networks, service accounts, secrets, etc.
Docs: https://developer.hashicorp.com/terraform/docs
Shared backend modules using:
- Prisma ORM → https://www.prisma.io
- PostgreSQL
- Firebase Storage
- Misc. utilities and API integrations
We use Playwright for end‑to‑end testing of the website.
Key principles
- Tests run against the real app in CI
- Visual regression screenshots are automatically updated in the pipeline and committed into the PR
- External Storyblok requests are mocked using a mockserver
Mock recording system
We use a record/replay mechanism for deterministic tests.
Record mode
- Set STORYBLOK_MOCK_MODE in .env.development to 'record'
- Restart dev server
mise dev - run tests:
STORYBLOK_MOCK_MODE=record npm run test:e2e
This:
- starts the mockserver as proxy
- records all outgoing Storyblok API requests
- stores them as JSON fixtures inside the repo
These recordings should be committed.
Replay mode (CI default)
In CI the environment runs in replay mode:
- mockserver serves recorded responses
- no external API calls are made
- tests are fully deterministic and fast
We use:
- mise-en-place → https://mise.jdx.dev
- Docker (for PostgreSQL)
- Firebase Emulators
- Node.js + npm
- Terraform (infra work only)
brew install mise
mise install
Inside /website, copy the sample file:
cp website/.env.local.sample website/.env.local
Edit values as necessary.
mise dev
This starts:
- Local PostgreSQL via Docker Compose
- Firebase Emulators (Auth + Firestore + Storage)
- Next.js website (public site, portal, dashboard)
Useful commands for copying local DB → staging (or vice versa).
pg_dump -Fc --no-owner "postgresql://social-income:social-income@localhost:5432/social-income" > local.dump
pg_restore --clean --if-exists --no-owner -d "postgresql://staging-website_google_sql_user:xxxx@yyyy:5432/staging-website-google-sql-database" local.dump
- Read the Storyblok docs → https://www.storyblok.com/docs
- Set env vars in
website/.env.local:STORYBLOK_PREVIEW_TOKENSTORYBLOK_PREVIEW_SECRETSTORYBLOK_WEBHOOK_SECRET(must match the Secret key on the Storyblok webhook; used to verify thewebhook-signatureheader)
- Optional: start local dev with HTTPS for live preview
mise run dev-ssl
To refresh cached pages when editors publish in Storyblok, add a webhook in the space (Settings → Webhooks):
- URL:
https://<your-production-domain>/api/revalidate - Secret key: set a value and put the same value in
STORYBLOK_WEBHOOK_SECRET(Storyblok signs the raw body; we verify thewebhook-signatureheader per Storyblok’s webhook signature docs) - Method: POST
- Triggers: Story published, Story unpublished, Story deleted, Story moved
The handler calls Next.js revalidatePath for the affected routes (see
website/src/lib/services/storyblok/revalidation.ts) and invalidates
/sitemap.xml.
We use the Storyblok CLI to generate TypeScript types from the CMS schema.
If you have made changes to the Storyblok schema, you can regenerate the types:
- Set these env vars in
website/.env.local:STORYBLOK_PERSONAL_ACCESS_TOKENSTORYBLOK_SPACE_ID
- Run:
npm run storyblok:generate
This command:
- Logs into Storyblok using your personal access token
- Pulls component schemas from the space
- Generates TypeScript types to
src/generated/storyblok/types/
Usage in components:
import type { Page } from '@storyblok/types/{SPACE_ID}/storyblok-components';rm -rf website/.next
mise dev
npm run firebase:export
Become a contributor of Social Income (tax-deductible in Switzerland).
Become a sponsor and help ensure the development of open source software for more equality and less poverty. Donations through the GitHub Sponsor program are used for building a strong developer community.
Social Income is a non-profit association (CHE-289.611.695) based in Zurich, Switzerland. Connect with us X, Insta, LinkedIn, Facebook or by email.
We believe that transparency builds trust and trust builds solidarity. This is why we disclose our finances to the public.
Open Source isn’t an exclusive club. It’s made by people just like you. These individuals, amongst many others, have made significant contributions to Social Income's success:
We receive in-kind donations from Google Nonprofit, GitHub, Codemagic, Linktree, Twilio, Algolia, JetBrains, Storyblok, 1Password, Mux, Sentry and Lineto. Our tools also leverage other open-source technologies, including solutions like FireCMS, Storybook and Tailwind CSS.
This project is licensed under MIT, with the exception of the Unica77 font, which is exclusively licensed to Social Income.

