mirror your GitHub repos to tangled.org automatically
1# synchub.to
2
3> mirror your GitHub repos to [tangled.org](https://tangled.org), automatically.
4
5- [👉 Check it out](https://synchub.to/)
6
7Install the GitHub App, connect your tangled identity, and every commit,
8branch, and tag will be mirrored to tangled. No additional configuration required.
9
10## Features
11
12- no workflow file required
13- one-time OAuth connection to tangled
14- per-push sync of branches and tags
15- a dashboard where you can resync, pause, or rotate keys
16
17> [!IMPORTANT]
18> Only public repositories are synced (tangled does not yet support private repositories).
19
20## Run it locally
21
22You will need [Node 24+](https://nodejs.org), [pnpm 10+](https://pnpm.io)
23(`corepack enable`), a [Neon](https://neon.tech) database (free tier is fine),
24and the [Smee CLI](https://smee.io) for webhook proxying
25(`pnpm add -g smee-client`).
26
27```bash
28corepack enable
29pnpm install
30cp .env.example .env # fill in the values, see below
31pnpm db:migrate
32pnpm dev
33```
34
35`.env.example` documents every variable. Generate the secrets with the bundled
36helpers:
37
38```bash
39pnpm gen:jwk # NUXT_ATPROTO_PRIVATE_JWK
40pnpm gen:encryption-key # NUXT_ENCRYPTION_KEY and NUXT_SESSION_PASSWORD
41pnpm gen:cron-secret # NUXT_CRON_SECRET
42```
43
44The rest (`NUXT_DATABASE_URL`, the `NUXT_GITHUB_APP_*` values) come from your
45Neon dashboard and a [new GitHub App](https://github.com/settings/apps/new).
46The App needs `contents:read` and `metadata:read` permissions plus the `push`,
47`create`, `delete`, and `repository` events, with its webhook pointed at your
48Smee URL.
49
50In separate terminals, proxy webhooks and drain the job queue:
51
52```bash
53smee --url <your-smee-url> --target http://127.0.0.1:3000/api/github/webhook
54pnpm jobs:tick # run as needed; in production Vercel Cron does this
55```
56
57## Deploy to Vercel
58
59synchub.to runs on Vercel with a Neon Postgres database.
60
611. Apply migrations against your production database:
62 ```bash
63 NUXT_DATABASE_URL="<pooled neon connection string>" pnpm db:migrate
64 ```
652. Import the repo into Vercel (the Nuxt preset is auto-detected) and set every
66 variable from `.env.example` under **Settings > Environment Variables**.
67 Mark the secrets (`NUXT_DATABASE_URL`, `NUXT_GITHUB_APP_PRIVATE_KEY`,
68 `NUXT_ATPROTO_PRIVATE_JWK`, `NUXT_ENCRYPTION_KEY`, `NUXT_SESSION_PASSWORD`,
69 `NUXT_GITHUB_WEBHOOK_SECRET`, `NUXT_CRON_SECRET`) as **Sensitive**.
703. Set `NUXT_PUBLIC_URL` to your real origin and point the GitHub App webhook at
71 `https://<your-domain>/api/github/webhook`.
724. Deploy.
73
74The worker runs on a Vercel Cron (declared in `nuxt.config.ts`, so no
75`vercel.json` is needed) and appears under **Settings > Cron Jobs** after the
76first deploy.
77
78> [!NOTE]
79> The GitHub App private key is multi-line, but Vercel env values are single
80> line. Collapse the newlines to literal `\n` before pasting:
81>
82> ```bash
83> awk 'NF {printf "%s\\n", $0}' your-app.private-key.pem
84> ```
85>
86> Locally, keep the real newlines as shown in `.env.example`. Migrations are
87> manual: re-run `pnpm db:migrate` against production whenever you ship a
88> schema change.
89
90## License
91
92Made with ❤️
93
94Published under [MIT License](./LICENCE).