A Startup API-powered Cloudflare Worker, created with
npm create startup-api. It transparently proxies requests to your origin and
layers on user accounts, authentication, and other Startup API features.
The worker logic lives in the @startup-api/cloudflare
package. This project is a thin wrapper: a src/index.ts that
builds a configured instance with the createStartupAPI factory and re-exports
it, plus per-deployment settings in wrangler.jsonc and
.dev.vars. Upgrade the framework by bumping the @startup-api/cloudflare
dependency.
Configuration is split in two:
- Environment variables (
.dev.vars,wrangler.jsoncvars, or the dashboard) hold credentials and secrets plus per-deployment values:ORIGIN_URL,SESSION_SECRET, the OAuth*_CLIENT_ID/*_CLIENT_SECRETpairs,AUTH_ORIGIN,USERS_PATH,ADMIN_IDS. A provider turns on automatically once its client id and secret are present. - The
createStartupAPIfactory in src/index.ts holds non-secret behavior: which providers to configure, extra OAuth scopes, the Patreon campaign id, entitlement freshness, and the access policy.
npm install # also syncs framework assets into ./public
npm run dev # http://localhost:8765Local secrets live in .dev.vars (gitignored); a SESSION_SECRET was generated
for you at creation time. See .dev.vars.example for all
supported variables.
npm run deploy
npx wrangler secret put SESSION_SECRET # set the production session secretSet ORIGIN_URL and any OAuth credentials in the Cloudflare dashboard
(Workers & Pages → your worker → Settings → Variables) or in the vars
block of wrangler.jsonc. Keep secrets out of source control —
set them with wrangler secret put:
# (no auth provider secrets to set)This project was scaffolded with AT Protocol (Bluesky) enabled, configured
in providers in src/index.ts. Credential-based providers turn on
once their client id and secret are present (placeholders are already in .dev.vars).
To finish setup for each provider:
AT Protocol (Atmosphere) login is decentralized: there is no provider to register with and no client secret. The worker is a public OAuth client identified by a metadata document it serves itself.
- Already enabled — its key is in
providersin src/index.ts. To toggle it per deployment without code, setATPROTO_ENABLEDtruthy instead. - Deploy over HTTPS with a stable hostname. The worker automatically serves its
client metadata at
https://<your-worker-url>/users/auth/atproto/client-metadata.json(this URL is the OAuthclient_id) and registers the redirect URIhttps://<your-worker-url>/users/auth/atproto/callback. - That's it — visitors sign in with their handle (e.g.
alice.bsky.social) or DID; their own server handles authentication. No secret to set.
| Variable | Required | Description |
|---|---|---|
ORIGIN_URL |
Yes | Base URL of the origin/object this worker proxies to. |
SESSION_SECRET |
Yes | Secret used to sign session cookies (set as a Wrangler secret). |
USERS_PATH |
No | Path for internal assets (default /users/). |
AUTH_ORIGIN |
No | Base URL for OAuth redirects (overrides request origin). |
GOOGLE_CLIENT_ID |
No | Google OAuth2 client ID. |
GOOGLE_CLIENT_SECRET |
No | Google OAuth2 client secret. |
TWITCH_CLIENT_ID |
No | Twitch OAuth2 client ID. |
TWITCH_CLIENT_SECRET |
No | Twitch OAuth2 client secret. |
PATREON_CLIENT_ID |
No | Patreon OAuth2 client ID. |
PATREON_CLIENT_SECRET |
No | Patreon OAuth2 client secret. |
PATREON_WEBHOOK_SECRET |
No | Secret for verifying Patreon webhook signatures. |
ATPROTO_ENABLED |
No | Set truthy (true/1/yes/on) to enable AT Protocol (Bluesky) login without code. |
ADMIN_IDS |
No | Comma-separated admin user IDs. |
Setting an OAuth provider's client id and secret enables it. Redirect URIs follow
the pattern https://<your-worker-url>/users/auth/<provider>/callback. AT Protocol
(Bluesky) has no credentials — enable it via atproto: {} in
src/index.ts or the ATPROTO_ENABLED flag above.
OAuth scopes, the Patreon campaign id, entitlement freshness, and the access
policy are configured in src/index.ts by passing a config object
to createStartupAPI:
import { createStartupAPI } from '@startup-api/cloudflare';
const api = createStartupAPI({
providers: {
google: {},
twitch: {},
patreon: {
scopes: 'identity.memberships',
campaignId: '<CAMPAIGN_ID>',
freshness: { ttl: true },
},
},
// accessPolicy: { rules: [/* ... */], default: { mode: 'public' } },
});
export default api.default;
export const { UserDO, AccountDO, SystemDO, CredentialDO } = api;Enabling a provider's freshness.cron also requires a matching triggers.crons
in wrangler.jsonc; freshness.webhook (Patreon) requires
PATREON_WEBHOOK_SECRET and a webhook pointed at
<your-worker-url>/users/webhooks/patreon.
npm run update-startup-api # install the latest @startup-api/cloudflare + refresh ./public/usersThis installs the newest published @startup-api/cloudflare (across minor/major
versions) and re-runs sync-assets via the postinstall hook. To stay within your
current semver range instead, use npm update @startup-api/cloudflare.