REST API CLI Tool
A command-line interface for managing very_simple_rest API deployments.
Overview
This CLI tool simplifies the setup and management of very_simple_rest API applications, with a focus on secure user management and configuration.
Installation
From Source
# Clone the repository
# Build the CLI tool
# Run the CLI
The workspace default member is the CLI package, so cargo build and cargo run from the
repository root target vsr. Use cargo build --workspace when you want the full workspace, or
cargo build -p very_simple_rest for the library package.
Cargo Install
For an unpublished local checkout:
Commands
Init
Scaffold a starter project from the bundled template:
The starter template now uses local Turso by default and wires in the shared runtime security helpers for request limits, CORS, trusted proxies, auth rate limits, and response headers.
Setup
Initialize a new API deployment with interactive prompts:
This command will:
- Check your database connection
- Create necessary tables if they don't exist
- Help you create an admin user
- Generate a
.envtemplate file
For non-interactive setup (e.g., in CI/CD pipelines):
If you run vsr from a directory containing exactly one .eon service, the CLI auto-discovers
it for setup, create-admin, check-db, and gen-env. You can still pass --config
explicitly when you want a non-default file:
When --config points to a .eon service and --database-url / DATABASE_URL are both absent,
the CLI uses the service’s compiled default database URL. For SQLite services, that now defaults
to encrypted database.engine = TursoLocal, which resolves to the matching SQLite-compatible file
URL and the default TURSO_ENCRYPTION_KEY env var.
Env Generation
Generate a .env file directly:
When --config points to a .eon service, the generated file mirrors the compiled default
database URL plus the required/default Turso and security env vars such as
TURSO_ENCRYPTION_KEY, CORS_ORIGINS, TRUSTED_PROXIES, and the configured logging filter env
var when those are referenced by the service.
Server Generation
Generate a runnable Rust server project from a bare .eon service:
This emits:
Cargo.tomlsrc/main.rs- the copied
.eonfile .env.exampleopenapi.jsonmigrations/0000_auth.sqlwith built-in auth enabled by defaultmigrations/0001_service.sql
You can also build a server binary directly:
Useful options:
--without-authexcludes the built-in auth/account routes and omitsmigrations/0000_auth.sql--package-nameoverrides the generated Cargo package name--build-dirkeeps the temporary Cargo project in a known location--keep-build-dirpreserves the generated build project after compilation--output distwrites the binary into an existing directory; otherwise it defaults to the current directory and names the binary after the.eonfile stem
Built-in auth is enabled by default. If the .eon service already defines a user table, re-run
with --without-auth because the built-in auth migration owns that table name.
vsr build also exports the generated runtime artifacts next to the binary in
<binary>.bundle/, including .env.example, openapi.json, the copied .eon file, README.md,
migrations/, and relative TLS certificate files when they exist at build time.
The generated server fails fast if built-in auth is enabled and JWT_SECRET is missing. vsr gen-env and emitted .env.example files still help by generating or surfacing the required env
vars, but runtime auth is no longer allowed to fall back to a random secret.
Generated server projects serve the OpenAPI document at /openapi.json and Swagger UI at /docs.
When a .eon service defines static mounts, vsr server emit also copies those directories into
the generated project and wires the generated server to serve them. When a .eon service defines
security, the emitted server also applies the compiled JSON body limit, CORS policy,
trusted-proxy handling, auth rate limits, security headers, and built-in auth token settings
automatically. When a .eon service defines database.engine, the emitted server also carries
that runtime engine config into the project, including encrypted local Turso bootstrap by default
for bare SQLite .eon services. When a .eon service defines logging, the emitted server also
uses the compiled log env var, default filter, and timestamp precision instead of hard-coded
logger defaults. When a .eon service defines tls, the emitted server binds HTTPS with Rustls
and HTTP/2, defaults BIND_ADDR to 127.0.0.1:8443, and can use vsr tls self-signed to
generate local certificate PEM files.
TLS Certificate Generation
Generate a self-signed certificate and private key for local development:
Behavior:
- with
--config api.eon, the command uses the configured.eontls.cert_pathandtls.key_path - with no config and no explicit output paths, it defaults to
certs/dev-cert.pemandcerts/dev-key.pemin the current directory - default SANs are
localhost,127.0.0.1, and::1 - private keys are written with restrictive permissions on Unix
OpenAPI Generation
Render an OpenAPI document from either a .eon service or derive-based Rust resources:
Useful options:
--titleoverrides the document title--versionoverrides the OpenAPI version string ininfo.version--server-urlchanges the generated server URL, which defaults to/api- built-in
/auth/register,/auth/login, and/auth/meroutes are included by default, with/auth/megrouped underAccountin Swagger --without-authremoves those built-in auth/account routes from the document--exclude-tableremoves specific tables from the document
Static Files In .eon
Bare .eon services can define service-level static mounts:
static: {
mounts: [
{
mount: "/assets"
dir: "public/assets"
mode: Directory
cache: Immutable
}
{
mount: "/"
dir: "public"
mode: Spa
index_file: "index.html"
fallback_file: "index.html"
cache: NoStore
}
]
}
Supported options:
mount: URL prefix such as/assetsor/dir: directory relative to the.eonfilemode:DirectoryorSpaindex_file: optional directory index filefallback_file: SPA fallback filecache:NoStore,Revalidate, orImmutable
The loader rejects mounts that escape the .eon root or conflict with reserved routes such as
/api, /auth, /docs, and /openapi.json.
Database Engine In .eon
Bare .eon services can also define a service-level database engine. For SQLite services, the
default when this block is omitted is:
database: {
engine: {
kind: TursoLocal
path: "var/data/<module>.db"
encryption_key_env: "TURSO_ENCRYPTION_KEY"
}
}
You can still override it explicitly:
database: {
engine: {
kind: TursoLocal
path: "var/data/app.db"
encryption_key_env: "TURSO_ENCRYPTION_KEY"
}
}
Current support:
Sqlx: the legacy runtime path; use this explicitly if you want plain SQLx SQLite for a SQLite.eonserviceTursoLocal: bootstraps a local Turso database file and uses the project runtime database adapter with SQLite-compatible SQLTursoLocal.encryption_key_env: reads a hex key from the named environment variable and uses Turso local encryption with the current default cipher (aegis256) during bootstrap
Current limitation:
- This is still a project-local runtime adapter, not a true upstream SQLx
Anydriver.
Security In .eon
Bare .eon services can also define service-level security defaults:
security: {
requests: { json_max_bytes: 1048576 }
cors: {
origins: ["http://localhost:3000"]
origins_env: "CORS_ORIGINS"
allow_credentials: true
allow_methods: ["GET", "POST", "OPTIONS"]
allow_headers: ["authorization", "content-type"]
}
trusted_proxies: {
proxies: ["127.0.0.1", "::1"]
proxies_env: "TRUSTED_PROXIES"
}
rate_limits: {
login: { requests: 10, window_seconds: 60 }
register: { requests: 5, window_seconds: 300 }
}
headers: {
frame_options: Deny
content_type_options: true
referrer_policy: StrictOriginWhenCrossOrigin
}
auth: {
issuer: "very_simple_rest"
audience: "public-api"
access_token_ttl_seconds: 3600
}
}
This config currently controls:
- JSON body size limits for generated resource and built-in auth routes
- CORS origins, headers, methods, credentials, and preflight caching on the emitted server
- trusted-proxy IP handling for forwarded client addresses
- in-memory built-in auth login and registration rate limits
- security response headers on the emitted server
- built-in auth JWT
iss,aud, and token TTL defaults
Secrets such as JWT_SECRET remain environment-driven. The current auth rate limiter is
process-local rather than distributed.
Create Admin
Create a new admin user:
# Interactive mode with prompts
# Non-interactive mode with parameters
If the built-in auth user table has extra numeric claim columns such as tenant_id,
org_id, or claim_workspace_id, the CLI detects them automatically. Interactive admin creation
prompts for those values, and non-interactive flows accept environment variables named
ADMIN_<COLUMN_NAME>, for example ADMIN_TENANT_ID=1.
Check Database
Verify database connection and schema:
This will:
- Test the database connection
- Check if required tables exist
- Count existing users and admins
- Provide recommendations based on findings
Generate .env Template
Create a template .env file with common configuration options:
Environment Variables
The CLI tool respects the following environment variables:
| Variable | Description | Default |
|---|---|---|
DATABASE_URL |
Database connection string | Derived from --config or a single local .eon; otherwise sqlite:var/data/app.db?mode=rwc |
ADMIN_EMAIL |
Default admin email address | None |
ADMIN_PASSWORD |
Default admin password | None |
ADMIN_<COLUMN_NAME> |
Optional built-in auth claim column value, for example ADMIN_TENANT_ID |
None |
JWT_SECRET |
Secret key for JWT tokens | Required for built-in auth at runtime |
Examples
Complete Setup Example
# Set database URL
# Initialize the application
# Check database status
.eon-Driven Local Turso Example
# Use the compiled database settings from a bare .eon service explicitly
Creating Admin in CI/CD Pipeline
# Set required variables
# Create admin non-interactively
Security Best Practices
- Never store admin credentials in version control
- Use environment variables or a secure secret management system
- Change default admin passwords immediately in production
- Use strong, unique passwords
- Consider setting up a dedicated admin user for each team member
Troubleshooting
Common Issues
Database Connection Errors
- Verify the database URL format
- Ensure the database server is running
- Check file permissions for SQLite databases
Admin Creation Fails
- Ensure both email and password are provided
- Verify the database is accessible and writable
- Check if an admin with the same email already exists