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
34 changes: 34 additions & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,40 @@ stance`"* — this tool is Rust-primary now, with SPARK/Ada hooks planned
for the correctness-critical `+integrity.rs+` path (called via Zig FFI
per the hyperpolymath ABI/FFI standard).

=== Where a launcher keeps its state

A generated launcher writes two files: a pid file and a log. Unless the
config says otherwise (`+[runtime]+` `+pid-file+` / `+log-file+`) they land
under the invoking user's own XDG directories, never in shared,
world-writable space:

[cols="1,2,1",options="header"]
|===
|File |Default |Overridden by

|pid
|`+$XDG_RUNTIME_DIR+`, else `+$XDG_STATE_HOME+`, else
`+~/.local/state+` — as `+<app>-server.pid+`
|`+[runtime]+` `+pid-file+`

|log
|`+$XDG_STATE_HOME+`, else `+~/.local/state+` — as
`+<app>-server.log+`
|`+[runtime]+` `+log-file+`
|===

The log goes to the *state* directory rather than the runtime directory
because it has to survive a logout, which `+$XDG_RUNTIME_DIR+` does not
promise. Both directories are created `+0700+` by the launcher before the
first write.

Before 2026-09-25 both defaults were `+/tmp/<app>-server.{pid,log}+`:
world-writable, and predictable from nothing but the app name, so any local
user could create or symlink the path before the launcher's first run and
influence what it later killed or removed (issue #48). Launchers minted
before that date keep their old paths until they are re-minted — set the
two keys explicitly, or re-mint, to move them.

=== Why a declarative format for inputs

* A launcher carries a metadata block in its own header, so a generated
Expand Down
22 changes: 22 additions & 0 deletions crates/launcher-common/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,30 @@ pub struct Runtime {
#[serde(default)]
pub command: Vec<String>,

/// Where the generated launcher writes its pid file.
///
/// Default (when unset): `+$XDG_RUNTIME_DIR+`, falling back to
/// `+$XDG_STATE_HOME+` and then to `+~/.local/state+`, as
/// `+<app>-server.pid+`. Before 2026-09-25 the default was
/// `+/tmp/<app>-server.pid+` — world-writable and predicted entirely by
/// the app name, so any local user could create or symlink the path
/// before the launcher's first run and steer what it later killed or
/// removed (#48). The default is emitted into the script as a SHELL
/// expression, not resolved here, because the launcher runs on the
/// user's machine rather than the one it was minted on; the script
/// creates the directory `+0700+` before it writes.
///
/// Set it to override, e.g. `+pid-file = "/var/run/myapp.pid"+`. A
/// leading `+~+` is expanded (see `+integration::expand_home+`).
#[serde(default)]
pub pid_file: Option<String>,
/// Where the generated launcher writes its log.
///
/// Default (when unset): `+$XDG_STATE_HOME+`, falling back to
/// `+~/.local/state+`, as `+<app>-server.log+`. The state directory
/// rather than the runtime directory because a log has to survive a
/// logout, which `+$XDG_RUNTIME_DIR+` does not promise. Same history and
/// the same override mechanism as [`Runtime::pid_file`].
#[serde(default)]
pub log_file: Option<String>,

Expand Down
Loading
Loading