A Tangled knot on CF Workers, backed by CF Artifacts

Files
README.md

tangledart #

A Tangled knot running on Cloudflare Workers, with Cloudflare Artifacts as its Git backend.

Tangled is a code hosting platform built on AT Protocol. Repositories live on servers called knots, which you can host yourself. Artifacts handles Git storage; tangledart adds Tangled's authentication, permissions and repository APIs. Durable Objects store metadata and events, and R2 stores Git LFS files.

It's still experimental. Git works over HTTPS only, there's no SSH. The current org.tangled.* protocol includes Git queries, repository-scoped record writes, signed AT Protocol repositories and the binary subscribeRepos event stream. The earlier sh.tangled.* routes remain available for existing clients.

Set KNOT_CONTACT_EMAIL to a public operator email. Enable KNOT_CURRENT_PROTOCOL=true only after importing and verifying existing repository metadata. The operator tool in scripts/upgrade-current-protocol.ts preserves source evidence, verifies signed CAR exports and separates backup, staging, activation, identity discovery and foster handover. Preserve the knot signing key and repository DIDs throughout the upgrade.

Current record repositories have a 16 MiB limit and 10,000 records. Record attachments are limited to 1 MiB each and 32 MiB per repository, stored in Durable Object SQL and included in backups. Git LFS has separate limits below. This implementation does not provide native Rust knot COB filesystem parity.

AI disclosure #

I built this with help from AI coding agents. I directed the design and did the testing and debugging.

Run locally #

Requires Node.js 24 and Git. No Cloudflare account needed.

npm ci
npm run dev

This starts the Worker with a temporary Git server and test identities. Run npm test for unit tests, or npm run test:integration for the integration suite (also requires Git LFS). npm run test:current checks current Tangled schemas, signed records, permission changes, event streaming and restart persistence against a local Git backend.

Deploy #

In Cloudflare, you'll need an active domain, Artifacts access, R2 enabled and a Workers plan supporting the configured CPU limit. See the Artifacts setup and Workers docs.

npm ci
npx wrangler login
cp wrangler.jsonc wrangler.deploy.local.jsonc

Edit the copy with your Worker name, knot hostname (both the route and KNOT_HOSTNAME), account DID (KNOT_OWNER), Artifacts namespace and R2 bucket name. Use a separate namespace and bucket for each knot, and leave the rest as is.

npx wrangler r2 bucket create YOUR_BUCKET_NAME
npm run key:generate -- /path/to/private/worker-secrets.json
npm run config:check -- --config wrangler.deploy.local.jsonc --secrets-file /path/to/private/worker-secrets.json
npm run deploy -- --config wrangler.deploy.local.jsonc --secrets-file /path/to/private/worker-secrets.json

Deployment creates the Worker and Durable Object namespaces, and sets up the custom domain. Artifacts creates its namespace with the first repository. Keep the R2 bucket private. Back up your signing key; don't commit or replace it.

Git clients and Tangled's servers need to reach the hostname directly, so don't put it behind Cloudflare Access or a browser challenge. If your bot or WAF rules block them, add an exception for that hostname. Check https://YOUR_KNOT_HOST/xrpc/_health, then register the knot on Tangled.

To update later, run npm run deploy -- --config wrangler.deploy.local.jsonc. Your secrets stay in place.

Large files #

Use Git LFS for models and other large files. The defaults allow 3 GiB per file and 30 GiB of LFS storage per repository; both limits are configurable.

For files above 64 MiB, configure direct R2 uploads to avoid Cloudflare's incoming request-size limit. Set LFS_R2_ENDPOINT to your R2 S3 account endpoint and LFS_R2_BUCKET to the bucket bound as LFS_BUCKET. Create an R2 API token with Object Read & Write, restricted to that bucket, and install its credentials:

npx wrangler secret put R2_ACCESS_KEY_ID --config wrangler.deploy.local.jsonc
npx wrangler secret put R2_SECRET_ACCESS_KEY --config wrangler.deploy.local.jsonc

In that bucket, add an object lifecycle rule to delete objects under lfs/staging/ after one day. Keep the bucket private. Git LFS uploads directly using short-lived URLs; the knot checks size, SHA-256 and permissions before publishing the file. No extra client setup or public bucket is needed. Without these credentials, uploads through the Worker remain limited to 64 MiB.

Push from your computer #

The credential helper stores session tokens in ~/.config/tangledart/credentials/ (or under XDG_CONFIG_HOME). Files use mode 600; tokens are unencrypted on disk. Your app password is not saved. The helper gets short-lived push tokens from your PDS.

npm run build:cli
npm install --global ./tools/tangled-cli
tangledart-login YOUR_HANDLE --knot YOUR_KNOT_HOST

Login asks for an app password. Create a repository on Tangled, choose your knot, and use its HTTPS URL with ordinary Git commands. tangledart-logout YOUR_KNOT_HOST removes your saved session.

License #

MIT. See third-party notices for dependency credits.