mirror your GitHub repos to tangled.org automatically synchub.to
mirror sync tangled github git
TypeScript 86%
Vue 13%
CSS <1%
<1%

README.md

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+ and pnpm 10+.

Start by creating a Neon project, then copy the pooled Postgres connection string from the Neon dashboard. Keep it ready for the NUXT_DATABASE_URL value in the env setup step below.

Next, open smee.io, create a new channel, and copy that URL. This URL is the webhook destination GitHub should send to during local development. If you do not have the CLI yet, install it with pnpm add -g smee-client.

Then create a new GitHub App at github.com/settings/apps/new. On the creation form, set GitHub App name to any unique name, Homepage URL to your local origin (http://127.0.0.1:3000), Callback URL to http://127.0.0.1:3000/api/github/oauth/callback, Setup URL to http://127.0.0.1:3000/connect, and enable "Redirect on update".

Leave the user authorization toggles at their defaults, but do copy the Client ID and generate a client secret: the Callback URL above drives a user-OAuth leg that proves a connecting user administers the installation before any tangled handle is bound to it, so NUXT_GITHUB_APP_CLIENT_ID and NUXT_GITHUB_APP_CLIENT_SECRET are required, not optional. Keep webhooks active and set Webhook URL to your Smee URL with a webhook secret you choose.

Set repository permissions to contents:read and metadata:read. For event subscriptions, use push, create, delete, and repository.

If you do not see those event checkboxes yet, save the app after adding the webhook URL, then return to the "Permissions & events" page and select them there.

After creation, copy App ID, Client ID, generate a client secret, and generate a private key (.pem) for the NUXT_GITHUB_APP_* values.

If you need to install this app in organizations/accounts other than the owner account, go to Advanced > Danger zone and make the app public.

Now install dependencies, which the secret-generation helpers below need:

corepack enable
pnpm install

Create your local env file and fill in every variable:

cp .env.example .env

Use these helpers for the generated values:

pnpm gen:jwk               # NUXT_ATPROTO_PRIVATE_JWK
pnpm gen:encryption-key    # NUXT_ENCRYPTION_KEY and NUXT_SESSION_PASSWORD
pnpm gen:cron-secret       # CRON_SECRET

Once .env is complete, apply migrations:

pnpm db:migrate

Local development runs in three terminals. Proxy webhooks to your dev server:

smee --url <your-smee-url> --target http://127.0.0.1:3000/api/github/webhook

Run the app:

pnpm dev

And drain the job queue as needed while developing (Vercel Cron does this in production):

pnpm jobs:tick

Deploy to Vercel #

synchub.to runs on Vercel with a Neon Postgres database.

  1. Apply migrations against your production database:
    NUXT_DATABASE_URL="<pooled neon connection string>" pnpm db:migrate
    
  2. Import the repo into Vercel (the Nuxt preset is auto-detected) and set every variable from .env.example under 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, CRON_SECRET) as Sensitive.
  3. Set NUXT_PUBLIC_URL to your real origin, point the GitHub App webhook at https://<your-domain>/api/github/webhook, and set the App's Setup + Callback URLs to https://<your-domain>/connect and https://<your-domain>/api/github/oauth/callback.
  4. 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.