Skip to Content
Getting StartedInstallation

Installation

Start with the local-app lane: Next.js development, a local SQLite file, development/password sign-in, local content and the free personal workspace. This baseline needs no organization membership or Cloudflare, OAuth, SMTP, payment, analytics, CMS or DevReview account. Choose the core composition to stop new sales and organization enrollment. Demo/mock checkout is unavailable for real purchases and never grants paid access. Dependency installation still needs access to trusted npm packages.

LaneDefault URLPurpose
local-apphttp://localhost:3101Fast Next.js development and the canonical Playwright lane
local-workerhttp://localhost:8787Optional local Wrangler runtime checks
remote-cloudflareYour own preview/production domainExplicit remote deployment

Install your template copy

This is a commercial, closed-source developer template. Start from the source copy supplied to you; the public preview does not provide source-repository access. Use Node.js 24 LTS, the declared npm 10.9.2, Git, and Bash on macOS or Linux. Check node --version and npm --version before installing; Node’s bundled npm may differ from the declared version. From the root of your own source copy, install its locked dependencies:

npm ci

The default dependency graph does not require the private DevReview packages. Preserve lockfile integrity; a checksum failure is a failed install, not a reason to replace the expected checksum with a downloaded artifact’s checksum.

Before creating local environment files, you can verify the installation with the maintained baseline:

npm run test:baseline -- static npm run test:baseline -- adoption

This generates Velite content, runs lint and type checks, and builds Next.js plus the Pagefind search index. It uses synthetic configuration without a database or provider credentials. The runner requires Node 24 and refuses auto-loaded local environment files; use a separate checkout if you already have local configuration. A successful static lane does not verify login, database initialization, external providers, or deployment. Run npm run test:baseline -- --help for the separate test lanes.

The bounded adoption lane checks product selections, retained navigation and disabled operations, runtime/diagnostic environment agreement, safe diagnostic output, the reading-list SQLite example and Decision Log transitions/0038 upgrades on disposable libSQL and local D1. Local D1 requires workerd and loopback access. The lane includes invalid Usage limits and production-secret rules, uses synthetic inputs, and does not contact external providers or run the full application suite. The existing CI runs the same explicit test list. Authentication guide examples are typechecked by static; fresh Auth.js identity and revocation behavior remain in domain.

Configure local-app

Copy the public example after running any configuration-free baseline checks:

cp .env.local.example .env.local

Generate a local Auth.js secret without changing an env file automatically:

node -e "console.log(require('node:crypto').randomBytes(32).toString('base64'))"

Set that value in .env.local and keep these local settings:

APP_URL="http://localhost:3101" AUTH_SECRET="your-generated-local-secret" AUTH_TRUST_HOST="1" DATABASE_URL="file:./dev.db" PAYMENT_PROVIDER="mock" DEV_AUTH_EMAIL="member@example.test" DEV_AUTH_PASSWORD="dev-password" DEV_AUTH_NAME="Local Member"

Use .env.example as the optional-variable reference. Local database commands read only .env and .env.local, then apply shell overrides: default < .env < .env.local < shell. An explicit empty DATABASE_URL is invalid. The default when it is absent is file:./dev.db.

The resolved file must stay inside this project directory. Remote URLs, escaping paths, symlink aliases and any nonempty CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_D1_DATABASE_ID or CLOUDFLARE_D1_TOKEN cause rejection before database writes. Keep remote credentials in their own lane files; inspect the named input and its source when a conflict is reported. Local commands never silently remove conflicting credentials.

Inspect configuration without side effects

Before initializing the database, select your application composition in src/config/product.ts, then run the read-only adoption diagnostic from the project root. A core deployment uses ...productCompositions.core; the shipped live purchase selection reports blocked until its required configuration is supplied:

npm run adoption:check -- --lane local-app

It reads the chosen lane’s local configuration and the single product declaration. It does not create a database, inspect its schema, seed records, start the app, load provider SDKs, contact a service, or deploy. Existing database files remain unchanged. A missing database is still SCHEMA_UNVERIFIED, with initialization as the next step.

SituationCommandResult
Learn the interface without configurationnpm run adoption:check -- --helpHelp and exit 0; no project configuration import or read
Inspect local-app inputsnpm run adoption:check -- --lane local-appText checks with safe reason codes and key names
Inspect local-worker inputsnpm run adoption:check -- --lane local-workerLocal file checks; actual Worker bindings remain unverified
Inspect preview inputs as JSONTEMPLATE_ADOPTION_LANE=preview npm run --silent adoption:check -- --format jsonOne JSON document on stdout; no remote connection
Override an inherited lane for one runTEMPLATE_ADOPTION_LANE=preview npm run adoption:check -- --lane productionInspect the production lane’s local input file only

--lane accepts local-app, local-worker, preview or production and defaults to unset. --format accepts text or json, defaulting to text. Explicit CLI options override TEMPLATE_ADOPTION_LANE and TEMPLATE_ADOPTION_FORMAT in the shell, including invalid lower-priority values. These selectors are not loaded from env files. No arguments and no selector variables show help; running with a format but no lane, unknown options, positionals, or invalid/empty selector values returns exit 2. --help takes precedence over all other inputs.

LaneInput precedence, lowest to highest
local-appDomain defaults → .env → .env.local → shell
local-workerDomain defaults → .dev.vars.dev → shell
previewDomain defaults → .env.cloudflare.preview → shell
productionDomain defaults → .env.cloudflare → shell

Private feedback uploads require the Assets private-store policy, a separate private bucket and Images binding. Public R2/S3 credentials never establish that readiness. With attachments selected, a plain Node diagnostic reports missing private binding inputs; Worker lanes without request context remain unknown. The operator observation can describe present private binding conditions, but does not probe remote storage or establish permission. Disabling new uploads does not revoke access to historical attachments.

Absent lane files are optional when the shell supplies the required inputs; unreadable files produce TOOL_FAILURE. Adoption requires APP_URL and a nonempty AUTH_SECRET. The diagnostic and runtime share the pure src/env/contract.ts declarations, including Usage and the inspected NODE_ENV’s production-secret rule; diagnostics also apply guarded local-app target rules. Invalid optional settings remain errors even when their capability is disabled or SKIP_ENV_VALIDATION is set. Live checkout requires supported Stripe configuration; demo is currently unavailable and grants no paid capability. See Configuration for the selected composition and existing-customer obligations.

The non-local lanes inspect these files, not the effective environment of a running Worker. They do not read private Wrangler binding configuration or infer a remote database/bucket from credentials. A selected upload capability with unavailable Worker context remains unknown; public S3 inputs cannot make private uploads available. DevReview artifact trust, installed identity and adapter compatibility remain unverified; use its separate preflight only after choosing that integration.

Interpret checks and recover

ExitMeaning
0No known required configuration blocker; unverified checks can remain
1A required input, local target or selected capability is blocked
2Usage is invalid or the tool cannot complete inspection

Exit 0 is not installation, schema, login, payment, application or deployment success. A pass check covers only its named declaration/input. Capability state and observation are separate: disabled describes an unselected declaration, blocked a known missing/invalid input, unknown insufficient runtime context, and available sufficient configuration for that operation. All diagnostic observations are unverified; even Mock’s mode=demo does not prove payment or entitlement. These states describe selected configuration, not user permissions or a guarantee that every legacy consumer enforces a switch.

JSON contains schemaVersion: 1, lane, the public product revision, checks and safe operator capabilities. Automated consumers must reject unsupported schema versions. Values, credentials, provider URLs, absolute local paths and raw exceptions are omitted; failures include only controlled codes, key names and source categories. Use npm’s --silent option when piping JSON, because npm otherwise prints its own command banner. The CLI sends tool/usage diagnostics to stderr; valid inspections, including exit 1 results, leave stderr empty. Usage errors have no JSON result; a tool failure after valid selectors emits a versioned failure result.

nextStep and action are identifiers, never commands with interpolated secrets:

Next-step IDAction
noneNo change indicated for this check
configure-environment, inspect-input-filesCorrect the named input in the selected lane or its shell override; check file permissions without publishing values
review-local-targetResolve the named conflict using the local-app target rules above
verify-installationVerify the trusted lock and installation using the public-baseline steps
initialize-local-schemaFollow the guarded local database initialization steps below
verify-runtime-schema, verify-runtimeFollow the chosen lane’s explicit configuration/deployment and runtime verification steps
configure-capability, select-checkout-modeReconcile the explicit selection in src/config/product.ts with its domain inputs
verify-checkoutVerify the configured supported purchase flow and original-attempt recovery, then verify Stripe independently before selling
devreview-preflightFollow the optional DevReview prerequisites and integration:doctor -- --preflight below

Initialize the local database

npm run db:push npm run db:seed -- --admin-action none

Both commands print the same normalized local target. db:push applies the ordered drizzle/ migrations with the installed Drizzle migrator, including data backfills and custom constraints. It accepts an empty file or a matching Drizzle migration history. A database created by older schema-only push is refused: preserve it and rehearse a data transfer into a fresh migrated file instead of inventing migration history. Stop app processes before migrating and restart them afterward.

Seed creates the four missing demo plans (free, pro, pro-yearly, enterprise). Existing plan prices, text, active/deleted flags and provider IDs remain unchanged. Seed does not create schema.

npm run db:init runs migrations followed by seed with its default admin intent. If seed preflight fails, the earlier migrations may already be committed. --force and schema-only push are unsupported; both would bypass the complete migration contract.

Create an administrator explicitly

Choose an email different from the ordinary development account and add it to .env.local:

ADMIN_EMAIL="admin@example.test" ADMIN_PASSWORD="choose-a-local-admin-password"

Then run:

npm run db:seed -- --admin-action create

--admin-action accepts none or create and overrides TEMPLATE_ADMIN_ACTION. Its default is none. A legacy ADMIN_EMAIL with no explicit action fails with ADMIN_INTENT_REQUIRED before seed writes; use create deliberately or explicit none to ignore the admin inputs. There is no default admin password.

An existing active, non-deleted admin returns unchanged and PASSWORD_NOT_UPDATED. The password you just supplied is not installed or verified. Sign in using the account’s existing password. An ordinary, disabled or deleted account with the same email returns ADMIN_CONFLICT before plan writes. Seed never promotes users, reactivates accounts, resets passwords, or changes verification/profile fields. Use the account-management workflow for those actions.

Read results and recover

ResultMeaning and next action
succeeded / exit 0Every requested step completed or was already present; inspect created, unchanged and skipped steps.
failed / exit 1Preflight or the first operation failed. SCHEMA_REQUIRED means run the schema step for the same target; DB_BUSY is retryable after the other local writer finishes.
partial / exit 1Earlier steps completed but a later step failed. Those writes remain; fix the reported problem and rerun to fill missing records.
unknown / exit 1, or interrupted process with no final resultA write may have committed. Rerun against the same target so unique keys reconcile existing records; do not assume failure or reset the database.

Plan and admin uniqueness conflicts never overwrite the winning record. A competing ordinary/admin-disabled account can cause an admin conflict after plans completed, reported as partial. Local db:seed and HTTP bootstrap now share these create-only rules. HTTP uses the explicit operator command below; it never resets passwords or promotes an existing user.

Initialize an already running app through HTTP

After migrating the target database and starting the authorized application, configure APP_URL and its exact XADMIN_SECRET in the selected private helper environment. db:seed:d1:local reads .dev.vars.dev, :preview reads .env.cloudflare.preview, and db:seed:d1 reads .env.cloudflare; shell values override those files. The local-worker command requires a localhost HTTP(S) target.

# Explicit plans-only initialization: npm run db:seed:d1:local -- initialize --admin-action none # Explicit new administrator, using private ADMIN_EMAIL / ADMIN_PASSWORD inputs: npm run db:seed:d1:preview -- initialize --admin-action create

No command or --help shows help without reading environment files or calling the app. There is no default administrator password; HTTP creation accepts passwords of 8–128 characters. An existing active admin stays unchanged, including its original password. Other existing accounts conflict before any plan writes.

The helper prints safe step results: exit 0 means succeeded, exit 2 retains failed/partial/unknown, and exit 1 means configuration, authentication or invalid/lost transport. Initialization is sequential: earlier successful inserts remain if a later step fails. Inspect the steps before a deliberate later invocation; the helper never retries automatically. HTTP always disables caching.

Old direct clients must send { "adminAction": "none" } or { "adminAction": "create", "adminEmail": "...", "adminPassword": "..." }. Missing/malformed bodies, undeclared fields and invalid credentials are refused before writes. The old default-password and overwrite behaviors are removed.

Run and verify local-app

Generate local content, then start the app on the chosen port:

npm exec velite npm run dev:fast -- --port 3101

If you edit local blog content, rerun Velite or start npm exec velite -- --watch in another terminal. Keep APP_URL and the actual port aligned. If the port is occupied, choose an unused port and update both; do not stop an unrelated service.

  1. Open the home page  and /docs. Open /pricing when new purchases are selected; the core composition returns 404 there.
  2. Sign in on /login with member@example.test and the configured development password. This creates an ordinary user when absent, only in development. Verify /admin is denied.
  3. Sign out or use a new browser session. Sign in with the separate explicitly seeded admin and its existing password; verify /admin is accessible. A stale session does not prove the current stored role.
  4. As the ordinary user, open /workspace, create a bookmark and reload it. This verifies a real free resource without organization membership or payment configuration.
  5. With new purchases selected, open /pricing; core returns 404 for that route. Demo or unavailable checkout does not create a paid entitlement; a pricing fallback card alone does not prove database initialization. Real selling requires configured catalog offers, an enabled supported provider and independently verified confirmation.

The development credentials provider is not a production/test-mode login promise. Registration/password recovery with real email and real payments require their own configured integrations and verification.

Disposable browser checks

The default Playwright preparation recreates e2e.db, installs Chromium, pushes schema, explicitly creates dev@example.com as its admin fixture, and removes .next, playwright-report, and test-results. Run it when no other process owns those build/report directories:

npm run test:e2e

Preparation validates the target before any mkdir, removal, or browser installation. Only file:./e2e.db or a named .db file directly under .cache/e2e/ is accepted; dev.db, external files and symlink targets are rejected. Use a distinct email for ordinary-user tests. For an intentionally isolated database:

PW_E2E_DATABASE_URL="file:./.cache/e2e/adoption.db" npm run test:e2e

This file is disposable and will be deleted on preparation. Remote runtime smoke checks and PW_E2E_REUSE_SERVER=1 use different setup responsibilities; do not treat them as initialization proof.

Optional: DevReview

The public baseline keeps publicProductConfig.capabilities.devReview false and runs without private packages, keys, or an adapter. Opt-in is unavailable until you obtain trusted, independently verified, versioned artifacts. After verifying their provenance and checksums, install them into your own copy:

npm install --save-dev --save-exact /absolute/trusted/devreview-shared-VERSION.tgz /absolute/trusted/devreview-vite-plugin-VERSION.tgz

Replace the paths/version with the artifacts you reviewed, retain the generated SHA-512 lock entries, and use npm ci for subsequent installations. Do not reuse removed original-project download URLs, invent checksums, or use a downloaded checksum as proof that an unknown artifact is trusted.

Provide .devreview/next.cjs exporting composeDevReview(config, target) according to the DevReviewNextAdapter contract in scripts/devreview-next.ts. Implement it against your reviewed SDK’s actual API, honoring the supplied projectId, serverUrl and applyInProduction. This repository supplies no vendor adapter because no trusted private SDK is available in the baseline.

Set your own DEVREVIEW_PROJECT_ID and DEVREVIEW_SERVER_URL (an HTTPS origin, or loopback HTTP), then explicitly set publicProductConfig.capabilities.devReview to true in src/config/product.ts. Production instrumentation defaults to false; opt in with DEVREVIEW_APPLY_IN_PRODUCTION=true only when intended. DEVREVIEW_API_TOKEN is only for explicit integration CLI operations and is not passed to the build adapter.

CommandEffect
npm run integration:doctor -- --preflightCheck local target, pinned artifacts and adapter prerequisites.
npm run integration:packPreflight, build, then inspect manifests.
npm run integration:doctor -- --remoteAdditionally probe your bootstrap endpoint after local checks.
npm run integration:bootstrapFetch your chosen target’s bootstrap data.
npm run manifest:upload -- [MANIFEST] [VERSION]Publish the selected manifest to your chosen integration target.

These commands provide --help without reading project env and reject missing target/artifact prerequisites before network I/O. Baseline installation does not verify private SDK compatibility or remote connectivity. Any remote smoke test also needs an explicitly supplied PW_BASE_URL for your own app.

Optional: local-worker

cp .dev.vars.example .dev.vars.dev npm run cf:preview

Keep .dev.vars.dev on APP_URL=http://localhost:8787; keep .env.local on the local-app lane. Set the local-worker Auth.js/admin/cron/trigger secrets in its own file. Tracked custom-worker owns the Worker runtime and scheduled behavior. Follow the configuration guide for D1 bindings and the separate local-worker schema/seed workflow.

npm run test:e2e:worker checks an already running Worker on 8787; npm run test:e2e:worker:headed opens a visible browser. Neither command changes the default local-app lane into a Worker lane.

Optional: remote Cloudflare

cp wrangler.remote.example.toml wrangler.remote.toml cp .env.cloudflare.example .env.cloudflare cp .env.cloudflare.example .env.cloudflare.preview

Put your private account/routes/bindings in wrangler.remote.toml, remote helper secrets in .env.cloudflare*, and follow the deployment guide. Tracked wrangler.toml remains template-safe. Remote release keeps root drizzle.config.ts; db:migrate, db:studio, remote release and db:seed:d1* are not covered by the guarded local-app guarantee.

Troubleshooting

  • Target conflict: check the named D1 input in the shell, .env.local and .env; keep credentials in the intended lane. Error messages omit credential values.
  • Missing schema: compare the printed push/seed target and run npm run db:push for that same file. Do not delete an existing database to diagnose connection errors.
  • Existing admin: PASSWORD_NOT_UPDATED preserves the old password; supplying another password to seed does not reset it.
  • Dependency failure: resolve the reported trusted-package/integrity problem and rerun npm ci. Optional DevReview failure does not require enabling that integration for the baseline.
  • Usage: npm run db:push -- --help and npm run db:seed -- --help work without environment files, database connections, or provider credentials.