Partner swaps that actually happen. Surka runs cross-promotion swaps between software founders from yes to results: terms, deadlines, reminders, proof of delivery, and a record of who keeps their word.
See the codePartner swaps that actually happen. Two founders agree to promote each other, and Surka helps run the swap from yes to results: the terms, the deadlines, follow-up, a check that each side delivered, and what the swap produced. During the first pilot, follow-up is manual; automated email delivery has not been verified in production.
Live site · How a swap runs · Run it locally
No database to install and no accounts to create. It runs locally on Postgres compiled to WebAssembly, the integration tests run against a real Postgres in memory, and every form works with JavaScript turned off.
This repository is Phase 1 of the master plan: the deal sheet and the swap runner, built to make the manual pilot faster. You, the operator, still decide every swap; the software holds the terms, chases deadlines, checks proof, and keeps score.
Reputation counts commitments kept, never results: a partner controls whether they deliver, not whether an audience clicks. Old outcomes fade with a 180-day half-life, so one bad swap doesn't follow anyone forever.
Requires Node 20 or newer. No database to install: locally, Surka runs on PGlite, real Postgres compiled to WebAssembly, stored in ./.pglite.
npm install
cp .env.example .env.local # then set ADMIN_PASSWORD and SESSION_SECRET
npm run db:seed # optional: three demo swaps at different stages
npm run dev
Open http://localhost:3000 for the landing page, and http://localhost:3000/admin for the operator dashboard. The seed script prints private links for each demo swap.
| Command | What it does |
|---|---|
npm run dev | Development server |
npm run build / npm start | Production build and server |
npm run typecheck | TypeScript, strict mode |
npm test | Unit tests for the rules, plus integration tests against an in-memory Postgres |
npm run smoke | Starts the production build and runs a whole swap through the real forms. Run npm run build first. |
npm run db:generate | New migration from changes to src/db/schema.ts |
npm run db:migrate | Applies migrations to DATABASE_URL |
npm run db:seed | Demo data, only into an empty database |
Surka runs on Vercel with a hosted Postgres. Every deploy applies any new database migrations before building (npm run vercel-build), so the database never falls behind the code.
vercel link in this folder.DATABASE_URL to the project for you.npm run setup:vercel yourself. It generates the session and cron secrets, sets a random operator password, and shows that password once.main, or run vercel deploy --prod. The daily reminder job in vercel.json starts with the first production deploy, and Vercel Cron sends CRON_SECRET as a bearer token automatically.Without DATABASE_URL, a Vercel deployment refuses to start and says why, instead of falling back to a local database that can't work there.
| Variable | Required | Purpose |
|---|---|---|
DATABASE_URL | In production | Postgres connection string. Empty means local PGlite. |
APP_URL | No | Public URL for links and emails. Defaults to the Vercel production domain; set it for a custom domain. |
ADMIN_PASSWORD | Yes | Operator dashboard password |
SESSION_SECRET | Yes | 16+ random characters for signing the operator session |
CRON_SECRET | Yes | Protects /api/cron/reminders |
RESEND_API_KEY, EMAIL_FROM | No | Send reminder emails through Resend. Without them, emails print to the server log. |
CONTACT_EMAIL | No | Where "Run your first swap" on the landing page goes |
src/db).src/lib/swap-rules.ts, reputation.ts, reminders.ts) with no database or framework code, so the rules are easy to read and test.src/lib/services) that validates every input with Zod and writes every change to an append-only timeline. Pages and actions call services; services never import Next.js.src/
app/ pages, server actions, the tracking redirect, the reminder cron
components/ logo, deal sheet, form and status components
db/ schema and the Postgres/PGlite client
lib/ rules, validation, sessions, email
lib/services/ swaps, metrics, reminders
drizzle/ SQL migrations
scripts/ migrate, seed, end-to-end smoke test
tests/ unit and integration tests
| Phase | What it is | Gate to the next |
|---|---|---|
| 0. Manual pilot | Five swaps run by hand | 3 of 5 swaps on time, 3 of 5 founders want another |
| 1. Deal sheet and runner (this code) | Deal sheets, reminders, proof, results, and the pilot scoreboard | 10 more swaps, with operator time per swap cut in half |
| 2. The agent | AI drafts terms and copy from a link, chases both sides, suggests partners | Swaps finish without the operator, and the first founder pays |
| 3. The network | Reputation from kept commitments, suggestions from real results, billing | New founders arrive through deal sheets |
The dashboard tracks the Phase 0 and 1 numbers directly: completed swaps, the on-time rate, the share of partners who accept, operator minutes per swap, and businesses that come back for another.
TypeScript
92.6%
JavaScript
6.0%
Partner swaps that actually happen. Surka runs cross-promotion swaps between software founders from yes to results: terms, deadlines, reminders, proof of delivery, and a record of who keeps their word.
See the codePartner swaps that actually happen. Two founders agree to promote each other, and Surka helps run the swap from yes to results: the terms, the deadlines, follow-up, a check that each side delivered, and what the swap produced. During the first pilot, follow-up is manual; automated email delivery has not been verified in production.
Live site · How a swap runs · Run it locally
No database to install and no accounts to create. It runs locally on Postgres compiled to WebAssembly, the integration tests run against a real Postgres in memory, and every form works with JavaScript turned off.
This repository is Phase 1 of the master plan: the deal sheet and the swap runner, built to make the manual pilot faster. You, the operator, still decide every swap; the software holds the terms, chases deadlines, checks proof, and keeps score.
Reputation counts commitments kept, never results: a partner controls whether they deliver, not whether an audience clicks. Old outcomes fade with a 180-day half-life, so one bad swap doesn't follow anyone forever.
Requires Node 20 or newer. No database to install: locally, Surka runs on PGlite, real Postgres compiled to WebAssembly, stored in ./.pglite.
npm install
cp .env.example .env.local # then set ADMIN_PASSWORD and SESSION_SECRET
npm run db:seed # optional: three demo swaps at different stages
npm run dev
Open http://localhost:3000 for the landing page, and http://localhost:3000/admin for the operator dashboard. The seed script prints private links for each demo swap.
| Command | What it does |
|---|---|
npm run dev | Development server |
npm run build / npm start | Production build and server |
npm run typecheck | TypeScript, strict mode |
npm test | Unit tests for the rules, plus integration tests against an in-memory Postgres |
npm run smoke | Starts the production build and runs a whole swap through the real forms. Run npm run build first. |
npm run db:generate | New migration from changes to src/db/schema.ts |
npm run db:migrate | Applies migrations to DATABASE_URL |
npm run db:seed | Demo data, only into an empty database |
Surka runs on Vercel with a hosted Postgres. Every deploy applies any new database migrations before building (npm run vercel-build), so the database never falls behind the code.
vercel link in this folder.DATABASE_URL to the project for you.npm run setup:vercel yourself. It generates the session and cron secrets, sets a random operator password, and shows that password once.main, or run vercel deploy --prod. The daily reminder job in vercel.json starts with the first production deploy, and Vercel Cron sends CRON_SECRET as a bearer token automatically.Without DATABASE_URL, a Vercel deployment refuses to start and says why, instead of falling back to a local database that can't work there.
| Variable | Required | Purpose |
|---|---|---|
DATABASE_URL | In production | Postgres connection string. Empty means local PGlite. |
APP_URL | No | Public URL for links and emails. Defaults to the Vercel production domain; set it for a custom domain. |
ADMIN_PASSWORD | Yes | Operator dashboard password |
SESSION_SECRET | Yes | 16+ random characters for signing the operator session |
CRON_SECRET | Yes | Protects /api/cron/reminders |
RESEND_API_KEY, EMAIL_FROM | No | Send reminder emails through Resend. Without them, emails print to the server log. |
CONTACT_EMAIL | No | Where "Run your first swap" on the landing page goes |
src/db).src/lib/swap-rules.ts, reputation.ts, reminders.ts) with no database or framework code, so the rules are easy to read and test.src/lib/services) that validates every input with Zod and writes every change to an append-only timeline. Pages and actions call services; services never import Next.js.src/
app/ pages, server actions, the tracking redirect, the reminder cron
components/ logo, deal sheet, form and status components
db/ schema and the Postgres/PGlite client
lib/ rules, validation, sessions, email
lib/services/ swaps, metrics, reminders
drizzle/ SQL migrations
scripts/ migrate, seed, end-to-end smoke test
tests/ unit and integration tests
| Phase | What it is | Gate to the next |
|---|---|---|
| 0. Manual pilot | Five swaps run by hand | 3 of 5 swaps on time, 3 of 5 founders want another |
| 1. Deal sheet and runner (this code) | Deal sheets, reminders, proof, results, and the pilot scoreboard | 10 more swaps, with operator time per swap cut in half |
| 2. The agent | AI drafts terms and copy from a link, chases both sides, suggests partners | Swaps finish without the operator, and the first founder pays |
| 3. The network | Reputation from kept commitments, suggestions from real results, billing | New founders arrive through deal sheets |
The dashboard tracks the Phase 0 and 1 numbers directly: completed swaps, the on-time rate, the share of partners who accept, operator minutes per swap, and businesses that come back for another.
TypeScript
92.6%
JavaScript
6.0%