You need: a scaffolded project.
One command
pithy worker is the command, project structure is what it writes into, one project or two is the decision above it, and shared databases is what two Workers with the same binding name get.
pithy worker add adminIt scaffolds apps/admin/ with its wrangler.jsonc (named <project>-admin), its pithy.worker.jsonc, its tsconfig.json and its package.json — then generates every discovered Worker’s .dev.vars, takes a dev port, and installs the workspace.
At a terminal it also asks where the Worker answers, per environment, against your account’s real zones, and writes the answer as a domains block.
All-or-nothing. Anything that fails after the directory is made rolls it back, so the same command works on the retry.
apps/ is the registry
There is no hand-maintained list. pithy dev, pithy deploy, pithy migrate and the rest enumerate apps/* and read each Worker’s config.
Add, remove or rename a Worker and the dev set follows automatically.
pithy worker list what exists, which autostart, which port each holds
pithy worker remove deletes the directory and releases its portremove deletes a directory, not a database. Your data is untouched.
Renaming moves three strings at once
A Worker’s name is three things that have to agree: the directory, the deployed script name, and vars.WORKER — which is what tells two Workers’ audit events apart when they share a database.
pithy worker rename admin consoleOnly rename reaches Cloudflare, and only to ask a question: which scripts are live under the old name. It never writes there.
sync is the safe one to run any time
pithy worker syncWrites what the Worker’s config declares into its wrangler.jsonc. It touches no account and runs no deploy — safe on any branch, at any time, as often as you like. Running it twice changes nothing and writes nothing.
Two halves:
The address. A domains block names where the Worker answers per environment, and sync writes the route and the BASE_URL it implies.
This is the only non-interactive way to get that route written. Before it, a domains block added by hand declared an address nothing served — doctor reported it healthy, deploy shipped it, and the Worker answered on nothing.
The Workflows and crons your app capability declares, for every environment.
Which Worker composes what
Each Worker has its own pithy.config.ts and composes its own capabilities. An admin Worker that needs no payments does not compose payments.
Two Workers that declare the same binding share one database — which is why worker is stamped on every audit row, and the only thing that tells their events apart.
The administrative surface goes on one
The control-plane seam is composed on one Worker per project, and a connection targets that one. In a project with several, pithy dashboard connect refuses the ambiguity and asks which.
Before you add one
**Read **one project, or two? — a second Worker inside one project and a second project are different decisions, and the second is the expensive one to get wrong.