Addons

district addon creates and removes a workspace's addons: D1 databases, R2 buckets and KV namespaces. An addon belongs to a workspace, not to an app. Every member of the workspace can read it; a member with the resource permissions can create, rename and delete it. No clone is read: each subcommand accepts --workspace <id> and the environment flags, and a caller who can open several workspaces names one with --workspace <id>.

A workspace holds at most 10 D1 databases, 10 R2 buckets and 3 KV namespaces, deleting and failed ones included. A display name is 1 to 100 characters and unique among the live addons of one kind in the workspace, so the same name can name a D1 database and an R2 bucket.

Creating an addon binds it to no app. An app binds it through its own wrangler configuration, which names the addon by its alias, and the next deployment reads that configuration. Bindings and environments gives the rules. The alias is the hadd_ id in lower case with the underscore replaced by a hyphen: hadd-01k4x0j8w1qz5b7n3g2r6tq9yd. It never changes; a rename changes the display name only.

District ships no SQL or migration command. A D1 schema is the app's own responsibility, applied through app code at runtime. One app is the schema owner of each database: two apps that migrate the same database can apply conflicting changes, and a rollback of a deployment does not undo a schema change.

The CLI and the API show no Cloudflare identifier of an addon. Deleting an addon destroys its data. The deletion cannot be undone, and District promises no recovery for R2 objects or KV values. Deleting an app deletes no addon. district addon delete requires --confirm.

A subcommand that names one addon takes a hadd_ id or a display name. An id needs no workspace; a value that starts with hadd_ and is not a well-formed id is a usage error, not a name. A name is looked up in the selected workspace, in every kind unless --kind names one. A name that two kinds hold is a usage error that lists the candidates with their --kind.

In a terminal, each status carries a symbol and a color: ✔ ready, ✖ failed, ○ provisioning. A status has one of these values:

  • provisioning: the addon exists and the platform is creating the resource. The platform retries a refused create on its own schedule.
  • ready: the resource exists. An app can bind the addon.
  • failed: the provider refused the resource, or the platform gave up. The recorded error says why. The addon holds a workspace slot until it is deleted.
  • deleting: the deletion is claimed and the platform is removing the resource. The addon leaves lists once the provider confirms.
  • deleted: the resource is gone. No list or lookup answers the addon.

Each subcommand exits 3 when the addon service does not answer. Run the command again after the number of seconds in the message. The message says what the call left behind.

district addon create

district addon create <kind> <name> [--workspace <id>] [--json] [--production | --staging | --dev]

Creates a D1 database (d1), an R2 bucket (r2) or a KV namespace (kv) in the workspace, and waits for the resource. The command prints the addon as soon as it exists, then reads it every 2 seconds for up to one minute. Once the addon is ready, it prints the wrangler configuration fragment that binds it.

Creating an addon binds it to no app. An app binds the addon through its own wrangler configuration, which names the addon by its alias, and the next deployment reads that configuration. Each environment section of the configuration lists its own bindings.

Arguments

ArgumentDescription
<kind>The resource to create: d1, r2 or kv.
<name>The display name, 1 to 100 characters. It must be free among the live addons of the kind in the workspace.

Flags

FlagDescriptionDefault
--workspace <id>The workspace, when you can open more than one. The error lists them.-
--jsonPrints the addon after the wait as one JSON object, without the snippet or any progress line.false
--production | --staging | --devThe environment to act in. Pass one at most. Without a flag, the command uses the environment that the last district login recorded, or production.-

Output

$ district addon create d1 orders
created orders (hadd_01K4X0J8W1QZ5B7N3G2R6TQ9YD)
kind       d1
status     ○ provisioning
workspace  ws_01K4X0J8W1QZ5B7N3G2R6TQ9YD
alias      hadd-01k4x0j8w1qz5b7n3g2r6tq9yd
status     ✔ ready

Creating an addon binds it to no app. Add the binding to the wrangler configuration of the app that uses it; the
next deployment reads it. Each environment section lists its own bindings.

hadd-01k4x0j8w1qz5b7n3g2r6tq9yd
{
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "hadd-01k4x0j8w1qz5b7n3g2r6tq9yd"
    }
  ]
}

The snippet puts the alias in the field the provider resolves a name by: database_name under d1_databases for D1, bucket_name under r2_buckets for R2 and id under kv_namespaces for KV. The binding name (DB, FILES, CACHE) is a placeholder the app renames.

A provider that answers within the request makes the addon ready at once, and the command prints the snippet without a wait. An addon whose provisioning fails prints its recorded error and exits 1; the addon holds a workspace slot until it is deleted. An addon still provisioning after one minute exits 1 with district addon show <id> in the message: the platform keeps trying on its own schedule.

The command sends one creation key per invocation and repeats a request that the API answered 503 or did not answer with the same key, so a repeat cannot create a second addon. After three attempts it exits 3 and says the addon may exist. A workspace at its cap, a taken name, and a workspace the caller cannot create resources in are refused with the API's own message.

Exit codes

Exit codeMeaning
0The addon is ready. Its snippet is printed.
1The API refused the call with its own message: the workspace is at its cap, the name is taken, or the caller cannot create resources in the workspace. Provisioning failed, the addon was still provisioning after one minute, or no read of it answered in that minute.
2The command line names a kind that is not d1, r2 or kv, or the caller can open several workspaces and names none.
3The addon service did not answer. The message says what the call left behind.

district addon list

district addon list [--kind d1|r2|kv] [--workspace <id>] [--json] [--production | --staging | --dev]

Prints a header row, then one row per addon of the workspace, newest first: the name, the kind, the status and the id. Deleted addons are omitted; a deleting addon is listed until the provider confirms. A workspace with no addon prints nothing and exits 0.

Flags

FlagDescriptionDefault
--kind d1|r2|kvLists addons of one kind only.every kind
--workspace <id>The workspace, when you can open more than one. The error lists them.-
--jsonPrints the addons as one JSON array.false
--production | --staging | --devThe environment to act in. Pass one at most. Without a flag, the command uses the environment that the last district login recorded, or production.-

Output

$ district addon list
NAME     KIND  STATUS    ADDON
orders   d1    ✔ ready   hadd_01K4X0J8W1QZ5B7N3G2R6TQ9YD
uploads  r2    ✖ failed  hadd_01K4X0J8W1QZ5B7N3G2R6TQ9YE

Exit codes

Exit codeMeaning
0The rows were printed. A workspace with no addon also exits 0.
1The API refused the call with its own message.
2The command line names a kind that is not d1, r2 or kv, or the caller can open several workspaces and names none.
3The addon service did not answer. The message says what the call left behind.

district addon show

district addon show <addon> [--kind d1|r2|kv] [--workspace <id>] [--json] [--production | --staging | --dev]

Prints the addon's state, its alias, its recorded error while the API holds one, and the apps that bind it.

The apps list is a display list: the API answers up to 100 apps the caller can read and states no total. A list at the bound gets a sentence that says more can bind the addon. The command reads no deployment list; district addon delete reports the complete reference set when it refuses. An app is in the list while one of its retained deployments binds the addon, including a deployment that no longer serves.

Arguments

ArgumentDescription
<addon>The addon, by hadd_ id or by display name. An id needs no workspace. A name is looked up in the selected workspace, in every kind unless --kind names one.

Flags

FlagDescriptionDefault
--kind d1|r2|kvThe kind of the addon a display name selects. Required when two kinds hold the name; the error lists them.-
--workspace <id>The workspace, when you can open more than one. The error lists them.-
--jsonPrints the addon, with boundApps, as one JSON object.false
--production | --staging | --devThe environment to act in. Pass one at most. Without a flag, the command uses the environment that the last district login recorded, or production.-

Output

$ district addon show orders
addon    orders (hadd_01K4X0J8W1QZ5B7N3G2R6TQ9YD)
kind     d1
status   ✔ ready
alias    hadd-01k4x0j8w1qz5b7n3g2r6tq9yd
created  2026-08-25T09:00:00Z
updated  2026-08-25T09:00:05Z

no app binds this addon

Exit codes

Exit codeMeaning
0The addon was printed.
1The API refused the call with its own message.
2The command line names a malformed id, an addon nobody can read, a name no kind holds, a name two kinds hold without --kind, or the caller can open several workspaces and names none.
3The addon service did not answer. The message says what the call left behind.

district addon rename

district addon rename <addon> <new-name> [--kind d1|r2|kv] [--workspace <id>] [--json] [--production | --staging | --dev]

Changes the display name and prints the new name with the unchanged id and alias. No app configuration changes. A taken name, a name outside its bounds, and a deleting addon are refused with the API's own message.

Arguments

ArgumentDescription
<addon>The addon, by hadd_ id or by display name. An id needs no workspace. A name is looked up in the selected workspace, in every kind unless --kind names one.
<new-name>The new display name, 1 to 100 characters. It must be free among the live addons of the kind in the workspace.

Flags

FlagDescriptionDefault
--kind d1|r2|kvThe kind of the addon a display name selects. Required when two kinds hold the name; the error lists them.-
--workspace <id>The workspace, when you can open more than one. The error lists them.-
--jsonPrints the addon after the rename as one JSON object.false
--production | --staging | --devThe environment to act in. Pass one at most. Without a flag, the command uses the environment that the last district login recorded, or production.-

Output

$ district addon rename orders orders-v2
renamed orders to orders-v2 (hadd_01K4X0J8W1QZ5B7N3G2R6TQ9YD)
alias  hadd-01k4x0j8w1qz5b7n3g2r6tq9yd (unchanged)

Exit codes

Exit codeMeaning
0The addon has its new name.
1The API refused the call with its own message: the name is taken or outside its bounds, the addon is deleting, or the caller cannot update resources in the workspace.
2The command line names a malformed id, an addon nobody can read, a name no kind holds, a name two kinds hold without --kind, or the caller can open several workspaces and names none.
3The addon service did not answer. The message says what the call left behind.

district addon delete

district addon delete <addon> --confirm [--kind d1|r2|kv] [--workspace <id>] [--json] [--production | --staging | --dev]

Deletes the addon and destroys its data. The deletion cannot be undone. --confirm is required: without it the command exits 2 before any call and deletes nothing. No terminal question stands in for the flag, so a script and a person state the same intent.

The deletion is claimed at once: no app can bind the addon from then on. deleted means the provider confirmed the resource is gone within the request. deleting means the platform is still removing it; the addon leaves district addon list once the provider confirms.

Arguments

ArgumentDescription
<addon>The addon, by hadd_ id or by display name. An id needs no workspace. A name is looked up in the selected workspace, in every kind unless --kind names one.

Flags

FlagDescriptionDefault
--confirmConfirms that the data is destroyed. Without it, the command deletes nothing and exits 2.false
--kind d1|r2|kvThe kind of the addon a display name selects. Required when two kinds hold the name; the error lists them.-
--workspace <id>The workspace, when you can open more than one. The error lists them.-
--jsonPrints the deleted id and its status as one JSON object.false
--production | --staging | --devThe environment to act in. Pass one at most. Without a flag, the command uses the environment that the last district login recorded, or production.-

Output

$ district addon delete orders-v2 --confirm
deleted orders-v2 (hadd_01K4X0J8W1QZ5B7N3G2R6TQ9YD): the data is destroyed

The API refuses an addon that a retained deployment binds. The message is followed by a table of the references the caller can see, up to 100, the count of references that belong to apps the caller cannot see, and a sentence when the table was cut. A deployment is retained until District removes its script, 14 days or more after the deployment stops serving. Remove the binding from the app's configuration, deploy to each target that bound it, and repeat after the earlier deployments are removed.

The API refuses an R2 bucket that still holds objects. The addon is ready again, and the message states the object count, with at least when the scan stopped at its bound. Bind the bucket to an app that empties it, then repeat. A count of 0 with a refusal can be an unfinished multipart upload, which the count does not see; abort it through the app.

Exit codes

Exit codeMeaning
0The deletion is claimed. deleted means the data is destroyed. deleting means the platform is still removing the resource and its data.
1The API refused the call with its own message: a deployment binds the addon, the bucket holds objects, the provider refused, or the caller cannot delete resources in the workspace.
2The command line lacks --confirm, names a malformed id, an addon nobody can read, a name no kind holds, a name two kinds hold without --kind, or the caller can open several workspaces and names none.
3The addon service did not answer. The message says what the call left behind.