valis / Reference / API reference
Substrate - API reference
Exported surface for the substrate subsystem. Part of the API reference.
Package valis/src/substrate/fs-store
Classes
fs-store
Filesystem-backed store backend. Each publication is a subdirectory of DATA-DIR; each attribute is a plain text file; the body is a file named "body". Implements the CLOS store protocol (store.lisp). The seam for the v2 fossil/venti backend — replace this class without touching publication nodes.
Variables
*durable-writes*
When true (the default), store writes are flushed to stable storage before reporting success: the temp file's data is fdatasync'd before the rename, and the containing directory is fsync'd after, so a write that has returned survives a host crash or power loss. Bind to NIL only where durability is deliberately traded for speed (bulk imports, benchmarks); on rotating storage each durable write costs two device flushes.
*pub-data-dir*
Pathname for the publications data directory. Set by start-fabric to the XDG data path (~/.local/share/valis/publications) or the test scratch path. Nil before the fabric is up.
Package valis/src/substrate/publication
Classes
attr-file-node
A file node representing one named attribute of a publication. node-read returns the stored UTF-8 string, applying the tolerant-read default when the attribute is absent. node-write writes through to the store (the owner's authoring path; adapter sessions cannot reach this via the projection).
body-file-node
A file node representing the body content of a publication. node-read returns the raw body octets. Writes are the owner authoring path: each one lands in the pending buffer at its own offset, and the close replaces the stored body with the assembled result. Committing at close rather than per write is what makes a body that spans several 9P messages arrive whole, and it makes the replacement atomic from a reader's side: the previous body is served until the new one is complete. A fid opened and closed without a write commits nothing, so reading the body never rewrites it.
pub-dir
A directory node representing one publication. Its named children (title, author, published-date, content-type, body, gemtext) are assembled on demand by node-walk and node-entries from the store. gemtext is the per-protocol Gemini representation; it is absent for publications that only publish a generic body.
Variables
*pub-store*
The active store backend for publication nodes. Set by start-fabric after pub-data-dir is established; nil before the fabric is up.
Package valis/src/substrate/store
Conditions
store-error
Signalled by store operations when an I/O error or protocol violation occurs for a specific publication slug.
Generic functions
store-create-publication
(store-create-publication store slug)
Create a publication directory for SLUG. Signals STORE-ERROR on slug collision, invalid slug characters, or I/O error. Slug characters are validated to [a-z0-9-] before any disk operation — prevents path-component injection at the filesystem level.
store-error-cause
(store-error-cause condition)
Undocumented: this exported symbol needs a docstring.
store-error-slug
(store-error-slug condition)
Undocumented: this exported symbol needs a docstring.
store-list-publications
(store-list-publications store)
Return a list of publication slug strings known to STORE. The list reflects current disk state on each call — never a snapshot.
store-publication-exists-p
(store-publication-exists-p store slug)
Return T if a publication directory for SLUG exists in STORE, NIL otherwise.
store-publication-mtime
(store-publication-mtime store slug)
Return a universal-time integer representing the modification time of SLUG's publication directory, or NIL if the directory is absent. Used as the fallback published-date when the published-date attribute is not present — tolerant read, no error on absence.
store-read-attribute
(store-read-attribute store slug attribute-name)
Return the UTF-8 string value of ATTRIBUTE-NAME for SLUG, or NIL when the attribute file is absent. Tolerant read: absence is normal, not an error. The caller is responsible for applying any default value.
store-read-body
(store-read-body store slug)
Return the body content as an octet vector for SLUG, or an empty octet vector #() when the body file is absent. Tolerant read: absence is normal, not an error.
store-write-attribute
(store-write-attribute store slug attribute-name value)
Write VALUE (a string) as the attribute file ATTRIBUTE-NAME for SLUG. Creates the publication directory if absent. The write is atomic via rename-into-place so a reader never sees a partial write.
store-write-body
(store-write-body store slug octets)
Write OCTETS (an octet vector) as the body file for SLUG. The write is atomic via rename-into-place.
Package valis/src/substrate/store-store
Classes
store-store
Durable store backend over the content-addressed head/tree/block stack. Each publication is a subdirectory under a head-rooted "pub" directory; each attribute a named file child; the body a "body" file child. Implements the CLOS store protocol (store.lisp) behaviorally identically to fs-store at the publication seam, with no new on-disk format — it composes the existing codecs.
Generic functions
store-store-block-store
(store-store-block-store object)
Undocumented: this exported symbol needs a docstring.
store-store-head
(store-store-head object)
Undocumented: this exported symbol needs a docstring.
store-store-head-cache
(store-store-head-cache object)
Undocumented: this exported symbol needs a docstring.
Functions
make-store-store
(make-store-store data-dir)
Construct a STORE-STORE rooted at DATA-DIR: a block store over DATA-DIR, a head over that block store, and a fresh write lock.
publish-default-manifest
(publish-default-manifest head block-store manifest &key expected-generation)
Splice a "manifest" child into HEAD's current root tree over BLOCK-STORE, ALONGSIDE any existing "pub" child, then advance-head to the new root. The existing "pub" content is preserved — the splice replaces only the "manifest" key, so one head names both children. Genesis-tolerant: a nil root commits a root naming only "manifest" (pass :expected-generation nil, which advance-head commits as generation 0). Returns advance-head's (values new-generation reason).
score-low-word
(score-low-word score)
Fold the LAST 4 bytes of a 32-byte content SCORE big-endian into an (unsigned-byte 32). Only the low word of the score is needed for qid.version (v9fs uses it as a change-detector, not an ordered value), so this avoids a wide-integer conversion of the whole 32-byte digest. SCORE is a 32-byte (unsigned-byte 8) vector. No 0->1 remap here — that belongs to the version producer, not the byte fold.
store-node-score
(store-node-score store slug &key attribute body directory)
Return the 32-byte content score of the served node identified by SLUG and exactly one of the keyword selectors, or NIL when that node is absent (genesis/empty store, or an unwritten attribute/body leaf):
:directory t the SLUG's publication directory entry under "pub" :body t the SLUG's "body" leaf entry :attribute "name" the SLUG's named attribute leaf entry
A served publication node's qid.version is the low 32 bits of this score. The score is a pure function of the node's durable content (the Merkle property: a directory's score changes iff its listing changes, a leaf's iff its bytes change), so two coincident handles over one data-dir read the SAME score for the same node with zero coordination, and an unrelated slug's write cannot move this node's score. The entries are resolved through the revalidating cached pair walk (%pub-pairs/cached / %slug-pairs/cached) so a coincident write is reflected with staleness 0. STORE is the store-store. The reader returns the entry's score, not its decoded bytes — no extra block read. NIL for an absent node leaves the 0->1 remap and the nil->1 empty-case to the version producer.
Variables
*store-store-data-dir*
Pathname for the durable publication store's data directory. Set by start-fabric to the durable store's own root; nil before the fabric is up.