Naming rules and limits

One rule

Naming is the concept page. The names this governs land as bindings and tables, and the ceilings it derives against are accepted limits.

<project>-<env>-<thing>

Kebab-case, project first, environment second. No namespace is exempt.

Why there is a rule at all

Every Cloudflare namespace Pithy writes into is flat and account-wide — D1, KV, R2, Vectorize, Worker scripts, Workflows, the Secrets Store, the API-token list. None can be partitioned.

So the name is the partition, and the project segment is the only thing keeping two Pithy projects in one account from adopting or overwriting each other’s resources.

Provisioning finds a resource by name and reuses it. Without a project segment, find-then-create means a second project silently inherits the first’s database.

The namespaces

NamespaceNameExample
D1<project>-<env>-<binding>acme-prod-db
KV<project>-<env>-<binding>acme-prod-sessions
R2<project>-<env>-<capability>acme-prod-storage
Vectorize<project>-<env>-vector-<index>acme-prod-vector-docs
Worker script<project>-<env>-<capability>acme-prod-email
Workflow<project>-<env>-<capability>-<job>acme-prod-email-send
Secrets Store entry<project>-<env>-<secret>acme-prod-secrets-encryption-keys
CF API token<project>-<env>-<profile>acme-prod-ci-system
Email Routing rule<project>-global-<capability>-<purpose>acme-global-email-bounce
Turnstile widget<project>-prod-turnstile-<mode>acme-prod-turnstile-visible

Three things deliberately not on that list

Worker binding names — DB, SESSIONS, MEDIA_BUCKET — never carry the project. A binding is a variable name inside your own Worker; two projects cannot collide on it, and renaming one is a breaking change to your code for no benefit.

Secret registry keys never carry it either. For a Secrets Store secret the registry key is the binding name and the .dev.vars variable name — only the store entry it resolves to is scoped.

Table names stay pithy_<capability>_<table>. They live inside one database, which is already project-scoped by the database’s own name.

The length caps, and whose they are

NamespaceMaxOverflowSource
Workflow64RefuseCloudflare
Vectorize index64 bytesRefuseCloudflare
Worker script63RefuseCloudflare
R2 bucket63 (min 3)TruncateCloudflare
KV namespace title512TruncateCloudflare
D1 database128TruncatePithy’s
Secrets Store entry128TruncatePithy’s
API token label128TruncatePithy’s

Where the source is Pithy’s, Cloudflare documents no cap and 128 is the kit’s own ceiling.

The project-name budget

A project name is capped at 26 characters, and that number is derived rather than chosen — it is what the longest feature resource name leaves against R2’s 63:

    63   an R2 bucket name
  -  2   `-f`
  -  6   the issue number, six digits
  - 13   `-` + a legible slug
  - 13   `-` + a binding kept whole
  -  3   `-` + the kind
  ----
    26

The Workflow namespace is the other binding constraint, leaving 33:

    64   a Workflow name
  -  1   the hyphen
  -  7   the longest environment, `staging`
  -  1   the hyphen
  - 22   the longest capability-job, `media-audio-transcribe`
  ----
    33

A longer capability-job tail is refused rather than letting a new capability silently invalidate the project-name cap every existing project was accepted under.

Environments

dev, staging and prod are first class; staging at 7 characters is the longest, and that is the number the budgets above reserve.

A custom environment is a string, and still validated. dev can never be declared — it is the top-level stanza and it always exists.

Every declared environment is managed. There is no second, narrower list, because an environment deployed and not managed is one whose secrets have no master key.

Charset rules worth knowing

An R2 bucket must start and end alphanumeric.

A Vectorize index must start with a letter — which the project rule already guarantees, so the two agree by construction.

A Workflow name may start with an underscore. A Worker script may not start or end with a dash.

ESC