-
-
Notifications
You must be signed in to change notification settings - Fork 0
Filesystem Safety
Filesystem Safety in Numan represents a set of strict architectural invariants and utility functions designed to ensure the integrity of the host system while managing Nushell packages. The core mission of this subsystem is to prevent accidental overwrites of foreign files, ensure atomic state transitions, and maintain strict boundary containment for all managed artifacts. By enforcing these rules, Numan ensures that its operations remain "inert" until explicit activation and that it never follows unsafe filesystem structures like symlinks that could lead to path traversal vulnerabilities.
The system is built upon three primary pillars: mutation serialization through advisory locking, ownership verification via specific file markers, and rigorous path validation during entry resolution. These mechanisms work together to protect both the Numan root directory ($NUMAN_ROOT) and the external Nushell environment, particularly the vendor autoload files.
Numan employs a MutationLock to serialize all state-mutating commands, such as install, activate, deactivate, update, and gc. This prevents race conditions where multiple Numan instances might attempt to modify the lockfile or managed files simultaneously.
The lock is an advisory exclusive OS lock maintained at $NUMAN_ROOT/state/mutation.lock. It is implemented as an RAII guard; the lock is held as long as the MutationLock object exists and is released automatically when the object is dropped.
flowchart TD
Start[Command Start] --> CheckLock{Acquire Lock?}
CheckLock -- "Success" --> Process[Execute Mutation]
CheckLock -- "Fail (Locked)" --> Error[Return 'Mutation in Progress']
Process --> DropGuard[Drop MutationLock]
DropGuard --> Released[Lock Released]
The diagram shows the lifecycle of a mutation operation, illustrating how the advisory lock prevents concurrent executions.
Numan manages external files, specifically the Nushell vendor autoload file numan.nu. To ensure Numan never destroys user data or files owned by other tools, it uses an ownership marker and strict verification logic.
Every file generated and managed by Numan must begin with a specific two-line UTF-8 prefix.
| Marker Component | Value |
|---|---|
| Line 1 | # Generated and managed by Numan. Do not edit. |
| Line 2 | # Numan autoload schema: 1 |
Before any operation that overwrites or deletes a managed file, Numan performs a multi-step safety check.
- Parent Directory Check: Asserts that the parent directory (e.g., the vendor-autoload dir) is a regular directory, not a symlink or reparse point.
- File Type Check: Verifies the target file itself is not a symlink.
-
Content Check: Reads the file to ensure it starts with the
OWNERSHIP_MARKER.
Numan enforces strict containment to ensure that package payloads and module entries do not escape the designated Numan root or their specific package directories.
Numan utilizes a lexical check to ensure paths are "safe relative." A safe relative path must meet the following criteria:
- Must not be absolute.
- Must not contain parent directory traversal components (
..). - Must not contain root components or platform-specific prefixes (e.g., Windows drive letters).
When resolving a module entry, Numan performs a sequence of canonicalization and containment checks.
flowchart TD
A[Input: payload_rel, entry_rel] --> B{Safe Relative?}
B -- No --> C[Bail: Unsafe Path]
B -- Yes --> D[Canonicalize Numan Root]
D --> E[Canonicalize Payload Path]
E --> F{Inside Root?}
F -- No --> G[Bail: Escape Violation]
F -- Yes --> H[Canonicalize Entry Path]
H --> I{Inside Payload?}
I -- No --> G
I -- Yes --> J{Is Regular File?}
J -- No --> K[Bail: Unsafe Type]
J -- Yes --> L[ResolvedEntry Created]
The resolve flow ensures that even with symlinks inside a package, the final canonical path remains strictly bounded.
To prevent state corruption during crashes or interruptions, Numan relies on atomic filesystem operations.
-
Atomic JSON Writes: The lockfile, journals, and state files use a "tempfile + persist" strategy (e.g.,
write_json_atomic). -
Candidate Validation: When updating the
numan.nuautoload file, Numan first writes a temporary candidate file (e.g.,.<uuid>.candidate.tmp) in the same directory. - Atomic Rename: Once the candidate is validated by a Nu process, it is moved over the live managed file using an OS-atomic rename, ensuring the file is either fully updated or unchanged.
The following table summarizes the non-negotiable security invariants enforced by the filesystem safety module:
| Invariant | Implementation Detail |
|---|---|
| No Symlink Following | Numan will not operate on or follow symlinks for module entries or managed file paths. |
| Target Triple Detection | Platform triples (OS/Arch) are derived from compile-time target_env, not runtime environment variables. |
| Immutable Payloads | Payloads are stored in content-addressed, versioned directories and never overwritten in place. |
| Inert Installation | The install command only writes to $NUMAN_ROOT and never touches the external system. |
Filesystem safety serves as the foundational layer that allows Numan to interact with the user's environment predictably and securely, ensuring that all package management actions are reversible and contained.
Numan wiki for tonythethompson/numan · v0.1.4 · MIT