Skip to content

Installation

One command writes a working project. The scaffolder asks up to four questions, installs dependencies, creates a local database and leaves you with a dev server and an admin waiting for its first passkey.

create-plumix-app assembles a project rather than copying a fixed template. It reads the runtime you pick and the plugins you tick, then splices their imports, their config slots and their registrations into one plumix.config.ts. Choosing the media or the OG plugin is what puts storage: r2({ binding: "MEDIA" }) in the config and an r2_buckets entry in wrangler.jsonc. Both declare a storage requirement, and it is fulfilled once however many of them you tick. Choosing neither leaves both out.

You need one of npm, pnpm, yarn or bun. No published Plumix package declares an engines range, so no Node version is enforced at install time. The repository is developed and tested on Node 24, which is what its .nvmrc pins.

The scaffolder does not work out which package manager invoked it. detectPackageManager takes the npm_config_user_agent string, but the binary calls it with nothing, so it always falls through to npm and the post-scaffold install runs npm install inside your new project. Pass --pm pnpm, --pm yarn or --pm bun to get the manager you actually use.

The target directory must not exist, or must be empty, and its parent must exist. Its last path segment becomes the project name, so it has to match npm’s package grammar: lowercase letters, digits, and -, _ or ., up to 214 characters. The name is spliced into package.json, into wrangler.jsonc and into the passkey relying-party name, so the scaffolder rejects an out-of-grammar name up front rather than emitting a project that will not build.

Run the scaffolder with whichever package manager you use:

Terminal window
pnpm create plumix-app my-site

Then work through the rest:

  1. Answer the wizard. It asks only for what the command line did not already supply, so the command above, which names the directory, gets three questions rather than four. Those are which runtime to target, which plugins to include, and which sign-in methods to add. Run pnpm create plumix-app with no directory and it asks where to create the project first. Node is the default runtime, running as a plain process against a local SQLite file; pass --runtime cloudflare for a Worker against D1 instead. Passkeys are always on, so the sign-in question is about what to layer beside them. The plugin question opens with SEO and Feeds ticked, so a new site serves head meta, robots.txt, a sitemap and feeds from the first request; nothing else is preselected. Untick either and the generated project has no import and no registration for it. One pairing is worth knowing before you do: og declares SEO as a peer and needs it registered to reach og:image, so unticking SEO while keeping OG installs the package without wiring it up.

  2. Let the post-scaffold steps run. The scaffolder installs dependencies, runs plumix migrate generate and plumix migrate apply --local, then initializes a git repository and makes one commit. Each step is skippable with a flag, and none of them throws, so a failure never leaves you without a project.

    The three do not report failure equally. A failed install and a failed database setup are both named in the output, and the printed next steps then carry the commands to finish by hand. The migration steps run the installed plumix binary, so they are skipped whenever the install did not succeed, with no message saying they were skipped. Git reports nothing either way. A failed git init, or a commit rejected for want of a configured git identity, prints no warning and no recovery step, and git init is skipped outright when the target directory is already inside a repository.

  3. Start the dev server. On the default Node runtime, the scaffolded config pins the passkey origin to port 3000:

    Terminal window
    cd my-site
    pnpm dev -- --port 3000

    Vite serves the site at http://localhost:3000, against a local SQLite file. On --runtime cloudflare, run plain pnpm dev instead — it serves at http://localhost:5173, against a local D1 database in .wrangler.

  4. Claim the admin account. Open http://localhost:3000/_plumix/admin (:5173 on Cloudflare). With no users in the database, the admin sends you to “Create admin account”. Enter an email and an optional name, submit, and the browser runs the WebAuthn ceremony. The account you create this way is an admin. The role is decided inside the insert statement itself, so two people submitting the form at the same moment cannot both be elected.

  5. Confirm the public site renders. Open http://localhost:3000 (:5173 on Cloudflare). The scaffolded theme greets you and links back to the admin.

Registration closes after the first account

Section titled “Registration closes after the first account”

The passkey registration endpoint counts the users table before it issues a challenge. Zero rows means bootstrap, and the request is allowed without a session. One row or more means registration is closed, and an unauthenticated request is refused with registration_closed. An already-signed-in user hitting the same endpoint is adding a device instead, and the email in the request has to match their own.

So the first person to reach a fresh deploy owns it. Deploy and claim the account in the same sitting, or gate the deploy until you have.

Every prompt has a flag, and --yes accepts the defaults for whatever you did not pass. That combination is what makes the scaffolder usable from a script or a CI job, where there is no terminal to prompt into. The wizard only runs on a real TTY and never under CI.

Terminal window
pnpm create plumix-app my-site --plugins blog,pages,media,seo,feeds --auth oauth --pm pnpm --yes

The flags are:

  • --runtime <id> picks the runtime, node or cloudflare. Defaults to node.
  • -p, --plugins <ids> takes a comma-separated list. The ids are blog, pages, menu, comments, media, audit-log, og, seo and feeds. Leave the flag off and you get seo and feeds. The flag replaces that pair rather than adding to it, so --plugins blog scaffolds blog alone, and --plugins= scaffolds none.
  • --auth <ids> adds sign-in methods to passkeys, as a comma-separated list: oauth for GitHub, magic-link, and cfAccess for Cloudflare Access on the cloudflare runtime. Leave it off and you get passkeys alone. An unknown id exits with the list of methods the runtime offers.
  • --pm <name> picks npm, pnpm, yarn or bun. Without it you get npm. An unrecognised name exits with an error rather than falling back.
  • --no-install skips the dependency install.
  • --no-db skips generating and applying the local migration.
  • --no-git skips git init and the first commit.
  • -y, --yes accepts defaults for everything not flagged.

Skipping a step is not skipping the work. A project whose migration never ran fails on the first request with a missing-table error, so run them yourself:

Terminal window
cd my-site
pnpm install
pnpm exec plumix migrate generate
pnpm exec plumix migrate apply --local

plumix migrate generate writes the resolved schema to .plumix/schema.ts and hands it to drizzle-kit, which emits SQL into drizzle/. The tables come from core plus whatever tables your plugins declare, so adding a plugin that carries its own tables means generating again. plumix migrate apply --local runs those files against the local D1 database.

The wizard’s sign-in question saves you an edit. It locks nothing in. Picking GitHub OAuth adds a github(...) provider to the auth block and writes GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET into .dev.vars. Picking magic links adds a magicLink block and sets mailer: consoleMailer(), which logs the sign-in link to the terminal instead of sending mail. Both are edits to plumix.config.ts you can make by hand afterwards.

Cloudflare Access appears in the same list when the runtime is Cloudflare. It replaces the request authenticator rather than adding a login form, so the identity provider in front of the worker decides who the user is.

Project Structure is the file-by-file account of what the scaffolder just wrote. Configuration is the reference for the config file it assembled. Passkeys covers the WebAuthn ceremony, the relying-party id and adding a second device. Secrets covers .dev.vars, EnvInput and production secrets. Plugins covers installing and configuring the nine official plugins.

Read Project Structure next, then model something. Content Modelling shows how a local plugin registers an entry type, a taxonomy and a meta box, and it is the page every later section assumes you have read. When the model is settled, Deploy Your Site takes the project to a live URL.