CLI Reference
All valet-dev commands, flags, and environment variables.
# terminal
bunx valet-dev codegen --watchThe valet-dev CLI handles project initialization, code generation, deployment, and environment variable management. All commands run locally and communicate with the server for server-side operations.
If you are using Vite, prefer valetPlugin() for day-to-day development. It wraps the same codegen flow and optional local server startup into your vite dev command. The CLI remains the lower-level interface for non-Vite apps, CI, and deploy workflows.
valet-dev init
# terminal
bunx valet-dev initScaffolds a valet/ directory in the current project with a starter schema.ts, a built-in email/password auth.ts, and an example function file. Appends _generated/ to .gitignore if present.
By default, init registers a new project on the managed platform (with an auto-generated name like brave-falcon-leaps) and writes the resulting VALET_PROJECT_URL and VALET_DEPLOY_KEY to a .env file in the project root. Subsequent commands like valet-dev codegen run with no flags.
If you already have a running valet-server (self-hosted or provisioned separately), use --project-url to skip registration and connect directly:
# terminal
bunx valet-dev init --project-url https://my-server.example.com --deploy-key abc123For Expo projects (detected by the presence of expo in package.json dependencies), init also writes EXPO_PUBLIC_VALET_PROJECT_URL to .env. Expo requires the EXPO_PUBLIC_ prefix for environment variables to be accessible in your app.
Flags
| Flag | Default | Description |
|---|---|---|
--dir <name> | valet | Name of the valet directory to create. |
--server <url> | https://fly.valet.host | Orchestrator URL to register a new project with. |
--project-url <url> | -- | Use an existing valet-server URL directly (skips registration). |
--deploy-key <key> | -- | Deploy key for the project (used with --project-url). |
--help, -h | -- | Show help. |
valet-dev codegen
# terminal
bunx valet-dev codegen --watchGenerates typed client code from your schema and functions, and pushes them to the server.
For Vite projects, this is usually what valetPlugin() runs for you automatically in dev and build. Use the CLI directly when you are outside Vite or want manual control over the codegen step.
Codegen reads valet/schema.ts, all function files, and valet/cron.ts if present, then produces typed hooks, API references, handler registries, and a generated cron.json manifest in the output directory. If a server URL and deploy key are available, it also pushes the schema, functions, and cron manifest to the server.
Flags
| Flag | Default | Description |
|---|---|---|
--valet-dir <path> | ./valet | Path to your valet directory. |
--output-dir <path> | ./_generated/valet | Where to write generated files. |
--project-url <url> | $VALET_PROJECT_URL | Project URL to push schema and functions to. |
--deploy-key <key> | $VALET_DEPLOY_KEY | Deploy key for server push. |
--watch, -w | -- | Watch for changes and regenerate. |
--help, -h | -- | Show help. |
Custom directories
# terminal
bunx valet-dev codegen --valet-dir src/valet --output-dir src/_generated/valetvalet-dev deploy
# terminal
bunx valet-dev deployBuilds and pushes schema and functions to a production project.
If valet/cron.ts exists, deploy also stages and activates the generated cron manifest.
For self-hosted valet-server instances, use --project-url to deploy directly:
# terminal
bunx valet-dev deploy --project-url https://my-prod.example.com --deploy-key abc123On the managed platform, deploy auto-registers a production project on first run and saves credentials to .env.production. Subsequent runs push to the existing production project.
Flags
| Flag | Default | Description |
|---|---|---|
--valet-dir <path> | ./valet | Path to your valet directory. |
--output-dir <path> | ./_generated/valet | Where to write generated files. |
--project-url <url> | -- | Deploy to this project URL directly (skips registration). |
--deploy-key <key> | -- | Deploy key for the project (used with --project-url). |
--help, -h | -- | Show help. |
CI usage
// package.json
{
"scripts": {
"prebuild": "bunx valet-dev deploy"
}
}In CI environments (Vercel, EAS, etc.), set VALET_PROJECT_URL and VALET_DEPLOY_KEY as environment variables instead of relying on .env.production.
Credential priority
Deploy resolves credentials in this order:
- Environment variables (
VALET_PROJECT_URL,VALET_DEPLOY_KEY) .env.production.env
valet-dev cron
# terminal
bunx valet-dev cron run daily-digestManually triggers a loaded cron job against the current server. This is primarily for development and debugging. The run goes through the same server action executor as a real scheduled tick, but the action sees ctx.invoker.source === 'admin'.
Subcommands
| Subcommand | Description |
|---|---|
cron run <job-name> | Trigger a single cron job immediately. |
Flags
| Flag | Default | Description |
|---|---|---|
--project-url <url> | $VALET_PROJECT_URL | Project URL for the target server. |
--deploy-key <key> | $VALET_DEPLOY_KEY | Deploy key for the admin request. |
--scheduled-at <ms> | Date.now() | Override the scheduled timestamp passed to the action. |
--help, -h | -- | Show help. |
valet-dev branch
# terminal
bunx valet-dev branch status
bunx valet-dev branch fetch allow api.staging.example.comInspects and updates branch safety gates for a project that is already a branch. Gate updates require a deploy key and are rejected on non-branch projects.
Subcommands
| Subcommand | Description |
|---|---|
branch status | Print branch lineage and current safety gates. |
branch cron pause | Keep branch cron schedules disabled. |
branch cron resume | Allow branch cron schedules to run. |
branch fetch allow <host-or-url> | Add a host to the branch fetch allowlist. |
branch outbox sandbox | Route branch email/push outbox delivery to safe sinks. |
branch outbox live | Allow branch outbox delivery to live destinations. |
branch secrets deny | Deny production-secret reads in the branch. |
branch secrets allow-production | Allow production-secret reads in the branch. Use only for explicit operator-approved cases. |
Flags
| Flag | Default | Description |
|---|---|---|
--project-url <url> | $VALET_PROJECT_URL | Project URL. |
--deploy-key <key> | $VALET_DEPLOY_KEY | Deploy key for authenticated requests. |
--project <id> | $VALET_PROJECT_ID | Project ID for scoped branch metadata. |
--help, -h | -- | Show help. |
valet-dev env
# terminal
bunx valet-dev env set GITHUB_CLIENT_ID=abc123
bunx valet-dev env set GITHUB_CLIENT_SECRET secret456
bunx valet-dev env list
bunx valet-dev env unset GITHUB_CLIENT_SECRETManages server-side environment variables (e.g., OAuth client secrets, API keys). These variables are available to your server-side query and mutation handlers.
Subcommands
| Subcommand | Description |
|---|---|
env set KEY=VALUE | Set an environment variable (single-argument form). |
env set KEY VALUE | Set an environment variable (two-argument form). |
env unset KEY | Remove an environment variable. |
env list | List all environment variables for the project. |
Flags
| Flag | Default | Description |
|---|---|---|
--project-url <url> | $VALET_PROJECT_URL | Project URL. |
--deploy-key <key> | $VALET_DEPLOY_KEY | Deploy key for authenticated requests. |
--project <id> | $VALET_PROJECT_ID | Project ID for scoped secrets. |
--help, -h | -- | Show help. |
valet-dev auth
# terminal
bunx valet-dev auth initAdds authentication to an existing Valet project.
Subcommands
auth init
Adds authTables to your schema.ts import and spreads it into defineSchema(). Creates a valet/auth.ts file with a starter defineAuth configuration.
Without --provider, the generated auth.ts uses built-in email/password auth with providers: [].
If your schema already includes authTables, the import is skipped. If auth.ts already exists, it is left untouched.
Flags
| Flag | Default | Description |
|---|---|---|
--dir <name> | valet | Name of the valet directory. |
--provider <list> | [] | Comma-separated OAuth providers to configure. Omit it for built-in email/password only. |
--external | -- | Skip authTables and auth.ts creation (for Firebase, Clerk, or other external JWT providers). |
--help, -h | -- | Show help. |
External auth providers
If you use Firebase, Clerk, Auth0, or another external auth provider, pass --external to skip authTables injection and auth.ts creation. ctx.auth is still populated from the validated JWT — no Valet-specific auth tables are needed.
# terminal
bunx valet-dev auth init --externalEnvironment variables
| Variable | Description |
|---|---|
VALET_PROJECT_URL | Full project URL for codegen server push. Overridden by --project-url. |
VALET_DEPLOY_KEY | Deploy key for authenticated pushes. Overridden by --deploy-key. |
If a .env file exists in the project root with VALET_PROJECT_URL and VALET_DEPLOY_KEY, all commands use them as defaults. After valet-dev init, you can run valet-dev codegen with no flags.
Generated files
After running codegen, _generated/valet/ contains:
Always generated
| File | Description |
|---|---|
api.js | Runtime API object with function references. |
api.d.ts | TypeScript types for the API and data model. |
schema.json | Serialized schema for the server. |
react.js | Pre-configured React provider and hooks. |
react.d.ts | React type declarations. |
Generated when functions are defined
| File | Description | Condition |
|---|---|---|
handlers.js | Handler registry for local query and mutation execution. | When local queries or mutations exist. |
handlers.d.ts | Handler type declarations. | When local queries or mutations exist. |
functions.json | Serialized handler code for the server. | When any queries or mutations are defined. |
Watch mode
# terminal
bunx valet-dev codegen --watchIn development, run with --watch to regenerate on file changes. The watcher:
- Runs initial code generation.
- Pushes schema and functions to the server.
- Watches
valet/for.tsfile changes. - Debounces rapid changes (100ms).
- Regenerates and re-pushes on each change.
Press Ctrl+C to stop.
Server push
Codegen pushes two payloads to the server:
/api/functions-- handler code for server-side execution./api/schema-- table definitions, indexes, and sync configurations.
The server responds with migration status:
Pushed schema (3 tables) to server
+ Created table: comments
+ Added column todos.priority
~ Backfilled todos.priority (150 rows)If there are breaking changes (type changes on existing columns), the push fails:
ERROR: Schema push failed — breaking changes detected:
x Type change on todos.completed: number -> boolean
Hint: Add new columns with the desired types and migrate data.Deprecated: valet-codegen
The standalone valet-codegen binary is deprecated. Use valet-dev codegen instead -- it accepts the same flags. The old binary prints a deprecation warning and continues to work.
Troubleshooting
"schema.ts not found"
Error: schema.ts not found in ./valetYour valet directory does not contain a schema.ts file. Verify the --valet-dir flag points to the correct directory, and that the directory contains a schema.ts file that exports a schema via defineSchema().
# terminal
ls valet/schema.ts"Could not reach server"
Error: Could not reach server at https://fly.valet.hostThe server URL is unreachable. Codegen still generates local files; only the server push fails. Check that the URL is correct and that the server is running.
# terminal
curl -s -o /dev/null -w "%{http_code}" https://fly.valet.host/health"401 Unauthorized"
Error: 401 UnauthorizedThe deploy key is missing or invalid. Set the --deploy-key flag or the VALET_DEPLOY_KEY environment variable.
# terminal
bunx valet-dev codegen --deploy-key your-deploy-key-here"409 Conflict"
Error: 409 Conflict — breaking schema changes detectedYou attempted a breaking schema change (e.g., changing a column's type from v.number() to v.boolean()). Breaking changes are not supported directly. Add a new column with the desired type and migrate data instead.
// valet/schema.ts
import { defineSchema, defineTable, v } from 'valet-dev/server'
// Instead of changing completed from number to boolean,
// add a new column and backfill it:
export default defineSchema({
todos: defineTable({
title: v.string(),
completed: v.number().deprecated(),
isCompleted: v.boolean(),
})
.backfill('isCompleted', (doc) => doc.completed === 1),
})