vel Commands

> Complete reference for the per-project `vel` CLI - serve, build, migrations, queues, code generation, maintenance, and keys.

vel is the per-project binary. It’s created in your project root when you scaffold an app with velocity new. Run ./vel <command> - or alias vel to ./vel in your shell - from the project directory.

For the installer CLI (velocity new, velocity self-update, etc.), see Installer Commands.

Server

vel serve

Start the development server with live reload.

vel serve [flags]
FlagShortDefaultDescription
--port-p4000HTTP port
--env-edevelopmentEnvironment name (sets APP_ENV)
--no-watchoffDisable file-watching / auto-rebuild
--tags(none)Build tags passed to go build
vel serve
vel serve --port 3000
vel serve --env staging --no-watch
vel serve --tags="integration"

On start:

  1. Vite dev server launches on :5173 (full-stack projects only).
  2. The Go app compiles to .vel/tmp/server.
  3. .go files are watched; a change rebuilds and restarts.

vel build

Compile a production binary.

vel build [flags]
FlagShortDefaultDescription
--output-oproject dir nameOutput path (.exe appended when --os windows)
--os(host)Target GOOS
--arch(host)Target GOARCH
--tags(none)Go build tags
vel build
vel build --output ./bin/myapp
vel build --os linux --arch amd64

Database

vel migrate

Run all pending migrations.

vel migrate [--pretend]

--pretend prints the SQL that would run without executing it - useful for reviewing migration output before committing.

vel migrate:fresh

Drop all tables, then run every migration from scratch.

vel migrate:fresh [--force]

In a production-class environment this command refuses to run unless --force (-f) is passed. The guard treats production, prod, staging, and any unrecognised APP_ENV value as production, so a typo’d APP_ENV cannot disable it.

Destructive - deletes all data. Development / testing only.

vel migrate:rollback

Roll back the most recent batch of migrations.

vel migrate:rollback [--step N] [--force]
FlagShortDefaultDescription
--step-s1Number of batches to roll back (must be >= 1)
--force-foffBypass the production-environment guard

Like migrate:fresh, this is gated in production-class environments; pass --force to proceed.

vel migrate:status

Show which migrations have run.

vel migrate:status

vel db:wipe

Drop every table in the current database without running migrations.

vel db:wipe [--force]

In a production-class environment this command refuses to run unless --force (-f) is passed (same guard as migrate:fresh).

Destructive. Outside production there is no confirmation prompt. Use only when you know the database is disposable.

Queue and Scheduler

vel queue:work

Start a worker that processes queued jobs.

vel queue:work [--queue NAME] [--tries N] [--timeout S]
FlagShortDefaultDescription
--queue-qdefaultQueue to consume from
--tries(driver)Max attempts per job before marking failed
--timeout(driver)Per-job timeout in seconds
vel queue:work
vel queue:work --queue emails --tries 3 --timeout 60

vel schedule:work

Run the scheduler loop - picks up scheduled jobs defined via v.Schedule(...) and dispatches them when due.

vel schedule:work

Typically run under a process supervisor (systemd, Docker, etc.) rather than manually.

Cache

vel cache:clear

Flush the configured cache store.

vel cache:clear

Maintenance Mode

vel down

Put the app into maintenance mode. All requests return 503 except those bearing the bypass secret.

vel down [--secret TOKEN] [--retry N]
FlagDefaultDescription
--secret(none)Bypass token. Send it in the X-Maintenance-Bypass header (or visit /<secret>) to mint a signed velocity_maintenance_bypass cookie that exempts the browser
--retry(none)Value for the Retry-After response header (seconds)
vel down --secret "abc123" --retry 60

vel up

Exit maintenance mode.

vel up

Keys

vel key:generate

Generate a fresh 32-byte encryption key and write it to .env under APP_KEY, base64-encoded with a base64: prefix. If .env does not exist it’s created; an existing APP_KEY= line is replaced in place. The .env file is written with owner-only (0600) permissions.

vel key:generate

Routes

vel route:list

Print every registered route with method, path, and name.

vel route:list

Rebuilds the bootstrap lifecycle internally before printing - the output always reflects the current v.Routes(...) definition.

Code Generation

All make:* commands scaffold a file into the conventional location for that type. Names are converted to the right case for the artifact (snake_case for migration files, PascalCase for types).

Every make:* command accepts a --dir <path> flag to override the default output directory (the artifact’s conventional folder). The value is validated and must stay inside the project tree.

vel make:handler

vel make:handler <name> [--resource] [--api] [--dir PATH]
FlagShortDefaultDescription
--resource-roffScaffold CRUD methods (Index/Show/Store/Update/Destroy)
--apioffJSON responses instead of view rendering
--dirinternal/handlersOutput root override

Output: internal/handlers/<name>.go. Namespaced names like Admin/Dashboard nest under the output root.

vel make:handler User
vel make:handler Post --resource
vel make:handler Admin/Dashboard
vel make:handler Product --api --resource
vel make:handler User --dir internal/web/handlers

vel make:model

vel make:model <name> [--uuid] [--soft-deletes] [--migration] [--dir PATH]
FlagShortDefaultDescription
--uuidoffUse UUID primary key
--soft-deletesoffAdd deleted_at column and scope
--migration-moffAlso scaffold the migration
--dirinternal/modelsOutput directory override

Output: internal/models/<name>.go.

vel make:migration

vel make:migration <name> [--create TABLE] [--table TABLE] [--uuid] [--soft-deletes] [--dir PATH]
FlagAcceptsDescription
--create=VALUE or spaceGenerate a “create” migration for the given table
--table=VALUE or spaceGenerate an “alter” migration for the given table
--uuidflagUse UUID primary key in the create template
--soft-deletesflagInclude deleted_at in the create template
--dir=VALUE or spaceOutput directory override (default database/migrations)

Table names passed to --create / --table must match [A-Za-z_][A-Za-z0-9_]*. Output: database/migrations/<timestamp>_<name>.go.

vel make:migration create_posts --create=posts
vel make:migration add_slug_to_posts --table=posts

Other make commands

All take a name argument and scaffold a file into the conventional directory. Each also accepts --dir <path> to override that directory.

CommandOutput path
vel make:middlewareinternal/middleware/<name>.go
vel make:eventinternal/events/<name>.go
vel make:listenerinternal/listeners/<name>.go
vel make:jobinternal/jobs/<name>.go
vel make:mailinternal/mail/<name>.go
vel make:notificationinternal/notifications/<name>.go
vel make:resourceinternal/resources/<name>.go
vel make:policyinternal/policies/<name>.go
vel make:providerinternal/providers/<name>.go
vel make:commandinternal/commands/<name>.go
vel make:middleware RateLimit
vel make:listener SendWelcomeEmail
vel make:policy PostPolicy

vel make:grpc:service

vel make:grpc:service <Name> [flags]

Scaffolds a gRPC service end-to-end in one call:

  • api/proto/<leaf>/v1/<name>.proto - empty service block
  • api/proto/buf.yaml + api/proto/buf.gen.yaml (first run only)
  • internal/grpc/services/<name>.go - <Name>Service impl with a New<Name>Service() constructor and the <alias>.Unimplemented<Name>ServiceServer embed
  • internal/providers/grpc_provider.go - created on first call (unless --no-provider), then injected at // vel:grpc:imports and // vel:grpc:services markers on every subsequent call. The provider wires the service via velgrpc.NewServer(...) and Register<Name>ServiceServer(...).
FlagDefaultDescription
--packagederived from <Name>Directory leaf under api/proto/ and api/gen/go/
--proto-package<leaf>.v1Full wire package, e.g. velship.admin.v1
--dirinternal/grpc/servicesGo impl output directory
--alias<leaf>pbImport alias for the generated proto package
--proto-namelower-cased <Name> baseProto file base name (no extension)
--impl-namesnake_case <Name> baseGo impl file base name (no extension)
--no-provideroffSkip provider scaffolding / wiring (proto + impl only)

Name normalisation: vel make:grpc:service Foo, FooService, foo, and fooService all produce the Go type FooService with proto package foo.v1 and default import alias foopb. The proto file uses option go_package = "<module>/api/gen/go/<leaf>/v1;<leaf>v1" derived from the host project’s go.mod (so the generated package itself is named <leaf>v1, referenced through the foopb alias).

buf.yaml / buf.gen.yaml are written before the proto file, so a config-write failure leaves no partial scaffold on disk. The generated GRPCProvider does not hard-code WithReflection(true); the framework default (toggled by GRPC_REFLECTION env) reaches the file so the generated app boots cleanly in production.

If internal/providers/grpc_provider.go already exists without the marker comments (legacy hand-written provider), the command prints a manual wire snippet instead of mutating user code.

vel make:grpc:service Foo
vel make:grpc:service ChatService
vel make:grpc:service TemplateControl --package admin \
  --proto-package velship.admin.v1 --dir internal/shared/grpc/services --no-provider

After scaffolding, register providers.GRPCProvider{} in internal/app/bootstrap.go (printed as a hint on first run).

vel make:grpc:rpc

vel make:grpc:rpc <Service> <RPC> [--stream | --client-stream | --bidi]

Appends a new rpc to an existing service’s .proto and a matching method stub on the Go impl. The service must already exist; run vel make:grpc:service <Name> first.

FlagAliasesRPC shape produced
(none)Unary: rpc X(XRequest) returns (XResponse)
--stream--server-streamServer-streaming: returns (stream XResponse)
--client-streamClient-streaming: (stream XRequest) returns (X)
--bidi--bidirectionalBidi: (stream XRequest) returns (stream X)

Only one streaming flag may be set per invocation; combining them errors out.

The proto scanner walks the file with brace counting that respects // line comments, /* block */ comments, and "..." string literals at every position (header keyword, between keyword and name, between name and {, and inside the body). That means rpc-with-options blocks (grpc-gateway HTTP annotations) and commented-out draft headers do not corrupt insertion.

On the Go side, the generated method signature matches the RPC shape (for service Foo, impl type FooService, default proto alias foopb):

ShapeSignature
Unaryfunc (s *FooService) X(ctx context.Context, req *foopb.XRequest) (*foopb.XResponse, error)
Server streamfunc (s *FooService) X(req *foopb.XRequest, stream foopb.FooService_XServer) error
Client streamfunc (s *FooService) X(stream foopb.FooService_XServer) error
Bidifunc (s *FooService) X(stream foopb.FooService_XServer) error

context is added to the impl’s imports for unary only; streaming variants pull ctx from stream.Context() and do not need the import.

Idempotent: re-running with the same <Service> <RPC> pair detects the existing rpc and skips.

vel make:grpc:rpc Foo Hello
vel make:grpc:rpc Foo Tail --stream
vel make:grpc:rpc Foo Upload --client-stream
vel make:grpc:rpc Foo Chat --bidi

vel make:grpc:gen

vel make:grpc:gen

Runs buf generate inside api/proto. Streams buf’s stdout and stderr to your terminal so plugin errors are visible in real time. Fails with a clear message when:

  • api/proto/ does not exist (run make:grpc:service first)
  • buf is not on PATH (links to install docs)
  • buf generate exits non-zero
vel make:grpc:gen
# cd api/proto && buf generate
# Generated Go code in api/gen/go/

Help

vel help
vel --help
vel -h

Prints a grouped list of every command.