sigildocs

(sigil store)

Exports

storeprocedure

Construct a store struct.

store?procedure

Test if a value is a store struct.

store-dirprocedure

Get the dir field of a store struct.

store-dbprocedure

Get the db field of a store struct.

Return the default store path.

Checks SIGIL_STORE env var, then $XDG_CACHE_HOME/sigil/store, then ~/.cache/sigil/store.

(default-store-path)
; => "/home/user/.cache/sigil/store"
open-storeprocedure

Open or create a content-addressable store at the given path.

The store directory and SQLite database are created if they don't exist. Multiple processes can share a store safely (SQLite handles locking).

(define s (open-store (default-store-path)))
close-storeprocedure

Close a store, releasing the database connection.

with-storeprocedure

Call proc with an open store, closing it after.

(with-store (default-store-path)
  (lambda (s)
    (store-add-file s "test.txt" "/path/to/test.txt")))
store-pathprocedure

Return the filesystem path of the store directory.

Serialize publication of one content-addressed path across processes. The critical section is intentionally tiny: hash calculation and source acquisition happen before it; only the final copy + DB row are guarded. This makes a cold same-content miss idempotent instead of allowing two publishers to race a reader at the file/row boundary.

Add a file to the store by content hash.

Returns the store path. If an item with the same content hash and name already exists, returns the existing path without copying.

(store-add-file s "sigil-core.sgb" "/path/to/core.sgb")
; => "ab3f7c89...-sigil-core.sgb"

Return the content store path for SOURCE-PATH when that exact item is already present and verifies, otherwise #f. This is the read-only half of store-add-file, exposed for layered stores whose lower tiers cannot admit new content.

Add a directory tree to the store by content hash.

The directory is serialized deterministically (sorted entries, no metadata) before hashing. The tree is copied into the store.

Add content from a string or bytevector.

store-valid?procedure

Cheap structural check: the item has a DB row and its on-disk content exists with a matching recorded size. This is a fast pre-gate, NOT an integrity guarantee — a same-size corruption passes it. Content is authoritatively re-hashed by store-verify-item / copy-from-store (verify-on-use), which quarantine a mismatch as a cache miss.

Return the full filesystem path for a store path, VERIFYING its content first (verify-on-use for every serve path, not just the build cache).

Re-hashes the entry against its recorded key hash before returning. A corrupt/poisoned entry is quarantined and this RAISES — so consumers that resolve a store path and then symlink + chmod +x + execute it (the app launcher via sigil install / sigil manage rollback) can never be handed, or run, unverified bytes. This is what makes the store-wide "served only if it re-hashes to its key hash" contract actually hold store-wide; store-valid? alone is only a size-gate a same-length bit-flip passes.

Raises if the item doesn't exist, or if its content fails verification.

Return the references (dependencies) of a store item.

Return all items that reference a given store path.

store-listprocedure

List all valid store paths.

Look up an output by its exact full Seal derivation hash. Returns its store path on a verified hit and #f on a miss. A corrupt hit is quarantined and raises with the concrete hash so command layers can preserve actionable integrity provenance.

Look up and verify an exact derivation output, returning its store path, recorded content hash, and filesystem path as one atomic serve result. Callers that need more than the store-path string should use this API so they do not accidentally re-hash a large tree for each field.

Look up a store item by its exact full output content/tree hash. Multiple derivations may honestly produce identical bytes; choose the first verified path in stable order. Corrupt candidates are quarantined and skipped. If no verified candidate remains, raise the concrete CORRUPT detail instead of flattening it into a generic cache miss.

Attach store-owned provenance to an item without changing its content hash. External-build uses this for loud bounded-amber evidence.

derivationprocedure

Construct a derivation struct.

derivation?procedure

Test if a value is a derivation struct.

Get the name field of a derivation struct.

Get the system field of a derivation struct.

Get the inputs field of a derivation struct.

Get the builder field of a derivation struct.

Get the args field of a derivation struct.

Get the hash field of a derivation struct.

Get the output field of a derivation struct.

Register a builder procedure by name.

The builder receives (store derivation output-dir) and should produce the output file/directory in output-dir.

(register-builder! 'compile-module
  (lambda (store drv output-dir)
    (compile-file (source drv) (output drv))))

Create a derivation describing how to produce an output from inputs.

The output store path is computed from the derivation hash (a SHA-256 over the domain-separated Seal preimage of the derivation RECORD) — if the output already exists, the build is skipped. Seal makes the key INJECTIVE: no two distinct derivations can share a key (which would serve wrong bytes from the cache).

Two ways to key a derivation:

  1. CANONICAL (typed per-kind record — preferred). The caller builds a (sigil seal) record with the correct set/list semantics per the kind's frozen schema and passes kind: + schema-version: + record:. system and every input MUST already be folded into the record by the caller (the store does not synthesize them):
    (make-derivation s
      name: "core.sgb" kind: 'sgb-compile schema-version: 1
      inputs: (cons src-sp dep-sps)          ; for DB ref bookkeeping
      record: (seal-map (list (cons 'system sys) ...)))
  1. GENERIC (no record: — the store synthesizes an injective record from name/system/inputs/builder/args). For simple/ad-hoc callers:
    (make-derivation s
      name: "out" inputs: (list a b) builder: 'my-builder
      args: '((version . "1.0")) system: "x86_64-linux")

inputs: is retained purely for DB reference tracking and is decoupled from keying. builder: names the store-build procedure; it also supplies the domain kind when kind: is omitted.

Default system identifier — fail-closed (spec T-ST6).

The old behavior defaulted unset SIGIL_SYSTEM_OS/SIGIL_SYSTEM_ARCH to "unknown", silently keying a derivation as "unknown-unknown" — an ambient-env channel in the key path. System is now REQUIRED: callers pass system: (build-cmds always does) and fold it into the record. The env vars remain an explicit opt-in, but an unset environment is a hard error, never a silent placeholder.

store-buildprocedure

Build a derivation if its output doesn't already exist.

If the output store path is already valid, returns it immediately (cache hit). Otherwise, calls the builder to produce the output, adds it to the store, and returns the path.

(store-build s drv)
; => "ab3f7c89...-sigil-core.sgb"

Register a pre-built file as the output of a derivation.

Use this when compilation happens outside the store (e.g., by the build system), and the result needs to be cached at the derivation's deterministic output path.

(store-add-build-output s drv "/path/to/compiled.sgb")
; => "ab3f7c89...-compiled.sgb"

Register a GC root pointing to a store path.

Roots prevent an item and its transitive references from being garbage collected. NAME is a human-readable identifier.

(store-add-root s "dev-build" output-path)

Remove a GC root by name.

store-rootsprocedure

List all GC roots.

Returns a list of alists with name, item_path, and created_at.

store-gcprocedure

Delete all store items not reachable from any root.

Returns an alist with deleted count and bytes freed.

(store-gc s)
; => ((deleted . 42) (freed . 15728640))
store-statsprocedure

Report store size and item count.

(store-stats s)
; => ((count . 150) (size . 52428800))
store-verifyvariable

Full store integrity check (fsck).

Re-hashes every item against its recorded key hash, flags items whose on-disk content is missing or corrupt, checks that every reference resolves to a present item, and sweeps the store directory for orphans: leftover .tmp.* write stages (killed-mid-write partials — deleted) and files with no DB row (moved to quarantine). When repair? is true, corrupt/missing items are quarantined and their DB rows dropped so the keys become clean misses; otherwise they are only reported.

Returns an alist: verified, corrupt, missing, dangling, deleted-tmp, quarantined (all counts except the lists corrupt/missing/dangling).

(store-verify s repair?: #t)
; => ((verified . 148) (corrupt) (missing) (dangling) ...)

Copy a store item to a destination path under verify-on-use.

The bytes are COPIED (never hardlinked, so the build tree and the store never share an inode) into a staging temp beside dest-path, and it is the STAGED bytes that are re-hashed against the recorded key hash — then the same temp is renamed into place only if they match. Hashing the copy we are about to publish, rather than a prior read of the source, means the bytes that land at dest-path are exactly the bytes that verified: no verify→copy TOCTOU window. A mismatch (truncation, bit-flip, wrong- content-right-name, a poisoned entry, or a source mutated mid-copy) is QUARANTINED and this returns #f — a cache MISS, so the caller rebuilds instead of consuming corrupt bytes. Returns #t on a verified copy.

Copy and verify an item without attempting to repair or quarantine its source store. This is for immutable fallback layers: corrupt entries become ordinary misses while the read-only store remains untouched.

Re-hash a store item's on-disk content and compare to its recorded key hash. Returns #t only if the item exists and matches; #f on any mismatch, missing content, or a legacy row that has no recorded hash.

A publisher makes bytes visible before its DB row. Conversely, a concurrent repair can replace bytes immediately after a reader's first failed hash. Before quarantine (an irreversible verdict for this live path), yield once and re-read both row and bytes. Stable corruption still fails twice; a just-appeared healthy item survives.

(No description)

(No description)

(No description)

(No description)

(No description)

hash-filevariable

(No description)

hash-datavariable

(No description)

(No description)

(No description)