synchub.to#
mirror your GitHub repos to tangled.org automatically.
Install the GitHub App, connect your tangled identity, and every commit, branch, and tag will be mirrored to tangled.
Features#
- no workflow file required
- one-time OAuth connection to tangled
- per-push sync of branches and tags
- a dashboard where you can resync, pause, or rotate keys
IMPORTANT
Only public repositories are synced (tangled does not yet support private repositories).
Run it locally#
You will need Node 24+, pnpm 10+
(corepack enable), a Neon database (free tier is fine),
and the Smee CLI for webhook proxying
(pnpm add -g smee-client).
corepack enable
pnpm install
cp .env.example .env # fill in the values, see below
pnpm db:migrate
pnpm dev
.env.example documents every variable. Generate the secrets with the bundled
helpers:
pnpm gen:jwk # NUXT_ATPROTO_PRIVATE_JWK
pnpm gen:encryption-key # NUXT_ENCRYPTION_KEY and NUXT_SESSION_PASSWORD
pnpm gen:cron-secret # NUXT_CRON_SECRET
The rest (NUXT_DATABASE_URL, the NUXT_GITHUB_APP_* values) come from your
Neon dashboard and a new GitHub App.
The App needs contents:read and metadata:read permissions plus the push,
create, delete, and repository events, with its webhook pointed at your
Smee URL. Set the Setup URL to <origin>/connect (tick Redirect on
update) and the Callback URL to <origin>/api/github/oauth/callback; the
latter drives the user-OAuth that verifies a connecting user administers the
installation before any handle is bound. Copy the App's Client ID and a
generated client secret into NUXT_GITHUB_APP_CLIENT_ID /
NUXT_GITHUB_APP_CLIENT_SECRET.
In separate terminals, proxy webhooks and drain the job queue:
smee --url <your-smee-url> --target http://127.0.0.1:3000/api/github/webhook
pnpm jobs:tick # run as needed; in production Vercel Cron does this
Deploy to Vercel#
synchub.to runs on Vercel with a Neon Postgres database.
- Apply migrations against your production database:
NUXT_DATABASE_URL="<pooled neon connection string>" pnpm db:migrate - Import the repo into Vercel (the Nuxt preset is auto-detected) and set every
variable from
.env.exampleunder Settings > Environment Variables. Mark the secrets (NUXT_DATABASE_URL,NUXT_GITHUB_APP_PRIVATE_KEY,NUXT_GITHUB_APP_CLIENT_SECRET,NUXT_ATPROTO_PRIVATE_JWK,NUXT_ENCRYPTION_KEY,NUXT_SESSION_PASSWORD,NUXT_GITHUB_WEBHOOK_SECRET,NUXT_CRON_SECRET) as Sensitive. - Set
NUXT_PUBLIC_URLto your real origin, point the GitHub App webhook athttps://<your-domain>/api/github/webhook, and set the App's Setup + Callback URLs tohttps://<your-domain>/connectandhttps://<your-domain>/api/github/oauth/callback. - Deploy.
The worker runs on a Vercel Cron (declared in nuxt.config.ts, so no
vercel.json is needed) and appears under Settings > Cron Jobs after the
first deploy.
NOTE
The GitHub App private key is multi-line, but Vercel env values are single
line. Collapse the newlines to literal \n before pasting:
awk 'NF {printf "%s\\n", $0}' your-app.private-key.pem
Locally, keep the real newlines as shown in .env.example. Migrations are
manual: re-run pnpm db:migrate against production whenever you ship a
schema change.
License#
Made with ❤️
Published under MIT License.