···4455- [👉 Check it out](https://synchub.to/)
6677+Install the GitHub App, connect your tangled identity, and every commit,
88+branch, and tag will be mirrored to tangled. No additional configuration required.
99+710## Features
81199-- a GitHub App
1010-- OAuth connection to tangled
1212+- no workflow file required
1313+- one-time OAuth connection to tangled
1114- per-push sync of branches and tags
1515+- a dashboard where you can resync, pause, or rotate keys
12161317> [!IMPORTANT]
1418> Only public repositories are synced (tangled does not yet support private repositories).
15191616-## Try it out locally
2020+## Run it locally
17211818-You will need:
2222+You will need [Node 24+](https://nodejs.org), [pnpm 10+](https://pnpm.io)
2323+(`corepack enable`), a [Neon](https://neon.tech) database (free tier is fine),
2424+and the [Smee CLI](https://smee.io) for webhook proxying
2525+(`pnpm add -g smee-client`).
19262020-1. A GitHub App with a webhook pointed at your local tunnel and `contents:read`, `metadata:read` permissions plus the `push`, `create`, `delete`, `repository` events.
2121-2. A [Neon](https://neon.tech) Postgres database.
2222-3. An AT Protocol confidential OAuth client (see `.well-known/atproto-client-metadata.json`).
2727+```bash
2828+corepack enable
2929+pnpm install
3030+cp .env.example .env # fill in the values, see below
3131+pnpm db:migrate
3232+pnpm dev
3333+```
23342424-Set your environment variables in a `.env` file:
3535+`.env.example` documents every variable. Generate the secrets with the bundled
3636+helpers:
25372626-```env
2727-NUXT_GITHUB_APP_ID=<github app id>
2828-NUXT_GITHUB_APP_PRIVATE_KEY=<github app private key, pem>
2929-NUXT_GITHUB_WEBHOOK_SECRET=<github app webhook secret>
3030-NUXT_DATABASE_URL=<neon postgres connection string>
3131-NUXT_ENCRYPTION_KEY=<32-byte base64 key for sealing ssh private keys>
3232-NUXT_CRON_SECRET=<shared secret for the worker cron route>
3838+```bash
3939+pnpm gen:jwk # NUXT_ATPROTO_PRIVATE_JWK
4040+pnpm gen:encryption-key # NUXT_ENCRYPTION_KEY and NUXT_SESSION_PASSWORD
4141+pnpm gen:cron-secret # NUXT_CRON_SECRET
3342```
34433535-### Setup
4444+The rest (`NUXT_DATABASE_URL`, the `NUXT_GITHUB_APP_*` values) come from your
4545+Neon dashboard and a [new GitHub App](https://github.com/settings/apps/new).
4646+The App needs `contents:read` and `metadata:read` permissions plus the `push`,
4747+`create`, `delete`, and `repository` events, with its webhook pointed at your
4848+Smee URL.
4949+5050+In separate terminals, proxy webhooks and drain the job queue:
36513752```bash
3838-# install dependencies
3939-corepack enable
4040-pnpm install
5353+smee --url <your-smee-url> --target http://127.0.0.1:3000/api/github/webhook
5454+pnpm jobs:tick # run as needed; in production Vercel Cron does this
5555+```
5656+5757+## Deploy to Vercel
5858+5959+synchub.to runs on Vercel with a Neon Postgres database.
41604242-# serve in dev mode, with hot reload at localhost:3000
4343-pnpm dev
6161+1. Apply migrations against your production database:
6262+ ```bash
6363+ NUXT_DATABASE_URL="<pooled neon connection string>" pnpm db:migrate
6464+ ```
6565+2. Import the repo into Vercel (the Nuxt preset is auto-detected) and set every
6666+ variable from `.env.example` under **Settings > Environment Variables**.
6767+ Mark the secrets (`NUXT_DATABASE_URL`, `NUXT_GITHUB_APP_PRIVATE_KEY`,
6868+ `NUXT_ATPROTO_PRIVATE_JWK`, `NUXT_ENCRYPTION_KEY`, `NUXT_SESSION_PASSWORD`,
6969+ `NUXT_GITHUB_WEBHOOK_SECRET`, `NUXT_CRON_SECRET`) as **Sensitive**.
7070+3. Set `NUXT_PUBLIC_URL` to your real origin and point the GitHub App webhook at
7171+ `https://<your-domain>/api/github/webhook`.
7272+4. Deploy.
44734545-# build for production
4646-pnpm build
7474+The worker runs on a Vercel Cron (declared in `nuxt.config.ts`, so no
7575+`vercel.json` is needed) and appears under **Settings > Cron Jobs** after the
7676+first deploy.
47774848-# preview in production mode
4949-pnpm preview
5050-```
7878+> [!NOTE]
7979+> The GitHub App private key is multi-line, but Vercel env values are single
8080+> line. Collapse the newlines to literal `\n` before pasting:
8181+>
8282+> ```bash
8383+> awk 'NF {printf "%s\\n", $0}' your-app.private-key.pem
8484+> ```
8585+>
8686+> Locally, keep the real newlines as shown in `.env.example`. Migrations are
8787+> manual: re-run `pnpm db:migrate` against production whenever you ship a
8888+> schema change.
51895290## License
5391