turboflare
v0.1.1
Published
Deploy a Turborepo remote cache server on Cloudflare Workers and R2.
Readme
turboflare
A small Turborepo remote cache server for Cloudflare Workers. Artifacts are stored in Cloudflare R2 and served through the Turborepo Remote Cache API.
What it supports
GET /v8/artifacts/statusHEAD /v8/artifacts/:hashGET /v8/artifacts/:hashPUT /v8/artifacts/:hashPOST /v8/artifactsPOST /v8/artifacts/events- Opaque bearer-token authentication
- Optional team scoping through
teamIdorslug - Artifact metadata headers, including Turborepo signature tags
Authentication model
turboflare uses opaque cache tokens published by a signer Worker.
The cache Worker hashes the incoming bearer token and reads compact token metadata from Workers KV. That metadata carries the allowed teams/projects, optional expiration, and read/write permissions. PUT /artifacts/:hash requires write; reads, status, events, and batch artifact queries require read.
The admin CLI uses Wrangler/Cloudflare authentication to mutate D1 directly. It writes token requests to D1, then a scheduled signer Worker reads pending requests, generates opaque tokens, stores only token hashes in D1, publishes active token metadata to KV, writes one-time token output to a private R2 bucket, and marks the request complete. The CLI reads the private R2 output through Wrangler and prints the token.
Config-Driven Setup
turboflare is designed to be bootstrapped into your own IaC repo. Start in a repo you own:
mkdir acme-infra
cd acme-infra
npm init -y
npx turboflare initThis writes:
alchemy.run.mjsturboflare.config.json- package scripts for apply/admin/deploy/destroy
Edit turboflare.config.json. The config file covers:
- Cloudflare resource names
- allowed Turborepo teams
- users
- projects
- project memberships
- named bootstrap tokens, such as a GitHub Actions token
Configured token IDs are stable. Re-running setup will not silently mint a new token for the same configured ID.
Apply the config:
npm run turboflare:configure
npx wrangler login
npm run turboflare:applyThis verifies Wrangler auth, deploys the Cloudflare stack with Alchemy, generates local support files for Wrangler/D1 commands, applies D1 migrations, writes .turboflare.json for CLI defaults, creates configured users/projects/memberships, and creates any missing configured token requests.
Tokens are only printed when first created. Store the GitHub Actions token in repository secrets as TURBO_TOKEN.
If you are adding turboflare to an existing IaC repo, run the same initializer from that repo:
npx turboflare initThe initializer updates package.json and runs npm install by default. Use --no-install if you want to review the generated files before installing dependencies. Use --force only if you want to overwrite existing generated files.
Alchemy Configuration
Turboflare can infer Cloudflare resource names from turboflare.config.json, but it cannot safely infer which Cloudflare account Alchemy should deploy into. Alchemy needs a profile with a login method and account selection.
Run this once from your IaC repo:
npm run turboflare:configureUse the default Alchemy profile unless you already know you need multiple profiles. The recommended login method is OAuth. If your Cloudflare user has access to multiple accounts, choose the account where the Worker, R2 buckets, D1 database, and KV namespace should live.
If Alchemy later says the profile is not logged in, refresh the OAuth session:
npm run turboflare:loginWrangler authentication is still separate because the admin CLI uses Wrangler for D1, R2, and KV operations:
npx wrangler loginWorking In This Repo
This repository is the implementation and package template. For local development here:
npm install
npx wrangler loginThen run setup. If turboflare.config.json exists, it will be used:
npm run setupsetup does the default path:
- verifies Wrangler/Cloudflare authentication
- creates or updates
.envwith resource names from config/defaults - deploys resources with Alchemy
- applies D1 migrations
- writes
.turboflare.jsonso the admin CLI knows the D1 database, private R2 output bucket, and Wrangler config
To also create an initial project and mint developer/CI tokens:
npm run setup -- --project my-project --user-email [email protected]This uses the project slug as the Turborepo team name. It mints:
- a read/write developer token for
[email protected] - a read/write CI token for subject
ci
You can change the CI subject:
npm run setup -- --project my-project --user-email [email protected] --ci-subject github-actionsThe default names are intentionally boring:
- cache Worker:
turboflare - signer Worker:
turboflare-signer - cache R2 bucket:
turboflare-cache - private token-output R2 bucket:
turboflare-admin-output - D1 database:
turboflare-admin - token KV namespace:
turboflare-tokens
Override only if you need to:
npm run setup -- --worker-name my-cache --signer-worker-name my-cache-signerDeploy with Alchemy
In a bootstrapped IaC repo, Alchemy deployment is just:
npm run turboflare:deployIn this implementation repo, the equivalent is:
npm run infra:deploy -- --env-file .envAlchemy prints the cache Worker URL and signer Worker name after deploy. It also manages:
- R2 bucket for cache artifacts
- private R2 bucket for admin token outputs
- Workers KV namespace for active token metadata
- D1 database for admin metadata
- Cache Worker with
CACHE,TOKENS, andALLOWED_TEAMS - Signer Worker with
DB,TOKEN_OUTPUTS,TOKENS, and a scheduled trigger
To tear everything down:
npm run turboflare:destroyDeploy with Wrangler
Wrangler is still supported if you prefer manual Cloudflare resources.
Create the R2 buckets:
npx wrangler r2 bucket create turboflare-cache
npx wrangler r2 bucket create turboflare-admin-outputCreate the KV namespace and update id in both wrangler.toml and wrangler.signer.toml:
npx wrangler kv namespace create turboflare-tokensCreate the D1 database and apply migrations:
npx wrangler d1 create turboflare-admin
npx wrangler d1 migrations apply turboflare-admin --remoteDeploy the Worker:
npm run deploy
npm run deploy:signerIf you want to limit access to specific Turborepo teams, edit ALLOWED_TEAMS in wrangler.toml:
[vars]
ALLOWED_TEAMS = "my-team,ci-team"Use * to allow any teamId or slug.
Admin CLI
The admin CLI uses Wrangler/Cloudflare authentication. Run npx wrangler login locally, or set CLOUDFLARE_API_TOKEN in CI.
If you used npm run setup, the CLI reads .turboflare.json for the D1 database and private R2 output bucket names.
Create a user and project:
npm run admin -- user-create --email [email protected] --name "Dev"
npm run admin -- project-create --slug my-project --name "My Project"
npm run admin -- member-add --project my-project --email [email protected] --role ownerMint a read/write Turbo cache token:
npm run admin -- token-create --project my-project --subject [email protected] --permissions read,write --ttl 2592000This inserts a token request into D1, waits for the scheduled signer Worker to publish token metadata to KV and write the one-time token output to private R2, reads that output through Wrangler, deletes the output object, and prints the opaque token.
Mint a read-only token for lower-trust environments:
npm run admin -- token-create --project my-project --subject ci-readonly --permissions read --ttl 86400Revoke an issued token:
npm run admin -- token-revoke --grant-id <grantId>Revocation marks the grant revoked in D1 and deletes the active token metadata from KV. KV is eventually consistent, so revocation is not guaranteed to be visible at every edge immediately.
Connect Turborepo
Point Turborepo at your Worker URL:
export TURBO_API="https://turboflare.<your-subdomain>.workers.dev"
export TURBO_TOKEN="<token from the admin CLI>"
export TURBO_TEAM="my-project"Then run your normal Turborepo commands:
npx turbo run buildFor local machines, you can also use Turborepo manual login:
npx turbo login --manualUse the Worker URL as the API URL, the same token as the token, and your team slug/id as the team.
Local development
Copy the example local secrets file:
cp .dev.vars.example .dev.varsEdit .dev.vars, then run:
npm run devApply local D1 migrations for the admin data layer:
printf 'y\n' | npx wrangler d1 migrations apply turboflare-admin -c wrangler.signer.toml --localRun the signer locally with scheduled-event testing:
npm run dev:signer -- --test-scheduledAfter creating a token request locally, trigger signing:
curl http://127.0.0.1:8788/__scheduledVerify
npm test
npm run typecheckRun the local end-to-end smoke test with Wrangler and a real Turbo fixture:
npm run smoke:turboThis starts wrangler dev, points the fixture project at the local Worker with TURBO_API, runs one build to upload an artifact, deletes the local Turbo cache and build output, then runs a second build and expects a remote cache hit.
Notes
Turborepo treats logs as cache artifacts. Avoid printing secrets during builds, because remote cache artifacts can contain task logs.
Artifact signing is handled by Turborepo itself. If your repo enables:
{
"remoteCache": {
"signature": true
}
}set TURBO_REMOTE_CACHE_SIGNATURE_KEY wherever turbo runs. This Worker stores and returns the x-artifact-tag header needed for verification.
