vsra 0.1.1

The vsr command-line interface for very_simple_rest
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
# 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

```bash
# Clone the repository
git clone https://github.com/MatiasHiltunen/very_simple_rest.git
cd very_simple_rest

# Build the CLI tool
cargo build --release

# Run the CLI
./target/release/vsr --help
```

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

```bash
cargo install vsra
```

For an unpublished local checkout:

```bash
cargo install --path crates/rest_api_cli
```

## Commands

### Init

Scaffold a starter project from the bundled template:

```bash
vsr init my-api
```

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:

```bash
vsr setup
```

This command will:
1. Check your database connection
2. Create necessary tables if they don't exist
3. Help you create an admin user
4. Generate a `.env` template file

For non-interactive setup (e.g., in CI/CD pipelines):

```bash
vsr setup --non-interactive
```

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:

```bash
vsr setup
vsr create-admin --email admin@example.com --password change-me
vsr check-db

vsr --config api.eon setup
vsr --config api.eon create-admin --email admin@example.com --password change-me
vsr --config api.eon check-db
```

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:

```bash
vsr gen-env
vsr --config api.eon gen-env --path .env.local
```

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:

```bash
vsr server emit --input api.eon --output-dir generated-api
```

This emits:

- `Cargo.toml`
- `src/main.rs`
- the copied `.eon` file
- `.env.example`
- `openapi.json`
- `migrations/0000_auth.sql` with built-in auth enabled by default
- `migrations/0001_service.sql`

You can also build a server binary directly:

```bash
vsr build api.eon --release
```

Useful options:

- `--without-auth` excludes the built-in auth/account routes and omits `migrations/0000_auth.sql`
- `--package-name` overrides the generated Cargo package name
- `--build-dir` keeps the temporary Cargo project in a known location
- `--keep-build-dir` preserves the generated build project after compilation
- `--output dist` writes the binary into an existing directory; otherwise it defaults to the
  current directory and names the binary after the `.eon` file 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. When
`runtime.compression.static_precompressed = true`, `vsr build` also generates `.br` and `.gz`
companion files for copied static assets inside that bundle.

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:

```bash
vsr tls self-signed
vsr --config api.eon tls self-signed --force
vsr tls self-signed --cert-path certs/dev-cert.pem --key-path certs/dev-key.pem --host localhost --host 127.0.0.1
```

Behavior:

- with `--config api.eon`, the command uses the configured `.eon` `tls.cert_path` and
  `tls.key_path`
- with no config and no explicit output paths, it defaults to `certs/dev-cert.pem` and
  `certs/dev-key.pem` in 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:

```bash
vsr openapi --input api.eon --output openapi.json
vsr openapi --input src --exclude-table user --output openapi.json
vsr openapi --input api.eon --without-auth --output openapi-no-auth.json
```

Useful options:

- `--title` overrides the document title
- `--version` overrides the OpenAPI version string in `info.version`
- `--server-url` changes the generated server URL, which defaults to `/api`
- built-in `/auth/register`, `/auth/login`, and `/auth/me` routes are included by default, with
  `/auth/me` grouped under `Account` in Swagger
- `--without-auth` removes those built-in auth/account routes from the document
- `--exclude-table` removes specific tables from the document

### `.eon` Reference Docs

Generate a Markdown reference for the currently supported `.eon` feature set:

```bash
vsr docs --output docs/eon-reference.md
```

The checked-in reference document lives at `docs/eon-reference.md`.

### Static Files In `.eon`

Bare `.eon` services can define service-level static mounts:

```eon
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 `/assets` or `/`
- `dir`: directory relative to the `.eon` file
- `mode`: `Directory` or `Spa`
- `index_file`: optional directory index file
- `fallback_file`: SPA fallback file
- `cache`: `NoStore`, `Revalidate`, or `Immutable`

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:

```eon
database: {
    engine: {
        kind: TursoLocal
        path: "var/data/<module>.db"
        encryption_key_env: "TURSO_ENCRYPTION_KEY"
    }
}
```

You can still override it explicitly:

```eon
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 `.eon` service
- `TursoLocal`: bootstraps a local Turso database file and uses the project runtime database
  adapter with SQLite-compatible SQL
- `TursoLocal.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 `Any` driver.

### Security In `.eon`

Bare `.eon` services can also define service-level security defaults:

```eon
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.

### Runtime In `.eon`

Bare `.eon` services can also define runtime defaults:

```eon
runtime: {
    compression: {
        enabled: true
        static_precompressed: true
    }
}
```

Generated modules expose this through `module::runtime()`. The parsed runtime options are:

- `compression.enabled`: emitted servers now apply dynamic HTTP response compression from this flag
- `compression.static_precompressed`: generated static mounts now serve `.br` and `.gz` companion
  files when present and add `Vary: Accept-Encoding`

`vsr build` now generates those companion files into `<binary>.bundle/` when this flag is enabled.
`vsr server emit` still copies the source static directories as-is.

### Create Admin

Create a new admin user:

```bash
# Interactive mode with prompts
vsr create-admin

# Non-interactive mode with parameters
vsr create-admin --email admin@example.com --password secure_password
```

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:

```bash
vsr check-db
```

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:

```bash
vsr gen-env
```

## 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

```bash
# Set database URL
export DATABASE_URL="sqlite:var/data/my_app.db?mode=rwc"
export JWT_SECRET="replace-me"

# Initialize the application
vsr setup

# Check database status
vsr check-db
```

### `.eon`-Driven Local Turso Example

```bash
# Use the compiled database settings from a bare .eon service explicitly
vsr --config tests/fixtures/turso_local_api.eon check-db
vsr --config tests/fixtures/turso_local_api.eon migrate apply --dir migrations
```

### Creating Admin in CI/CD Pipeline

```bash
# Set required variables
export DATABASE_URL="sqlite:var/data/app.db?mode=rwc"
export ADMIN_EMAIL="admin@example.com"
export ADMIN_PASSWORD="secure_random_password"
export ADMIN_TENANT_ID="1"
export JWT_SECRET="replace-me"

# Create admin non-interactively
vsr create-admin --email $ADMIN_EMAIL --password $ADMIN_PASSWORD
```

## 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

## Related Documentation

- [Main Project README]../../README.md
- [API Documentation]../../docs/api.md
- [Environment Configuration]../../docs/configuration.md