Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .changeset/dev-fresh-claim-scope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
'@objectstack/cli': patch
---

`os dev --fresh` states the isolation it actually delivers (#5594)

The `--fresh` block promised its tempdir "owns ALL persistent state for this
run". After #4968 that is true of everything the CLI itself places — the dev
SQLite DB (`OS_HOME` → `<home>/data/dev.db`, published as `OS_DATABASE_URL`),
the uploads root (published on the settings service's own name
`OS_STORAGE_LOCAL_ROOT`), and any plugin state keyed off `OS_HOME` — but it was
never true of state an **app** reaches by a relative path it declares itself.
Such a path is resolved by its own consumer against the process working
directory, which `--fresh` does not move, so the file lands in the project tree
and is still there after the run exits.

The live specimen is deliberate authoring, not a bug: the showcase's
`showcase-external` datasource declares
`filename: '.objectstack/data/showcase_external.db'` and documents that the path
resolves against the project cwd — so a `--fresh` showcase run leaves that file
(plus `-wal`/`-shm`) behind.

No behaviour changed. The `--fresh` flag help, the source comments, and the
`os dev` flag table in the CLI docs now name the covered surface
(`OS_HOME`-keyed state plus the env channels the CLI publishes) and state
plainly what falls outside it, with a docs note on declaring an absolute path
when a datasource should follow `--fresh`.

Re-anchoring app-declared relative paths on `OS_HOME` is a behaviour change
resting on an open contract question ("relative to cwd" vs "relative to this
run's home") and is deliberately not taken here.
17 changes: 16 additions & 1 deletion content/docs/deployment/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,29 @@ os dev --database file:./data/test.db --auth-secret $(openssl rand -hex 32)
| `-p, --port <n>` | `OS_PORT` / `PORT` | Listen port (default `3000`). In dev a busy port auto-hops to the next free one; the banner shows the actual port. |
| `--ui` | — | Force Console UI on (already on by default in dev) |
| `--compile` | — | Force compiling `objectstack.config.ts` → `dist/objectstack.json` before starting (auto when the artifact is missing; ignored with `--artifact`) |
| `--fresh` | — | Ephemeral `OS_HOME` in the OS tempdir (clean DB, uploads, storage), auto-deleted on exit; implies `--seed-admin` |
| `--fresh` | — | Ephemeral `OS_HOME` in the OS tempdir (clean DB, uploads root, and other `OS_HOME`-keyed state), auto-deleted on exit; implies `--seed-admin`. See the scope note below |
| `--seed-admin` / `--no-seed-admin` | — | Seed a dev admin (`admin@objectos.ai` / `admin123`) on an empty DB — default on; override with `--admin-email` / `--admin-password` |
| `-v, --verbose` | — | Verbose output |

By default `os dev` keeps your data between restarts in a project-local SQLite
file at `.objectstack/data/dev.db` (created on first run). Pass `--database`,
set `OS_DATABASE_URL`, or use `--fresh` for a throwaway run.

<Callout type="info" title="What `--fresh` covers">
`--fresh` isolates the state **the CLI places for the run**: everything keyed
off the ephemeral `OS_HOME` (the dev SQLite DB, the uploads root, plugin state
under `OS_HOME`) plus the env channels `os dev` publishes for it —
`OS_DATABASE_URL` and `OS_STORAGE_LOCAL_ROOT`. That tempdir is deleted on exit.

It does **not** relocate state your app reaches by a **relative path it
declares itself** — for example a datasource with
`config: { filename: '.objectstack/data/my.db' }`. Such a path is resolved by
its own consumer against the process working directory, which `--fresh` does
not change, so the file is written into your project tree and is still there
after the run ends. Declare an absolute path (or one derived from `OS_HOME`)
when you want a datasource to follow `--fresh`.
</Callout>

With a file-backed SQLite database, dev also provisions a sibling
`<db>.telemetry.<ext>` file registered as the `telemetry` datasource —
lifecycle-classed system data (activity streams, job runs, notifications,
Expand Down
36 changes: 29 additions & 7 deletions packages/cli/src/commands/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,18 @@ export default class Dev extends Command {
}),

// ── Ephemeral / fresh-environment helpers ────────────────────────
// `--fresh` creates an isolated tempdir for OS_HOME / DB / uploads
// so every run starts from a clean slate. Combine with `--seed-admin`
// `--fresh` creates an isolated tempdir for OS_HOME / DB / uploads, so
// every run starts from a clean slate for the OS_HOME-keyed state the
// CLI itself places (see the block in `run()` for the exact covered
// surface, and for what an app-declared relative path escapes — #5594).
// Combine with `--seed-admin`
// (default-on when --fresh) to also provision a logged-in admin
// account, so backend debugging never blocks on first-run wizards.
// The seeded admin uses FIXED, well-known credentials by default
// (admin@objectos.ai / admin123) so tooling never has to guess them —
// override with --admin-email / --admin-password when needed.
fresh: Flags.boolean({
description: 'Start with an ephemeral OS_HOME under the OS tempdir (clean DB, uploads, storage); auto-deletes on exit. Implies --seed-admin (admin@objectos.ai / admin123) unless --no-seed-admin is given.',
description: 'Start with an ephemeral OS_HOME under the OS tempdir clean DB, uploads root and other OS_HOME-keyed state for this run, auto-deleted on exit. State an app reaches by its own cwd-relative path (e.g. a datasource `filename: .objectstack/...`) is NOT covered and survives exit. Implies --seed-admin (admin@objectos.ai / admin123) unless --no-seed-admin is given.',
default: false,
}),
'seed-admin': Flags.boolean({
Expand Down Expand Up @@ -180,10 +183,29 @@ export default class Dev extends Command {
const environmentId = flags['environment-id'] ?? process.env.OS_ENVIRONMENT_ID ?? 'env_local';

// ── --fresh: ephemeral OS_HOME under the OS tempdir ─────────────
// Creates a unique scratch dir that owns ALL persistent state for
// this run: the SQLite DB (via OS_HOME → <home>/data/...), the
// storage-service uploads root (OS_STORAGE_LOCAL_ROOT), and any other
// state plugins keyed off OS_HOME. Auto-deleted on exit.
// Creates a unique scratch dir that owns the state this command can
// actually place, and nothing more. What it covers, exactly:
// - the dev SQLite DB the CLI resolves for the run
// (OS_HOME → <home>/data/dev.db, published as OS_DATABASE_URL),
// - the storage-service uploads root, published on the settings
// service's own env name OS_STORAGE_LOCAL_ROOT (#4968),
// - any other state a plugin keys off OS_HOME.
// In one sentence: framework-owned, OS_HOME-keyed state plus the env
// channels this block publishes below. Auto-deleted on exit.
//
// NOT covered — state reached by an app-declared RELATIVE path (#5594).
// Such a path is resolved by its own consumer against the process cwd,
// which `--fresh` does not move, so it is written into the project tree
// and survives exit. The live specimen is deliberate authoring, not a
// bug: examples/app-showcase's `showcase-external` datasource declares
// `filename: '.objectstack/data/showcase_external.db'` and says in its
// own comment that the path resolves against the project cwd — so a
// `--fresh` run of the showcase leaves that file (+ -wal/-shm) behind.
// Re-anchoring app-declared relative paths on OS_HOME would be a
// behaviour change resting on an open contract question ("relative to
// cwd" vs "relative to this run's home"); it is deliberately NOT taken
// here, and this comment states the covered surface instead of an
// unqualified promise a reader would rely on for isolation.
//
// The uploads root MUST be published under the name the settings
// service derives for it — `envKeyOf('storage','local_root')` (#4968).
Expand Down
Loading