D038 - Portable mode specification
decided · 2026-08-19
Triggers, OR'd: ORBISTOUN_PORTABLE_MODE
env var · binary filename stem contains portable
(case-insensitive) · a .portable directory
beside the binary.
Root: ./.portable/ beside the binary.
Strict containment - nothing outside that directory, traces
included. An exception is what makes "does not touch outside
its own directory" false, and traces are a dev tool, so the size concern
that motivated a separate knob does not apply.
Otherwise: directories::ProjectDirs →
%APPDATA%\orbistoun\. Portable is opt-in; the plain binary
is not portable.
The sentinel is a directory, never a file. Another
project of mine hit os error 183 on first run because the
sentinel and the data root are the same path - a .portable
file was written, then create_dir_all failed over
it. The directory existing beside the binary is itself the sentinel
(.exists() passes for a directory), so a portable install
is sticky with no sidecar. GUI onboarding materialises it with an
explanatory PORTABLE.txt inside and heals a stale file from
an older scheme.
Sentinel is checked beside the binary, not in the
working directory: PWD-based means cd into a folder
containing .portable silently relocates the data root. The
per-project workflow that would give is better served by an explicit
--data-dir flag.
Containment is a test, not a convention - run in
portable mode inside a temp directory, exercise the writing paths,
assert nothing was created outside the root. That is what stops someone
adding a std::fs call in six months.
Governs the GUI as well as the CLI, with a [Portable]
title badge. Portable mode governs where orbistoun writes,
never where it looks for content: test material is always an explicit
argument. Lands as an orbistoun-paths crate, before the
first thing that writes.