Configuration schema

The root

pithy.config.ts is the concept page for this file, each entry in capabilities is a capability, every name it composes is held to the naming rules, and what it generates into wrangler.jsonc is bindings.

export default definePithyConfig({
  name: "acme",
  environments: ["staging", "prod"],
  domains: { … },
  capabilities: [ … ],
});
Key
nameThe project. Capped at 26 characters — see why
environmentsEvery environment this project deploys to. dev can never be declared
domainsWhere each Worker answers, per environment
capabilitiesThe composition, in order

environments is load-bearing

Every declared environment is managed, and there is no second, narrower list.

An environment deployed and not managed is an environment whose secrets have no master key — which is a silent failure with a config field making it look intentional.

So declaring an environment is what costs a manager Worker, a D1, a set of resource names. A project that does not want five declares fewer.

domains is what worker sync reads

A domains block names where a Worker answers per environment, and pithy worker sync writes the route and the BASE_URL it implies.

Order matters, and it is yours

Middleware mounts in the order the array lists, and your app capability composes last.

So your middleware runs after auth has resolved the session and before another capability’s own gate — which is exactly what lets you put an authorization rule over somebody else’s route.

Every capability validates its own block

At assembly, not at first use.

An unknown algorithm, a roster a model cannot rate, a board field that is immutable, a plugin id the kit reserves, a category key outside your taxonomy — all fail on deploy rather than at 3am.

Where each capability’s keys live

AuthAuth reference
PaymentsPayments reference
StorageStorage reference
MediaMedia reference
EmailEmail reference
VectorVector reference
SecretsSecrets reference
TurnstileTurnstile reference
i18ni18n reference
AuditAudit reference
SupportSupport reference
TestersTesters reference
LedgerLedger reference
LeaderboardLeaderboard reference
RatingRating reference
MultiplayerMultiplayer reference
MatchmakingMatchmaking reference
Control planeControl plane reference

basePath is on almost all of them

Every capability’s mount point is configurable, defaulting to its own name.

Two consequences worth knowing:

A management client composes its calls from the manifest, which names the resolved path — so a client that hardcoded /payments would 404 against exactly the adopters who customized anything.

Two prefixes are fixed regardless. Email’s tracking callbacks keep /_pithy/email, because a tracking URL is already in somebody’s inbox. And testers’ path is movable but effectively one-shot, for the same reason.

The config is loaded by every CLI command

Which is why a Durable Object class must never be exported from a package entry point — that file is what pithy.config.ts imports, and a cloudflare:workers import on that path takes pithy upgrade down with it.

ESC