-
-
Notifications
You must be signed in to change notification settings - Fork 0
Immutable Payloads
Immutable Payloads represent the foundational storage and integrity strategy in Numan. The system ensures that every package installation—whether a plugin, module, script, or completion—is landed under a versioned, content-addressed path that is never overwritten in place. This approach guarantees reproducibility and safety across Nushell upgrades.
The design principles dictate that the installation process is entirely inert, writing only to the $NUMAN_ROOT and deferring any actual Nushell integration to a separate activation phase. By pinning payloads to unique, content-derived identifiers, Numan maintains a clear separation between different versions and origins of the same package.
Payloads are stored within the Numan root directory using a deterministic naming convention that includes the package type, owner, name, and a unique version-hash suffix. This structure ensures that updates do not replace existing files but instead land in a new, side-by-side directory until a garbage collection (GC) process removes unreferenced versions.
The standard path for an immutable payload follows this shape:
<root>/packages/<type>/<owner>/<name>/<version>-<sha8prefix>/
The following diagram illustrates the relationship between the Numan root and the immutable package storage.
flowchart TD
Root[$NUMAN_ROOT] --> Packages[packages/]
Packages --> Plugins[plugins/]
Packages --> Modules[modules/]
Plugins --> P_Owner[owner/]
P_Owner --> P_Name[name/]
P_Name --> P_V1[1.0.0-sha1/]
P_Name --> P_V2[1.1.0-sha2/]
Modules --> M_Owner[owner/]
M_Owner --> M_Name[name/]
M_Name --> M_V1[0.5.0-sha3/]
The diagram shows how multiple versions of the same package (e.g., v1.0.0 and v1.1.0) coexist as distinct directories under the same owner/name hierarchy.
Numan uses SHA-256 manifest hashing to verify the integrity of payloads. A unique revision_id is computed for every installed payload directory to ensure the contents remain unchanged after promotion to immutable storage.
The revision_id is a manifest hash of the installed payload directory. It is computed by:
- Walking every file in the payload recursively.
- Building a sorted list of entries formatted as
path:sha256:kind. - Calculating the SHA-256 hex digest of this manifest string.
The lockfile acts as the ground truth for which specific immutable payloads are active.
| Field | Type | Description |
|---|---|---|
payload_path |
String | Relative path from root to the immutable payload directory. |
revision_id |
Option | Manifest hash of the payload directory contents. |
payload_sha256 |
Option | SHA256 of the original downloaded payload archive. |
executable_sha256 |
Option | SHA256 of the specific plugin binary (plugin types only). |
origin |
Option | Origin of the install (e.g., registry:official, nupm_import). |
The installation transaction ensures that payloads are extracted and verified before being promoted to their final immutable path.
The following sequence diagram represents the flow of a package from download to immutable storage in the lockfile.
sequenceDiagram
participant T as Transaction
participant FS as File System
participant L as Lockfile
T->>FS: Download and Verify Archive
T->>FS: Extract to Temporary Staging
T->>T: Compute revision_id (Manifest Hash)
T->>FS: Promote Staging to Versioned Path
T->>L: Write Entry with payload_path and revision_id
Note over T,L: Payload is now Immutable
The following logic defines how the revision_id is derived to ensure cross-platform determinism by normalizing path separators.
// src/state/lockfile.rs:276-282
let rel_str = rel
.components()
.map(|c| c.as_os_str().to_string_lossy().into_owned())
.collect::<Vec<_>>()
.join("/");
let content = match std::fs::read(&path) {
Ok(b) => b,
Err(_) => continue,
};
let sha = compute_sha256(&content);Because payloads are immutable and never overwritten, operations like update or remove do not immediately delete files. Instead, they modify the lockfile's pointers.
- Update: Downloads the new version into a new side-by-side immutable directory.
- Remove: Deletes the reference from the lockfile. The payload remains on disk until a GC operation.
-
Garbage Collection (GC): Scans the
packages/directory and deletes any payload directories not referenced by the current lockfile or any active activation snapshots.
When importing packages from nupm, Numan maintains its immutability contract by creating a physical copy of the source files. Numan does not symlink to the nupm source; it creates a new immutable revision in the Numan root. This ensures that subsequent changes in the nupm source tree do not affect the Numan installation until an explicit re-import is performed.
Immutable payloads provide Numan with the ability to perform safe rollbacks and handle multiple Nushell versions simultaneously, as every package version exists in a unique, verified location on the filesystem.
Numan wiki for tonythethompson/numan · v0.1.4 · MIT