README (7881B)
1 # radroots_runtime_paths 2 3 `radroots_runtime_paths` provides canonical, typed runtime paths for Radroots 4 core libraries. Its public API is exposed only from the crate root. Pure path 5 resolution never reads the ambient process environment or performs filesystem 6 I/O. State-directory provisioning is a separate explicit operation. 7 8 ## Overview 9 10 - Validated service and instance identifiers define the sole canonical 11 `services/<service>/<instance>` namespace. 12 - Bounded service, instance, and credential artifact names are validated as 13 borrowed UTF-8 before the crate creates their retained strings; Serde identity 14 decoding uses the same boundary without a second prevalidation copy. 15 - Callers inject the platform, host-environment roots, profile, and typed 16 bootstrap provenance. 17 - A sealed `RuntimeContext` is the only public service-instance path 18 construction boundary. 19 - A sealed state-directory plan validates or provisions only the canonical 20 `services/<service>/<instance>` suffix under an existing state root. 21 - Service paths and common artifacts are non-forgeable immutable views. 22 - Pure shared geonames and runtime-store helpers remain independent of 23 service-instance path selection. 24 25 The former generic app/service/worker/shared namespace, raw root containers, 26 path overrides, process-environment selectors, and bootstrap path helpers are 27 intentionally removed. Callers must construct typed bootstrap provenance and 28 resolve a `RuntimeContext` from an explicitly injected resolver. 29 30 ## Example 31 32 The following deterministic Linux service-host example resolves one Myc 33 instance and its common artifacts without consulting or changing the host: 34 35 ```rust 36 use std::path::Path; 37 38 use radroots_runtime_paths::{ 39 InstanceId, RadrootsHostEnvironment, RadrootsPathProfile, 40 RadrootsPathResolver, RadrootsPlatform, RuntimeContext, 41 RuntimeContextBootstrap, RuntimeContextSource, ServiceId, 42 default_service_instance_artifacts, 43 }; 44 45 let resolver = RadrootsPathResolver::new( 46 RadrootsPlatform::Linux, 47 RadrootsHostEnvironment::default(), 48 ); 49 let bootstrap = RuntimeContextBootstrap::new( 50 RadrootsPathProfile::ServiceHost, 51 None, 52 RuntimeContextSource::SafeDefault, 53 RuntimeContextSource::BootstrapCli, 54 )?; 55 let context = RuntimeContext::resolve( 56 &resolver, 57 bootstrap, 58 ServiceId::new("myc")?, 59 InstanceId::new("primary")?, 60 )?; 61 let artifacts = default_service_instance_artifacts(context.paths()); 62 63 assert_eq!( 64 context.paths().config(), 65 Path::new("/etc/radroots/services/myc/primary"), 66 ); 67 assert_eq!( 68 artifacts.config(), 69 Path::new("/etc/radroots/services/myc/primary/config.toml"), 70 ); 71 # Ok::<(), Box<dyn std::error::Error>>(()) 72 ``` 73 74 ## Root Profiles 75 76 Every root below receives the suffix `services/<service>/<instance>`. The 77 service instance's `state` directory is derived from the selected data root. 78 79 | Profile | Config root | Data/state root | Cache root | Logs root | Run root | Secrets root | 80 | --- | --- | --- | --- | --- | --- | --- | 81 | Linux `ServiceHost` | `/etc/radroots` | `/var/lib/radroots` | `/var/cache/radroots` | `/var/log/radroots` | `/run/radroots` | `/etc/radroots/secrets` | 82 | Linux `InteractiveUser` | `$XDG_CONFIG_HOME/radroots` | `$XDG_DATA_HOME/radroots` | `$XDG_CACHE_HOME/radroots` | `$XDG_STATE_HOME/radroots/logs` | `$XDG_RUNTIME_DIR/radroots` | `$XDG_CONFIG_HOME/radroots/secrets` | 83 | macOS `InteractiveUser` | `$HOME/Library/Application Support/Radroots/config` | `$HOME/Library/Application Support/Radroots/data` | `$HOME/Library/Caches/Radroots` | `$HOME/Library/Logs/Radroots` | `$HOME/Library/Application Support/Radroots/run` | `$HOME/Library/Application Support/Radroots/secrets` | 84 | Windows `InteractiveUser` | `%APPDATA%\Radroots\config` | `%LOCALAPPDATA%\Radroots\data` | `%LOCALAPPDATA%\Radroots\cache` | `%LOCALAPPDATA%\Radroots\logs` | `%LOCALAPPDATA%\Radroots\run` | `%APPDATA%\Radroots\secrets` | 85 | `RepoLocal` | `<base>/config` | `<base>/data` | `<base>/cache` | `<base>/logs` | `<base>/run` | `<base>/secrets` | 86 87 Linux XDG inputs must be absolute. Empty, missing, or relative config, data, 88 state, and cache values use the corresponding absolute `HOME` defaults: 89 `.config`, `.local/share`, `.local/state`, and `.cache`. An absolute 90 `XDG_RUNTIME_DIR` is mandatory and has no fallback. Windows interactive 91 resolution requires both injected `APPDATA` and `LOCALAPPDATA`. Repo-local 92 resolution requires one explicit absolute, non-root base with no parent 93 traversal. Repo-local never becomes an implicit or production fallback. 94 95 ## State Directory Provisioning 96 97 `RuntimeContext::state_directory_plan` is pure and performs no filesystem I/O. 98 Its sealed result exposes no caller-selected path or mutable component. Calling 99 `RuntimeStateDirectoryPlan::provision` is an explicit filesystem operation. 100 101 For `InteractiveUser` and `RepoLocal`, the canonical data/state root must 102 already exist, be owned by the effective user, be owner-readable and 103 owner-searchable, and not be group- or other-writable. The provisioner may then 104 create only `services/<service>/<instance>`, one descriptor-relative component 105 at a time, with mode `0700`. It uses no-follow opens, validates every existing 106 directory, never changes an existing mode, and identity-checks any cleanup of 107 directories it created during a failed attempt. An entry whose identity cannot 108 be proven is preserved and the operation fails closed. 109 110 For `ServiceHost`, the entire state-directory suffix must already exist. The 111 plan performs validation only and never creates or permission-repairs service- 112 host directories. Provisioning is implemented only on Linux and macOS; other 113 targets fail with a stable path-free unsupported-platform classification. 114 115 ## Common Artifacts 116 117 | Artifact | Canonical location within the instance | 118 | --- | --- | 119 | Configuration | `<config>/config.toml` | 120 | SQLite state | `<state>/state.sqlite` | 121 | SQLite writer lock | `<state>/state.lock` | 122 | Local admin socket | `<run>/admin.sock` | 123 | Credential artifact | `<secrets>/<validated-credential-name>` | 124 125 The crate derives these paths. Its explicit state-directory provisioner owns 126 only the narrow behavior described above; it does not create any common 127 artifact, configuration, cache, log, run, or secrets path. Callers own artifact 128 creation and its security policy. The shared geonames and runtime-store helpers 129 are pure joins over caller-supplied roots and do not select or alter a 130 `RuntimeContext`. 131 132 ## Support Caveats 133 134 Deterministic path behavior is not a qualification claim. Linux service-host 135 on x86_64 and aarch64 is eligible for Tier 1 only after all release gates pass. 136 Linux and macOS interactive and explicit repo-local profiles on x86_64 and 137 aarch64 are developer-target behavior. Linux rootless OCI on x86_64 and 138 aarch64 is also only a target: it uses the Linux behavior with explicit mounts, 139 has no separate native path profile, and requires its own qualification gates. 140 macOS support does not imply launchd packaging, system-wide paths, or Linux 141 peer-credential equivalence. Windows interactive and repo-local derivation is 142 implemented but unsupported and carries no v1 support claim. 143 144 Non-Linux `ServiceHost`, `MobileNative`, Android/iOS/Other interactive, 145 mobile/browser/WASI service use, and every unlisted profile/platform pairing 146 are unsupported. Successful compilation or path derivation does not change a 147 target's qualification posture. 148 149 ## Public API Baseline 150 151 The final reviewed root-only API is frozen in the 152 [runtime-paths API baseline](../../contracts/api_baselines/radroots_runtime_paths.txt) 153 and checked by the package boundary tests. 154 155 ## Copyright 156 157 Except as otherwise noted, all files in the `radroots_runtime_paths` 158 distribution are 159 160 Copyright (c) 2025 Tyson Lupul 161 162 For information on usage and redistribution, and for a DISCLAIMER OF ALL 163 WARRANTIES, see LICENSE included in the `radroots_runtime_paths` distribution.