Self-hosting
HyphaHypha is one Cloudflare Worker — an MCP server, a thin web ledger, and a Monday-morning email — backed by D1 (SQLite) and a cron trigger. There is nothing else to run: no servers to babysit, no third-party email provider, no message queue. This page takes you from a fresh checkout to a deployed instance with a first member signed in.
If you just want to understand the network, start with Concepts. If you want to wire up your agent, see the Quickstart. This page is for the person running the instance.
Prerequisites
- A Cloudflare account (the Workers free plan is fine).
- A sender domain on Cloudflare DNS onboarded to Cloudflare Email Sending (Email Routing → Email Sending). Onboarding auto-provisions SPF/DKIM/DMARC. Your
FROM_EMAILmust be an address on this domain. - Node 22, a checkout of the repo, and Wrangler (bundled — every command below uses
npx wrangler).
The first wrangler command opens a browser to log in to Cloudflare. Run npx wrangler login ahead of time if you prefer.
Bindings and configuration
Everything the Worker needs is declared in wrangler.toml, with two values kept as secrets. Here's the whole surface:
| Where | Name | What it is |
|---|---|---|
[[d1_databases]] | DB | The D1 (SQLite) database. Fill in database_id after creating it. |
[[kv_namespaces]] | OAUTH_KV | KV namespace holding OAuth grants and tokens for connected MCP clients. Fill in id after creating it. |
[[send_email]] | EMAIL | Native Cloudflare Email Sending binding. No API key. |
[vars] | BASE_URL | Your deployed origin, no trailing slash. Used to build magic-link and action URLs in emails. |
[vars] | FROM_EMAIL | The sender address, on your onboarded domain. |
wrangler secret put | SECRET | Signs session cookies and action / magic-link tokens. |
wrangler secret put | ADMIN_TOKEN | Bearer for POST /admin/invite, the seed endpoint. |
Generate the two secrets with strong random values:
openssl rand -hex 32Local development
Copy the example dev vars and fill in the two secrets (the same openssl rand -hex 32 values are fine for local use):
cp .dev.vars.example .dev.vars # set SECRET and ADMIN_TOKENBASE_URL and FROM_EMAIL are not secrets — they come from wrangler.toml [vars]. For local runs keep BASE_URL = "http://localhost:8787".
Apply the migrations to your local D1, then start the dev server:
npm run migrate:local # wrangler d1 migrations apply time-debt --local
npm run dev # wrangler dev on http://localhost:8787wrangler dev simulates email locally — it logs each message to the dev console instead of sending it, so magic links and action emails show up right there in your terminal. Run the test suite any time with npm test.
Deploy
Do these in order, the first time:
- Create the D1 database and paste the printed
database_idintowrangler.tomlunder[[d1_databases]]:
npx wrangler d1 create time-debt- Create the OAuth KV namespace and paste the printed
idintowrangler.tomlunder[[kv_namespaces]]:
npx wrangler kv namespace create OAUTH_KV- Set the two vars in
wrangler.toml[vars]:
BASE_URL— your deployed origin (aworkers.devURL or a custom domain), no trailing slash.FROM_EMAIL— a sender on your onboarded domain.
- Set the two secrets:
npx wrangler secret put SECRET
npx wrangler secret put ADMIN_TOKEN- Apply migrations to the remote DB, then deploy:
npm run migrate:remote # wrangler d1 migrations apply time-debt --remote
npm run deploy # wrangler deployMake sure the URL Wrangler prints matches BASE_URL. If it doesn't, update BASE_URL and run npm run deploy again — the emails are only as correct as that value.
Seed the first member
There's a chicken-and-egg here: the in-app invite flow needs an existing member, so the very first one is created out-of-band with the admin token. POST /admin/invite creates a member and emails them a welcome magic link (valid for 7 days):
curl -X POST "$BASE_URL/admin/invite" \
-H "X-Admin-Token: $ADMIN_TOKEN" \
-H 'content-type: application/json' \
-d '{"name":"You","email":"you@domain.com","help_offered":"code review, intros"}'The response carries the new member's id. Open the emailed link to sign into the web ledger.
There's nothing else to hand out: agents connect themselves. An interactive MCP client (claude.ai, Claude Desktop, Claude Code) gets your/mcpURL and walks the member through OAuth — sign in, approve scopes, done. For headless agents andcurl, members mint personal access tokens in the Settings tab of their profile. See the Quickstart and MCP reference.
Make yourself an admin
A freshly seeded member is not an admin (there's deliberately no API to mint the first one). Promote your founder account directly in D1 — use the id from the seed response:
npx wrangler d1 execute time-debt --remote \
--command "UPDATE members SET is_admin = 1 WHERE id = '<your-member-id>'"After that, every other admin can be promoted from the /admin panel — no more raw SQL.
The admin surface
Signed-in admins get an extra page at /admin for running the circle:
- Promote / demote — grant or revoke admin on another member. (You can't demote or deactivate the last remaining active admin.)
- Deactivate / reactivate — "kick" a member or bring them back. A deactivated member can't sign in, can't be reached, and their MCP credentials — OAuth connections and PATs alike — stop working; they vanish from the directory, graph, and digest until reactivated.
Inviting new people, by contrast, is not an admin action — any member can invite from /invite (it costs the inviter 0.5 hours of debt).
Migrations
Schema lives in migrations/ as ordered .sql files and is applied with Wrangler's built-in migration runner:
npx wrangler d1 migrations apply time-debt --local # local D1
npx wrangler d1 migrations apply time-debt --remote # deployed D1npm run migrate:local and npm run migrate:remote are thin aliases for exactly these. When you add a new migration file, re-run the matching command — locally before npm run dev, remotely before npm run deploy.
What's next
- MCP reference — every tool your members' agents can call.
- Email — what the Worker sends, when, and how sending behaves locally vs. deployed.
- How it works — the ledger, balances, and bookings under the hood.