valis / Reference / API reference
Plugins - API reference
Exported surface for the plugin subsystem. Part of the API reference.
Package valis/src/plugin/active-module
Classes
active-module-spec
One registered active (resident) module. NAME is the module's name (the registry key material). START is a thunk that starts the module and returns a handle the seam owns; STOP is a thunk that tears the handle down; ALIVE-P is an optional thunk the seam polls for liveness (NIL when the option omits it). GRANTS is the list of egress-designation strings the module requests, carried verbatim as trust-neutral data, resolved into capabilities downstream, not here. CONTRACT-VERSION is the seam contract version admitted at registration.
Conditions
active-module-version-mismatch
Signalled by register-active-module when a module declares a contract version outside supported-active-module-contract-versions. The registry is left unchanged: the refusal happens before any setf gethash.
Functions
active-module-spec-alive-p
(active-module-spec-alive-p instance)
Undocumented: this exported symbol needs a docstring.
active-module-spec-contract-version
(active-module-spec-contract-version instance)
Undocumented: this exported symbol needs a docstring.
active-module-spec-grants
(active-module-spec-grants instance)
Undocumented: this exported symbol needs a docstring.
active-module-spec-name
(active-module-spec-name instance)
Undocumented: this exported symbol needs a docstring.
active-module-spec-start
(active-module-spec-start instance)
Undocumented: this exported symbol needs a docstring.
active-module-spec-stop
(active-module-spec-stop instance)
Undocumented: this exported symbol needs a docstring.
active-modules
(active-modules)
Return the registered ACTIVE-MODULE-SPECs in a deterministic order, sorted by name via STRING<. The stable order is what gives the boot supervisor a stable start/stop iteration order across runs.
register-active-module
(register-active-module &key name start stop alive-p grants (contract-version +active-module-contract-version+))
Register an active module under NAME. START/STOP/ALIVE-P are the lifecycle thunks the seam owns (ALIVE-P may be NIL); GRANTS is the module's requested egress-designation list, stored verbatim. Runs the fail-closed version check FIRST: if CONTRACT-VERSION is not in supported-active-module-contract-versions it signals ACTIVE-MODULE-VERSION-MISMATCH and adds NO entry: the refuse-before-act discipline leaves no partial registry state. Otherwise builds a fresh ACTIVE-MODULE-SPEC and stores it under (STRING NAME). IDEMPOTENT: re-registering the same name REPLACES the spec with no error, absorbing the compile-then-load double-fire of a module's define-module. Returns the spec.
unregister-active-module
(unregister-active-module name)
Remove the module registered under NAME (normalized via STRING). Returns T if a module was present and removed, NIL otherwise.
Variables
*active-modules*
Module name -> ACTIVE-MODULE-SPEC. The key is the name normalized via STRING, so :acme, "acme", and 'acme collapse to the one key "ACME". A resident module installs onto this registry on load via the :active-module define-module option. This is the registry the boot supervisor iterates to start/poll/stop each resident module; valis names no concrete module.
*supported-active-module-contract-versions*
The contract versions register-active-module admits. A module declaring a version outside this set is refused fail-closed: no silent skew.
Constants
+active-module-contract-version+
The active-module seam contract version valis currently exposes. With the registry and the register function, this is the surface valis signals a resident module over the seam.
Package valis/src/plugin/admit-module
Conditions
module-component-unhashed
The system would compile a file its identity was not computed over, so neither signature covers it.
module-load-failed
Both signatures covered the module and its load then failed, so some of its code may have run. A refusal by the gate is never this condition: there, none of the module's code ran.
module-loaded-from-elsewhere
After the load ASDF holds the system somewhere other than the identity directory the gate hashed.
module-root-not-identity-directory
The admitted bytes were not read from the directory their identity names, so a later reader could find different bytes under the same identity.
module-source-refused
The default loader refused to compile an admitted module because what it would compile is not exactly what the gate hashed.
Functions
admit-module
(admit-module system root files publisher-vouch owner-credential &key store (admission-kind :install) expiry-judged-at loader choose-credentials)
Admit and load the locally-present protocol module SYSTEM only when both of its signatures cover its identity: PUBLISHER-VOUCH, the publisher's token-chain: vouch rooted at an anchor the owner filed in STORE, and OWNER-CREDENTIAL, the owner's grant over the module or a standing grant naming its publisher. SYSTEM is the ASDF system designator handed to the loader; ROOT and FILES are the module root and its .asd and source pathnames, which module-bundle-identity names. STORE is the vouch store; without it nothing is filed and the call refuses. ADMISSION-KIND is :install (the default) or :restart, EXPIRY-JUDGED-AT the Unix time expiry is judged at (now when NIL); both pass to verify-module-admission. Both credentials are required arguments, so no call reaches the loader on one. Returns (values t nil) when the loader ran on SYSTEM, (values nil reason-string) on refusal, in which case the loader never ran, so a refused module never compiles, loads, or self-registers, or (values nil reason-string t) when both signatures admitted the module and its load then failed, so some of its code may have run. CHOOSE-CREDENTIALS, when given, is called with STORE held and returns (values publisher-vouch owner-credential) to judge and record in place of the two passed, so what is chosen from the store is what is judged, with no change to the store landing between. The store is held from that choice through the recording and let go before the load. LOADER is a test seam, called with SYSTEM alone. Without one, ROOT must be the identity directory of the module's identity under the module source root, and the module is compiled from there and nothing else, so the bytes that run are the bytes the signatures cover and a rollback loads the version it names. Any unhandled condition becomes a refusal, so this chokepoint is fail-closed. The reserved module/<hex of the identity> designation cannot collide with a mountable path: build-base-view never materializes /module…, so a vouch designation never names a live subtree.
confine-module-loading
(confine-module-loading compiled-root)
Set this image's ASDF configuration so that a module is found only through admission, and return COMPILED-ROOT. The source registry names no directory at all, so no module under the module source root is found by name; each admission names the one identity directory it admits. Compiled output goes beneath COMPILED-ROOT, keyed by each source's own path, so every identity directory compiles to a place of its own. The central registry and the system search functions, the dependency dist client's among them, are left as they are. A resident runs this once at boot; nothing else should, since it changes the whole image.
module-load-closure-files
(module-load-closure-files system identity-directory)
The files loading SYSTEM from IDENTITY-DIRECTORY would compile: its .asd and every source file of its own load plan, as truenames. Admission checks what a module would compile against its hashed set through this function. Reading the plan evaluates the .asd, which is code, so nothing should call this for a module that has not been admitted; a file selection made before admission, such as the one an install hashes, must be made without evaluating the .asd.
Package valis/src/plugin/condense-modules
Conditions
module-unreachable
Signalled during Pass 2 when a record's source cannot be materialized at the destination or its re-derived module-bundle-identity does not equal the recorded identity. A module-tier fault kept distinct from the store-tier module-manifest- corrupt: the whole condensation aborts fail-closed (no partial module set) before any admit-module call.
Generic functions
module-unreachable-system
(module-unreachable-system condition)
Undocumented: this exported symbol needs a docstring.
Functions
condense-modules
(condense-modules store root-entry &key vouch-locator vouch-store loader puller destination-root)
Stand the destination's module set up from the durable module manifest under ROOT-ENTRY in STORE: fail-closed, all-or-nothing.
VOUCH-LOCATOR is an (IDENTITY) -> (values publisher-vouch owner-credential admission-time) function: the two signatures a module's module designation must carry, and the Unix time it was admitted, at which their expiry is judged (now when it returns none). A record for which either comes back nil aborts the whole condensation before any load, and the reason names which signature is missing. VOUCH-STORE is the vouch store holding the owner's filed publisher anchors and standing grants; it is passed to admit-module, which refuses everything without it. Condensation is re-admission, so each module is admitted as a restart.
LOADER is admit-module's :loader test seam (without one, admit-module compiles each module from its identity directory); PULLER overrides module-puller for this call; DESTINATION-ROOT is the module source root, under which each record's relative source paths resolve against the directory its identity names.
A resident record (name-only, no identity) is skipped: the resident set is part of valis and has no bundle to reassemble, so it is never resolved, vouched or loaded here. Skipping never admits code.
Returns (values count loaded-systems) on success (the count of admitted modules and the list of their system-names in canonical order) once EVERY non-resident record admits. A manifest holding only resident records returns (values 0 nil). On any abort returns (values nil reason) and leaves edge-adapters exactly as it was on entry (no partial module set), or signals module-unreachable when a record cannot be reached / re-derived in Pass 2.
Pass 1 read-module-manifest (fail-closed: a corrupt manifest aborts here), then set the resident records aside. Pass 2 resolve + reachability + vouch precheck over ALL remaining records, NO loads. Pass 3 ordered admit-module per record on the Pass-2 resolved pair; restore edge-adapters from the entry snapshot on the first refusal.
condense-modules routes every module through admit-module and NEVER calls asdf:load-system directly: the gate is structurally in front of the loader. admit-module's own in-gate re-hash over the same resolved pair is intentional defense-in-depth (the gate independently re-verifies the content it is about to load), not a redundant resolution.
local-store-puller
(local-store-puller identity record destination-root)
The default local-store pull/resolve: the bytes are already on disk at the recorded relative paths under the record's identity directory beneath DESTINATION-ROOT, so this is exactly the nil-seam resolution surfaced as a puller value. Returns (values root files); IDENTITY is ignored (the bytes are not fetched, only located). Installed into module-puller by a wiring layer when a caller wants the seam non-nil; behaviorally identical to leaving the seam nil.
Variables
*module-puller*
The module pull/resolve transport seam, or NIL.
When NIL, condense-modules resolves a record's RELATIVE source list against DESTINATION-ROOT (the bytes are already on disk at the recorded relative paths, today's single-instance case). When non-nil, it is a function of (IDENTITY RECORD DESTINATION-ROOT) returning (values ROOT FILES), the resolved, ABSOLUTE (root files) locator for the module named by IDENTITY, or signalling module-unreachable when the bytes cannot be obtained.
This is the seam that keeps the pull transport-agnostic: a future IPFS/libp2p transport rebinds module-puller to materialize the bytes from a content- addressed network WITHOUT reshaping the module record (the IPFS gate stays open). The default is NIL so a unit test can drive the local on-disk path explicitly; local-store-puller is the local-fetch helper a wiring layer installs into the seam.
Package valis/src/plugin/dns-adapter
Classes
dns-adapter-spec
One registered DNS adapter. NAME is the adapter's name (the registry key material). HANDLER is a function of a decoded query that speaks the wire and returns a response designator; the wire engine binds it. CONTRACT-VERSION is the seam contract version the binding engine declared at registration, the value the fail-closed register-time check admitted. SOURCE is the raw zone-data-source the serving handler was bound over (in production a pg-zone-source), retained so a caller that already holds the admitted handler can reach the same underlying zone records: the AXFR emit path streams the zone directly off it. NIL for a registration that carried no source (the module self-registration path, which binds only a deferred-resolution handler).
Conditions
dns-adapter-version-mismatch
Signalled by register-dns-adapter when a binding engine declares a contract version outside supported-dns-contract-versions. The registry is left unchanged: the refusal happens before any setf gethash.
Functions
dns-adapter-spec-contract-version
(dns-adapter-spec-contract-version instance)
Undocumented: this exported symbol needs a docstring.
dns-adapter-spec-handler
(dns-adapter-spec-handler instance)
Undocumented: this exported symbol needs a docstring.
dns-adapter-spec-name
(dns-adapter-spec-name instance)
Undocumented: this exported symbol needs a docstring.
dns-adapter-spec-source
(dns-adapter-spec-source instance)
Undocumented: this exported symbol needs a docstring.
dns-adapters
(dns-adapters)
Return the registered DNS-ADAPTER-SPECs in a deterministic order, sorted by name via STRING<. The stable order is what gives the dispatcher a stable iteration order across runs.
register-dns-adapter
(register-dns-adapter &key name handler source (contract-version +dns-adapter-contract-version+))
Register a DNS adapter under NAME with HANDLER declaring CONTRACT-VERSION. Runs the fail-closed version check FIRST: if CONTRACT-VERSION is not in supported-dns-contract-versions it signals DNS-ADAPTER-VERSION-MISMATCH and adds NO entry: the refuse-before-act discipline leaves no partial registry state. Otherwise builds a fresh DNS-ADAPTER-SPEC and stores it under (STRING NAME). IDEMPOTENT: re-registering the same name REPLACES the spec with no error, absorbing the compile-then-load double-fire of a module's define-module and mirroring REGISTER-PROTOCOL :replace. HANDLER is a function of a decoded query returning a response designator. SOURCE is the optional raw zone-data-source the handler was bound over, retained on the spec so a serving path can reach the same zone records the handler answers from (the AXFR emit path streams the zone directly off it); NIL on the module self-registration path, which carries no source. Returns the spec.
unregister-dns-adapter
(unregister-dns-adapter name)
Remove the adapter registered under NAME (normalized via STRING). Returns T if an adapter was present and removed, NIL otherwise.
Variables
*dns-adapters*
Adapter name -> DNS-ADAPTER-SPEC. The key is the name normalized via STRING, so :dns, "dns", and 'dns collapse to the one key "DNS" (STRING on a symbol yields its symbol-name, on a string yields itself). A protocol module installs onto this registry on load via the :dns-adapter module option. This is the registry the dispatcher iterates to find the adapter that answers a decoded query; valis names no concrete adapter.
*supported-dns-contract-versions*
The contract versions register-dns-adapter admits. A binding engine declaring a version outside this set is refused fail-closed: no silent skew with the wire engine.
Constants
+dns-adapter-contract-version+
The DNS adapter seam contract version valis currently exposes. With the registry and the register function, this is the surface valis signals the out-of-tree wire engine over the bus.
Package valis/src/plugin/edge-adapter
Classes
edge-adapter-spec
One registered edge adapter. NAME is the adapter's name (the registry key material). CONSTRUCTOR is a function of (&key port budget) returning a fresh protocol whose PROTOCOL-PORTS reflect the port override (or the adapter's own default when PORT is NIL); the adapter owns binding its own …-port special. Nothing here names an adapter's port variable. BUDGET is the adapter's default per-port concurrent-connection budget (or NIL to take the controller's default).
Conditions
edge-adapter-attach-refused
The edge would not attach the adapter named ADAPTER-NAME. REASON is one of :not-registered, :edge-not-running, :already-attached, :port-already-bound or :bind-failed; PORTS names the ports the refusal concerns, when there are any, and CAUSE the condition underneath it, when there is one. Nothing the attempt put in place survives a refusal, and no port bound before it was touched.
Generic functions
edge-adapter-attach-refused-adapter-name
(edge-adapter-attach-refused-adapter-name condition)
Undocumented: this exported symbol needs a docstring.
edge-adapter-attach-refused-cause
(edge-adapter-attach-refused-cause condition)
Undocumented: this exported symbol needs a docstring.
edge-adapter-attach-refused-ports
(edge-adapter-attach-refused-ports condition)
Undocumented: this exported symbol needs a docstring.
edge-adapter-attach-refused-reason
(edge-adapter-attach-refused-reason condition)
Undocumented: this exported symbol needs a docstring.
Functions
edge-adapter-spec-budget
(edge-adapter-spec-budget instance)
Undocumented: this exported symbol needs a docstring.
edge-adapter-spec-constructor
(edge-adapter-spec-constructor instance)
Undocumented: this exported symbol needs a docstring.
edge-adapter-spec-name
(edge-adapter-spec-name instance)
Undocumented: this exported symbol needs a docstring.
edge-adapters
(edge-adapters)
Return the registered EDGE-ADAPTER-SPECs in a deterministic order, sorted by name via STRING<. The stable order is what gives the controller a stable port-bind order across runs.
register-edge-adapter
(register-edge-adapter &key name constructor budget)
Register an edge adapter under NAME with CONSTRUCTOR and BUDGET. Builds a fresh EDGE-ADAPTER-SPEC and stores it under (STRING NAME). IDEMPOTENT: re-registering the same name REPLACES the spec with no error: this absorbs the compile-then-load double-fire of a module's define-module and mirrors REGISTER-PROTOCOL :replace. CONSTRUCTOR is a function of (&key port budget). Returns the spec.
Registration asks for ports; it does not open them and it tells nobody. What a registered adapter answers on is known once the edge builds its protocol, so the edge controller is where those ports join the set this node says it serves and where the host agent is told. Putting the declaration here instead would report a port to the agent that admits it before anything had bound it.
unregister-edge-adapter
(unregister-edge-adapter name)
Remove the adapter registered under NAME (normalized via STRING). Returns T if an adapter was present and removed, NIL otherwise.
Variables
*attach-edge-adapter*
A function of one argument, an adapter name, that puts the registered adapter of that name in front of the running edge and answers the ports it bound, or NIL when the edge is not running and the next start-edge will bind the adapter instead. NIL when no edge controller is loaded, and then nothing is attached. The edge controller sets it when it loads. A refusal is signalled as an EDGE-ADAPTER-ATTACH-REFUSED.
*edge-adapters*
Adapter name -> EDGE-ADAPTER-SPEC. The key is the name normalized via STRING, so :http, "http", and 'http collapse to the one key "HTTP" (STRING on a symbol yields its symbol-name, on a string yields itself). A protocol module installs onto this registry on load via the :edge-adapter module option and is retired by the controller's delete-hook. This is the registry that replaces the edge controller's hard-coded adapter list; the controller iterates EDGE-ADAPTERS and binds a real port per spec.
Package valis/src/plugin/image-divergence
Functions
amend-image-divergence
(amend-image-divergence entry &key (reason nil reason-p) (detail nil detail-p))
Complete ENTRY, an entry note-image-divergence returned, once the outcome it was noted for is known, by replacing its REASON, its DETAIL or both. REASON is one of image-divergence-reasons, or :not-loaded when the change it was noted for was refused before any of its code was compiled, so the image did not diverge and image-diverged-p does not count the entry. A reason only moves forward: an entry that already reads :load-failed or :not-loaded keeps it, and amending it to anything else signals. The entry stays in the record and keeps its time; nothing is ever removed.
image-diverged-p
(image-diverged-p)
True when something has been noted as diverging this image since the process started, leaving out an entry completed as :not-loaded, which records a change refused before any of it was compiled. NIL means only that nothing was noted, never that the image matches what a fresh start would build: a supersede notes itself whatever its residue scan found, and a change nobody noted still happened.
image-divergence-entries
(image-divergence-entries)
This image's divergence entries, newest first, as a fresh list of fresh plists, so what a caller does with them never changes the record.
note-image-divergence
(note-image-divergence reason &key system detail)
Record that this image has diverged, for REASON, one of image-divergence-reasons, concerning SYSTEM, with DETAIL for the owner, at the present time. The record lasts until the process ends.
Package valis/src/plugin/mail-adapter
Classes
mail-adapter-spec
One registered mail adapter. NAME is the adapter's name (the registry key material). DELIVER is a function called with a queue entry that speaks the wire and returns one of :delivered / :deferred / :bounced and mutates nothing else; the driver, not the adapter, records the disposition. Inbound landing is the valis-exported LAND-MESSAGE the adapter calls into, so the spec carries no inbound-handler slot.
Functions
mail-adapter-spec-deliver
(mail-adapter-spec-deliver instance)
Undocumented: this exported symbol needs a docstring.
mail-adapter-spec-name
(mail-adapter-spec-name instance)
Undocumented: this exported symbol needs a docstring.
mail-adapters
(mail-adapters)
Return the registered MAIL-ADAPTER-SPECs in a deterministic order, sorted by name via STRING<. The stable order is what gives the drain driver a stable iteration order across runs.
register-mail-adapter
(register-mail-adapter &key name deliver)
Register a mail adapter under NAME with DELIVER. Builds a fresh MAIL-ADAPTER-SPEC and stores it under (STRING NAME). IDEMPOTENT: re-registering the same name REPLACES the spec with no error: this absorbs the compile-then-load double-fire of a module's define-module and mirrors REGISTER-PROTOCOL :replace. DELIVER is a function called with a queue entry, returning a disposition keyword. Returns the spec.
unregister-mail-adapter
(unregister-mail-adapter name)
Remove the adapter registered under NAME (normalized via STRING). Returns T if an adapter was present and removed, NIL otherwise.
Variables
*mail-adapters*
Adapter name -> MAIL-ADAPTER-SPEC. The key is the name normalized via STRING, so :smtp, "smtp", and 'smtp collapse to the one key "SMTP" (STRING on a symbol yields its symbol-name, on a string yields itself). A protocol module installs onto this registry on load via the :mail-adapter module option. This is the registry the outbound drain driver iterates to find the adapter that delivers a relay entry; valis names no concrete adapter.
Package valis/src/plugin/module-admission
Conditions
publisher-vouch-malformed
Signalled when a publisher vouch offered as token-chain: text does not decode, so it names no publisher; REASON says why.
Generic functions
publisher-vouch-malformed-reason
(publisher-vouch-malformed-reason condition)
Undocumented: this exported symbol needs a docstring.
Functions
accept-publisher-list
(accept-publisher-list store list-text &key before-filing)
File LIST-TEXT, a publisher-list: text, in STORE, the vouch store, only when it verifies: its issuer must be a filed publisher anchor, since only a publisher's creating identity may speak for it, and its signature must verify under that anchor's key over the bytes as received. BEFORE-FILING, when given, is then called with the verified fields and may refuse with (values nil reason), in which case nothing is written. Filed, it replaces only the list its own issuer filed before, and its revoked targets join the revocations the store remembers for that issuer. Returns (values fields nil) on success, so the caller never decodes the list itself, or (values nil reason) naming the list, having written nothing. The list is decoded exactly once, here.
designations-admitted-under-grant
(designations-admitted-under-grant store grant-hash)
The module designations in STORE, the vouch store, whose held owner credential is the grant GRANT-HASH names (its token-grant-hash), sorted. This is the set an owner backs out when withdrawing trust in a standing grant.
module-admitted-p
(module-admitted-p identity vouch)
Predicate form of verify-module-vouch: return just the boolean admission decision for callers that do not need the refusal reason. Fail-closed: any refusal or signalled condition reads as NIL.
publisher-list-age
(publisher-list-age store issuer &optional (now (unix-now)))
How many seconds before NOW the list ISSUER last filed in STORE, the vouch store, was issued, by its signed issue time, or NIL when ISSUER has filed none.
publisher-vouch-anchor
(publisher-vouch-anchor vouch)
The did:key of the publisher anchor VOUCH, a token-chain: publisher vouch, is rooted at, and the decoded leaf as a second value; NIL for any other carrier or for NIL. Signals publisher-vouch-malformed when token-chain: text does not decode. It verifies nothing: the gate and an install that looks up the publisher's grant derive the anchor here, so both name the same publisher.
verify-module-admission
(verify-module-admission identity publisher-vouch owner-credential &key store expiry-judged-at (admission-kind :install))
Decide whether the module whose IDENTITY (its multihash) is given may be admitted: both PUBLISHER-VOUCH and OWNER-CREDENTIAL must cover exactly that identity, and nothing the publisher has revoked may lie on the vouch's chain. Returns (values t nil), or (values nil reason-string) where the reason names the signature or the publisher list that refused. PUBLISHER-VOUCH is a token-chain: string rooted at a publisher anchor filed in STORE, the vouch store; with no STORE nothing is filed and nothing is admitted. OWNER-CREDENTIAL is the owner's grant over the identity, or a standing grant naming the publisher. ADMISSION-KIND is :install (the default), which requires a standing grant to be filed and active and a publisher list from the vouch's own publisher no more than seven days old that does not withdraw the vouch, or :restart, which consults neither the grant's withdrawal nor the list's age or withdrawals, and logs the list's age or its absence. A revocation this node remembers from the publisher's lists refuses at both. Expiry is judged at EXPIRY-JUDGED-AT, a Unix time, or now when it is NIL; signatures and revocation are always judged now. Fail-closed: no signal escapes.
verify-module-vouch
(verify-module-vouch identity vouch)
Decide whether VOUCH admits the module whose IDENTITY (its multihash, from module-bundle-identity) is given. Returns (values t nil) when an owner-rooted, unrevoked, unfenced authority grants :admit over exactly the designation of that identity; otherwise (values nil reason-string).
VOUCH is the held bearer credential, in any of the carrier forms the cap verify path accepts: a capability-token object, a "valis:""name:" bearer name string, or a "token:" serialized-token string. The trust check rides verify-token-chain / verify-capability-name-sig against the owner root and the shared revocation store: no parallel trust path, no second revocation check.
This answers the owner's half of admission only. Loading a module takes both signatures, through verify-module-admission; this answer alone never reaches the loader.
Fail-closed: any signalled condition becomes (values nil "<message>"); a refusal always carries a reason and never a bare nil, and no signal escapes.
verify-publisher-vouch
(verify-publisher-vouch identity vouch store)
Judge VOUCH as the gate judges the publisher's half of admission for the module IDENTITY names, against the anchors filed in STORE, now: (values anchor-did nil) when it verifies and nothing the publisher has revoked, as this node remembers from the publisher's lists, lies on its chain; else (values nil reason). The publisher's lists are read as a restart reads them: a list need not be held or fresh, and a withdrawal in a held list is not consulted, because what this answers is whether the vouch could re-admit the module at a restart, not whether it admits a fresh install. Anything that keeps a vouch for later admission asks this first, so a vouch that cannot admit is never held in place of one that can. Fail-closed: no signal escapes.
Constants
+publisher-list-install-freshness+
The oldest, in seconds by its signed issue time, a publisher list may be for a fresh install to rely on it: seven days.
Package valis/src/plugin/module-bundle
Conditions
module-designation-bad-length
The designation's digest is not the length its algorithm fixes: declared wrongly, cut short, or followed by further octets.
module-designation-invalid
Signalled when text offered as a module designation does not name a module identity valis mints.
module-designation-unknown-algorithm
The designation names a hash function valis does not mint module identities with. Refusing it keeps the same bytes from being presented under an algorithm valis never chose.
Generic functions
module-designation-invalid-designation
(module-designation-invalid-designation condition)
Undocumented: this exported symbol needs a docstring.
module-designation-invalid-reason
(module-designation-invalid-reason condition)
Undocumented: this exported symbol needs a docstring.
module-designation-unknown-algorithm-code
(module-designation-unknown-algorithm-code condition)
Undocumented: this exported symbol needs a docstring.
Functions
module-bundle-designation
(module-bundle-designation root files)
Compute the /module/<hex> designation for the module rooted at ROOT with the given .asd and source FILES: module-bundle-identity then module-designation.
module-bundle-identity
(module-bundle-identity root files &key (read-octets (function %read-file-octets)))
Return the identity of the module rooted at ROOT whose .asd and source FILES are the given pathname list: a multihash, today SHA-256 (34 octets).
Each file is entered by its path relative to ROOT, its size, and the SHA-256 of its raw bytes, and the entries are taken in order of relative path, so on-disk discovery order does not change the identity while any change to a file's bytes, size or name does.
The identity names its own algorithm, so a SHA-256 identity and a later BLAKE3 one can stand side by side and a vouch over one survives the arrival of the other. It is the only name a module has inside valis: a network's name for the same bytes is computed where that network is spoken and is never stored.
READ-OCTETS reads one file's raw bytes; an install passes a stricter reader that refuses a file that is a link or has other names.
module-designation
(module-designation identity)
Render IDENTITY, a module's multihash, as its /module/<lowercase hex> designation, the path a vouch grants :admit over. A SHA-256 identity renders as 68 hex digits, never truncated.
module-identity-directory
(module-identity-directory source-root identity)
The directory under SOURCE-ROOT that holds the bytes of the module IDENTITY names: SOURCE-ROOT/<lowercase hex of IDENTITY>/, the hex its module designation carries. Admission and restart re-admission read a module's bytes through this one mapping, and an install is to write them through it, so a version's bytes are found only where its identity says they are, and a rollback loads the bytes it names.
parse-module-designation
(parse-module-designation designation)
Return the identity DESIGNATION names: the multihash octets of a /module/<lowercase hex> designation. Signals module-designation-unknown-algorithm when it names a hash function valis does not mint, module-designation-bad-length when its digest is not the length that function fixes, and module-designation-invalid for any other text.
Package valis/src/plugin/module-change-diff
Classes
module-change
One difference between two versions of a module that a running image cannot take in place, or, when KIND is :stated, one it takes but whose effect the owner must be told. NAME is the definition it concerns as text, FILE the file relative to the version's directory, REASON a sentence for the owner and the log.
Functions
module-change-file
(module-change-file instance)
Undocumented: this exported symbol needs a docstring.
module-change-kind
(module-change-kind instance)
Undocumented: this exported symbol needs a docstring.
module-change-name
(module-change-name instance)
Undocumented: this exported symbol needs a docstring.
module-change-reason
(module-change-reason instance)
Undocumented: this exported symbol needs a docstring.
module-change-restart-required-p
(module-change-restart-required-p change)
True when CHANGE can only be taken by restarting into the new version.
module-changes-summary
(module-changes-summary changes)
One readable text naming each change in CHANGES that requires a restart, then each change that is only stated, for the owner and the log.
module-restart-changes
(module-restart-changes old-identity-directory new-identity-directory)
Compare the module version in OLD-IDENTITY-DIRECTORY with the one in NEW-IDENTITY-DIRECTORY from their bytes alone and return a list of module-change: each change outside what a running image can take in place, with a reason that begins "restart required", and each change it takes but must state, of kind :stated. An empty list means every difference is supported. The files compared are the .lisp and .asd files an install hashes. Nothing of either version is loaded, compiled or evaluated, and no package or symbol is created in this image, because compiling a change is itself the first thing that can break a running image. The comparison refuses on textual difference, not meaning, so it can refuse a harmless change. A top-level form of a kind the comparison does not take apart, such as a let around definitions, a module's own defining macro, or any form of a .asd other than a system definition, is compared whole, and any change inside it is restart required.
Package valis/src/plugin/module-class
Classes
module-inferred-system
The :class a protocol module's .asd names. It is simultaneously a modularize VIRTUAL-MODULE and an ASDF PACKAGE-INFERRED-SYSTEM: ASDF derives the component graph from each file's defpackage :import-from, and modularize's module lifecycle applies to the resulting system. Superclass precedence is VIRTUAL-MODULE before PACKAGE-INFERRED-SYSTEM, the cold-verified order from spike 004; both are asdf:system subclasses so the combined CPL is coherent with no metaclass conflict. valis defines and exports this class; a protocol module sets :class "valis/src/plugin/module-class:module-inferred-system".
Package valis/src/plugin/module-dry-run
Classes
supersede-dry-run
The outcome of a dry run. STATUS is 0 when both versions loaded, 5 when one was refused before any of its code ran, 7 when one was admitted and its load failed, 6 when the dry run could not be asked, and 2 or 3 for designations the child would not take. PHASE is "old" or "new", the version the status is about, or NIL. REDEFINED names what the new version redefined; WARNINGS holds the text of every warning its load raised that was not a style warning. PROCESS-ID is the child's, or NIL when none was started.
Functions
run-supersede-dry-run
(run-supersede-dry-run old-designation new-designation)
Ask another process to load the module version OLD-DESIGNATION names and then the one NEW-DESIGNATION names, through the node's own gate, and return what it found as a supersede-dry-run. Nothing of either version is compiled in this image. The process is supersede-dry-run-command's; with none configured nothing is started and the answer is status 6. A process that has not finished within supersede-dry-run-time-limit seconds is killed and reaped, and the answer is status 6, as it is when the process gives no verdict, more than one, or one that does not parse.
supersede-dry-run-message
(supersede-dry-run-message instance)
Undocumented: this exported symbol needs a docstring.
supersede-dry-run-phase
(supersede-dry-run-phase instance)
Undocumented: this exported symbol needs a docstring.
supersede-dry-run-process-id
(supersede-dry-run-process-id instance)
Undocumented: this exported symbol needs a docstring.
supersede-dry-run-redefined
(supersede-dry-run-redefined instance)
Undocumented: this exported symbol needs a docstring.
supersede-dry-run-status
(supersede-dry-run-status instance)
Undocumented: this exported symbol needs a docstring.
supersede-dry-run-warnings
(supersede-dry-run-warnings instance)
Undocumented: this exported symbol needs a docstring.
Variables
*supersede-dry-run-command*
NIL, or a function of an old and a new module designation returning the argument list, program first, that runs the dry run of superseding the one with the other. A serving node sets it to its own executable's module try-supersede verb; with none set a dry run cannot be asked, and a supersede must then be refused.
*supersede-dry-run-time-limit*
Seconds a dry run may take before its process is killed and the dry run answers that it could not be asked.
+supersede-dry-run-verdict-prefix+
What begins the one line a dry run answers with; the rest of the line is a JSON object.
Package valis/src/plugin/module-install
Conditions
module-directory-refused
Signalled when a module's directory cannot give an identity: it holds a symbolic link or anything else that is not a regular file or a directory, or it holds no file at all.
module-install-refused
Signalled inside install-module when a directory is refused before the gate is asked: it is not where an identity directory must be, or its bytes are not the identity it is named for.
module-install-refused-on-serving-node
Signalled when a module would be installed and recorded on a node that serves real names, before anything of the module loads, and again before its record is published. Nothing was loaded or recorded.
Generic functions
module-directory-refused-path
(module-directory-refused-path condition)
Undocumented: this exported symbol needs a docstring.
module-directory-refused-reason
(module-directory-refused-reason condition)
Undocumented: this exported symbol needs a docstring.
module-install-refused-directory
(module-install-refused-directory condition)
Undocumented: this exported symbol needs a docstring.
module-install-refused-on-serving-node-designation
(module-install-refused-on-serving-node-designation condition)
Undocumented: this exported symbol needs a docstring.
module-install-refused-on-serving-node-route
(module-install-refused-on-serving-node-route condition)
Undocumented: this exported symbol needs a docstring.
module-install-refused-on-serving-node-system
(module-install-refused-on-serving-node-system condition)
Undocumented: this exported symbol needs a docstring.
module-install-refused-reason
(module-install-refused-reason condition)
Undocumented: this exported symbol needs a docstring.
Functions
call-with-module-change-lock
(call-with-module-change-lock thunk &key timeout on-timeout)
Call THUNK holding the module change lock, which the same thread may already hold, and return its values. With TIMEOUT NIL the wait has no bound; otherwise, when the lock is not free within TIMEOUT seconds, ON-TIMEOUT is called instead and its values are returned.
check-module-identity-directory
(check-module-identity-directory identity-directory &key source-root on-identity)
Check IDENTITY-DIRECTORY as install-module checks the directory it installs from, and return its identity. The directory must be absolute, sit directly under SOURCE-ROOT (the configured module source root when NIL), be private to this node, and hold the bytes of the identity its name gives. ON-IDENTITY, when given, is called with the identity as soon as it is computed, before the name is checked against it. Signals module-install-refused or module-directory-refused on whatever install-module would refuse, before any of the module is read as Lisp. Returns (values identity root files source-root), as module-directory-identity returns the first three.
check-module-source-root
(check-module-source-root source-root)
Signal module-install-refused unless SOURCE-ROOT is an absolute path to a directory private to this node, beneath directories no one else can write. Everything that writes or reads module bytes under the root asks this first, so one root is judged one way.
durable-manifest-store
(durable-manifest-store)
The store holding this node's durable module manifest: the running node's durable publication store, or NIL when the node has none, as before it starts or on the plain file backend.
install-module
(install-module system identity-directory &key source-root (store *admit-vouch-store*) owner-credential loader (manifest-store (durable-manifest-store)) (declared-names (function resolve-edge-domains)) (owned-zones (function owned-zone-origins)))
Admit and load the module SYSTEM from IDENTITY-DIRECTORY, an absolute directory directly under SOURCE-ROOT (the configured module source root when NIL) named for the identity of the bytes it holds, only when its publisher vouch and one owner credential cover that identity, and then record it in the durable module manifest of MANIFEST-STORE, so a restart stands it up again. Everything the install reads, and every directory above the source root, must be safe from other users, as the checks below require. STORE is the vouch store the signatures come from and admission records in. OWNER-CREDENTIAL is offered when the module's own slot cannot admit a new install. LOADER is a test seam handed to admit-module; without one the module is compiled by admission's own loader, the first thing that evaluates any of it. MANIFEST-STORE is the running node's durable store by default; NIL records nothing, for an install that must leave the node's stores as they were. When there is a record to write and the readers DECLARED-NAMES and OWNED-ZONES report that this node serves real names, by the test node-serves-real-names-p applies, module-install-refused-on-serving-node is signalled before anything of the module loads. The install holds the module change lock throughout, so no other install or supersede changes module code in this image meanwhile; when another change holds it for longer than module-change-lock-timeout seconds, the install is refused as one made while another change is in progress, and nothing is loaded. Returns (values t nil identity) when admitted and recorded, or (values nil reason identity load-failed), IDENTITY NIL when it was never computed, and LOAD-FAILED true when the gate admitted the module and then its load failed or its record could not be published, so some of its code may have run, NIL when it was refused before any of its code ran. Admission, not this function, records the credentials that admitted the module.
module-directory-files
(module-directory-files identity-directory)
Every regular file under IDENTITY-DIRECTORY, as pathnames beneath its truename, except the publisher vouch file at its top: the files a module's identity covers. Signals module-directory-refused when the directory is missing, unreadable, a symbolic link or not private to this node, holds a link anywhere, holds a directory another user could write, any other kind of file or a name that is not UTF-8, lies too deep, or selects no file. It only lists directories, so nothing in the module is read as Lisp and nothing in it runs.
module-directory-identity
(module-directory-identity identity-directory)
The identity of the module whose bytes IDENTITY-DIRECTORY holds, exactly as an install computes it, returned as (values identity root files): the files module-directory-files selects, hashed relative to ROOT, the directory's truename, each read as an install reads it. Whoever signs a module calls this so the identity signed is the identity a node computes. Signals module-directory-refused on everything module-directory-files refuses and on any file an install would refuse to read.
node-serves-real-names-p
(node-serves-real-names-p &key (declared (function resolve-edge-domains)) (owned (function owned-zone-origins)))
Which route says this node serves real names, or NIL when neither does: :declared-names when DECLARED, the reader of the names the operator declared primary, returns any, else :owned-zones when OWNED, the reader of the zones held on someone's behalf, answers :queried with at least one origin. An operator state that cannot be read counts as serving nothing, so a node that declares no name and cannot reach its zones at install time is taken as serving none.
recorded-module-identity
(recorded-module-identity manifest-store system)
The identity the durable module manifest of MANIFEST-STORE records for the module SYSTEM, or NIL when it records none.
Macros
with-module-change-lock
(with-module-change-lock (&key (timeout (quote *module-change-lock-timeout*)) on-timeout) &body body)
Run BODY holding the module change lock, which the same thread may already hold. The wait is bounded by TIMEOUT seconds, module-change-lock-timeout by default, and NIL leaves it unbounded; when the lock is not free in time, ON-TIMEOUT is evaluated instead of BODY. A change made for the owner passes a bound, so it is refused rather than left waiting behind another change; the boot that stands the recorded modules up waits.
Variables
*module-change-lock*
Held by every install, request and supersede while it reads which module versions the image carries and loads module code.
*module-change-lock-timeout*
Seconds an install, request or supersede made for the owner waits for another module change to finish before it is refused as a change already in progress.
Constants
+module-directory-depth-limit+
The deepest a directory may lie beneath a module's identity directory. No module needs more, and a mount that loops back on itself must end in a refusal rather than exhaust the stack.
Package valis/src/plugin/module-residue
Functions
module-code-residue
(module-code-residue identity-directory)
The code compiled from files under IDENTITY-DIRECTORY that is still alive in this image after a full collection, as a list of (entry-point-names . source-namestring), one per live code object. After a supersede, the old version's identity directory names what the new version left of it running. This is a report of what the scan found, never a verdict on the image: it can include code nothing will run again, and it has been seen to miss live code entirely, so an empty answer does not mean the old version is gone. A supersede stays tentative until a restart whatever this returns. The full collection comes first for two reasons: dead code must be gone before it is counted, and without it the walk did not see a version loaded moments before at all.
Package valis/src/plugin/module-signatures
Conditions
bundled-publisher-vouch-refused
Signalled when the publisher vouch shipped in an identity directory cannot be filed; DIRECTORY is that directory and REASON says why. Nothing was filed.
Generic functions
bundled-publisher-vouch-refused-directory
(bundled-publisher-vouch-refused-directory condition)
Undocumented: this exported symbol needs a docstring.
bundled-publisher-vouch-refused-reason
(bundled-publisher-vouch-refused-reason condition)
Undocumented: this exported symbol needs a docstring.
Functions
file-bundled-publisher-vouch
(file-bundled-publisher-vouch store identity-directory &key accept)
File into STORE, the vouch store, the publisher vouch shipped in IDENTITY-DIRECTORY under bundled-publisher-vouch-file-name, against the module that directory is named for, exactly as received. Returns that module's designation. ACCEPT is as file-shipped-publisher-vouch takes it. Signals bundled-publisher-vouch-refused, filing nothing, when the directory names no module, holds no such file, the file is not token-chain: text, or ACCEPT refuses it. Nothing is written inside the directory.
file-shipped-publisher-vouch
(file-shipped-publisher-vouch store directory designation &key accept)
File into STORE, the vouch store, against DESIGNATION, the publisher vouch shipped at the top of DIRECTORY under bundled-publisher-vouch-file-name, exactly as received. Returns DESIGNATION. The caller vouches that DIRECTORY holds the bytes DESIGNATION names. ACCEPT, when given, is called with the vouch text and returns (values ok reason); the vouch is filed only when it is OK. Signals bundled-publisher-vouch-refused, filing nothing, when DIRECTORY holds no such file, the file is not token-chain: text, or ACCEPT refuses it. Nothing is written inside the directory.
locate-module-signatures
(locate-module-signatures store identity)
Return what STORE, the vouch store, holds for the module IDENTITY names: (values publisher-vouch owner-credential admission-time), with NIL in place of each that is not held. The owner credential is the one in the module's owner slot and nothing else. Reads the store alone: no module directory, no publisher list, and no signature is judged.
Constants
+bundled-publisher-vouch-file-name+
The name of the file inside a module's identity directory that carries the publisher's vouch shipped with the bundle. It is never part of the module's identity.
Package valis/src/plugin/module-signing
Conditions
module-signing-refused
Signalled when a module vouch or a publisher list is not signed; REASON says why. Nothing was signed and nothing was written.
publisher-list-drop-refused
Signalled when a publisher list would drop revocations a fresh install still needs; TARGETS names each one. Nothing was signed.
Generic functions
module-signing-refused-reason
(module-signing-refused-reason condition)
Undocumented: this exported symbol needs a docstring.
publisher-list-drop-refused-targets
(publisher-list-drop-refused-targets condition)
Undocumented: this exported symbol needs a docstring.
signing-custody-did
(signing-custody-did custody)
The did:key of the key CUSTODY signs with.
signing-custody-sign
(signing-custody-sign custody octets)
The 64-octet Ed25519 signature CUSTODY makes over OCTETS with the key signing-custody-did names.
Functions
sign-module
(sign-module custody directory delegation)
Through CUSTODY, sign the module whose bytes DIRECTORY holds and write the vouch into DIRECTORY as its bundled publisher vouch file. The identity is the one module-directory-identity computes, over the files module-directory-files selects, exactly as an install computes it; CUSTODY signs a grant of :admit over that identity's designation, chained through DELEGATION, the creating identity's delegation to CUSTODY's key. Returns (values vouch designation identity), VOUCH the token-chain: text written. Signals module-directory-refused on anything an install would refuse to hash, and module-signing-refused when DELEGATION is not to CUSTODY's key or has expired, the vouch name is taken by something other than a file, or the module holds version-control metadata in a .git directory at any depth, all before anything is signed, or when the signed vouch does not verify for the module, in which case nothing is written.
sign-publisher-list
(sign-publisher-list custody entries previous-list-text target-expiries &key sequence (issued-at (unix-now)))
Sign a publisher list naming ENTRIES, each (STATE . TARGET), as the creating identity CUSTODY holds, which is always the list's issuer, and return its publisher-list: text. PREVIOUS-LIST-TEXT is the list this one follows, NIL for a publisher's first. SEQUENCE defaults to one past the previous list's, or 1, and must exceed it. TARGET-EXPIRIES pairs each target the publisher has revoked with the Unix time after which it can no longer verify for a fresh install. Signals publisher-list-drop-refused, naming each target, when the previous list revokes a target that ENTRIES no longer revokes and its expiry has not passed or was never recorded, and module-signing-refused when the previous list's signature does not verify under CUSTODY's key, the previous list has another issuer, or SEQUENCE does not advance; nothing is signed.
Package valis/src/plugin/module-source
Conditions
module-identity-directory-conflict
Signalled when an identity is landed and its directory already exists holding other bytes. The directory is left exactly as it was.
module-landing-refused
Signalled when bytes cannot be staged, or staged bytes cannot land: a source that cannot be copied safely, staging that is not this node's, or bytes that are not the identity asked for. Nothing was landed.
module-request-refused
What request-module returns when a requested module was not admitted: nothing was loaded. request-module returns it rather than signalling it, so a caller asking for several modules goes on past one that is refused; it is an error so that a caller that cannot go on without the module can signal it as it is.
Generic functions
module-identity-directory-conflict-designation
(module-identity-directory-conflict-designation condition)
Undocumented: this exported symbol needs a docstring.
module-identity-directory-conflict-directory
(module-identity-directory-conflict-directory condition)
Undocumented: this exported symbol needs a docstring.
module-landing-refused-designation
(module-landing-refused-designation condition)
Undocumented: this exported symbol needs a docstring.
module-landing-refused-directory
(module-landing-refused-directory condition)
Undocumented: this exported symbol needs a docstring.
module-landing-refused-kind
(module-landing-refused-kind condition)
Undocumented: this exported symbol needs a docstring.
module-landing-refused-reason
(module-landing-refused-reason condition)
Undocumented: this exported symbol needs a docstring.
module-request-refused-cause
(module-request-refused-cause condition)
Undocumented: this exported symbol needs a docstring.
module-request-refused-designation
(module-request-refused-designation condition)
Undocumented: this exported symbol needs a docstring.
module-request-refused-reason
(module-request-refused-reason condition)
Undocumented: this exported symbol needs a docstring.
Functions
discard-staged-module
(discard-staged-module staging-directory &key source-root)
Remove STAGING-DIRECTORY, staged under SOURCE-ROOT (the configured module source root when NIL) and not landed, and let go of the lock this image holds on it. The signer stages a release to sign a private copy and lands nothing, so it hands its staging back here however signing ends. Signals module-landing-refused, removing nothing, when the directory is not staged under the root or another landing holds its lock, and module-install-refused for a root install would refuse. Even then the lock this image held is let go, so the next landing's sweep removes the staging.
land-module-bytes
(land-module-bytes staging-directory expected-identity &key source-root (store *admit-vouch-store*))
Land the bytes in STAGING-DIRECTORY, staged under SOURCE-ROOT (the configured module source root when NIL), as the module EXPECTED-IDENTITY names, in that identity's directory directly under the root, and file into STORE the publisher vouch shipped with them at bundled-publisher-vouch-file-name, once it verifies for that identity and, when a vouch that still verifies is already held, is rooted at the same creating identity. The identity is taken over the files module-directory-files selects, as install takes it. An identity directory is written once: when it is absent the staging is renamed into place whole; when it is present and holds those bytes it is left untouched and the vouch is filed from the staging; when it holds other bytes the landing is refused. Returns (values identity-directory status designation), STATUS :landed or :already-present. The landing holds the staging's lock throughout, and removes staging no landing holds before it looks at the bytes. The staging handed in is removed on every exit unless it became the identity directory. It is left untouched in three cases, since in none of them is it this landing's to remove: when SOURCE-ROOT names a root that install would refuse, when the directory is not staged under the root, and when another landing holds its lock. Signals module-install-refused for such a root; module-landing-refused for such a directory, an unreadable staging directory, want of a vouch store, an EXPECTED-IDENTITY that is not a module identity, or bytes that are not it; module-directory-refused for a staged link or other unsafe entry; module-identity-directory-conflict for a directory holding other bytes; and bundled-publisher-vouch-refused when the bytes carry no vouch or one that may not be filed, in which case nothing is filed and a freshly landed directory stays, inert, for a later vouched landing.
request-module
(request-module name identity &key source source-root (store *admit-vouch-store*) owner-credential loader allow-local-repository)
Bring the module IDENTITY names onto this node and insert it into the running image, loading the system NAME from its identity directory under SOURCE-ROOT (the configured module source root when NIL). IDENTITY is the module identity from the publisher's signed statement. I take the identity in the request so that a name can never choose bytes: NAME only says which system to load, and there is no name index and no notion of a latest version. SOURCE says where bytes may be found: NIL for bytes already here, (:directory release-directory) for a release from the dist, or (:git repository tag) for a third party's tagged release, fetched as stage-module-from-git-tag fetches it, from a local repository only when ALLOW-LOCAL-REPOSITORY. I let a tag, or a dist location, only say where bytes are: whatever a source holds lands only when it is IDENTITY, and it is admitted only on a publisher vouch over that identity and the owner's grant, offered as OWNER-CREDENTIAL or found by install. Given a source, the bytes are always staged and landed, even when their identity directory is present, since the source may carry a vouch this node lacks. STORE is the vouch store; LOADER is install's test seam. Returns (values :admitted identity-directory designation attached), or (values :refused refusal designation), REFUSAL a module-request-refused, when nothing was loaded; admission, not the request, records what admitted the module. An admitted module that registered an edge adapter answers on the running edge before this returns, with no restart, and the host agent is told the widened set, as attach-edge-adapter tells it; ATTACHED says what became of each such adapter, as %attach-installed-edge-adapters answers it. Signals module-install-refused, before anything is created, for a source root install would refuse.
stage-module-from-directory
(stage-module-from-directory source-directory &key source-root unshared (apart-from nil apart-from-p))
Copy the module whose release is SOURCE-DIRECTORY, an absolute directory such as a release unpacked from the dist, whole, into a fresh private staging directory under SOURCE-ROOT (the configured module source root when NIL), and return that staging directory for land-module-bytes. The staging stays locked by this image until its landing ends, so no other landing's sweep removes it meanwhile. The publisher vouch shipped at the top of the release is copied with it. With UNSHARED, as the signer asks, a release holding anything others can write, or a file with a second name, is refused, since its bytes could change after they are signed. With APART-FROM, a list of directories holding the node's keys or data, a release under one of them or under the source root, or holding one, is refused, judged by the directory opened and staged rather than by its path. Signals module-install-refused for a root install would refuse, before anything is written, and module-landing-refused, leaving nothing staged, for a source holding a link or anything else that cannot be copied safely.
stage-module-from-git-tag
(stage-module-from-git-tag repository tag &key source-root allow-local-repository)
Fetch the tree the tag TAG names in the git repository REPOSITORY and copy it whole into a fresh private staging directory under SOURCE-ROOT (the configured module source root when NIL), returning that staging directory for land-module-bytes, exactly as stage-module-from-directory does. Only a tag is fetched, never a branch, a commit or HEAD, and the tree staged is the tagged tree byte for byte, the publisher vouch it carries included. A tag can be moved or mistyped, so I let it say only where a release is, never what it is: nothing here names an identity, and land-module-bytes refuses bytes that are not the one asked for. REPOSITORY is fetched from over https or ssh only, and from a local path or a file URL only when ALLOW-LOCAL-REPOSITORY is true; git is allowed no other transport. A fetch runs git itself, tar, and what git runs to reach a repository over those transports: its own https helper, /bin/sh, which git runs the ssh command through because that command holds spaces, the ssh found on the search path, and for a local repository its own upload-pack; no program named by a URL scheme, a transport helper or an option runs. That list is not closed, because ssh also runs any program the running account's ssh configuration names, such as a ProxyCommand or a Match exec. An ssh fetch is made as the account running valis makes any ssh connection: that account's ssh configuration and known hosts, and those under /etc/ssh, apply to it. Ssh finds the account's home in the password database, not in $HOME, so the scratch home a fetch gives git does not keep that configuration or those known hosts out. Ssh runs in batch mode, so it never asks for a password or to accept a host key it does not know; but an account whose ssh configuration sets StrictHostKeyChecking=accept-new has an unknown host key accepted without asking, and one that sets StrictHostKeyChecking=no has an unknown or a changed host key accepted without asking. The whole fetch must finish within module-fetch-time-limit seconds. A user or password written before the host in REPOSITORY, a URL or an scp-style location, is never shown in the location a refusal names, percent-encoded or not, since it is found there by where it sits. In what git says, which a refusal quotes, only the user, the password and the two together are replaced, and only as REPOSITORY writes them, so one git quotes in another encoding is shown. A token in a query string is shown. Signals module-install-refused for a root install would refuse, before anything is written, and module-landing-refused, leaving nothing staged, nothing fetched and nothing running behind, for a tag or repository git could take as an option or a program to run, a repository reached over a transport not allowed, a tag git would not fetch, a fetch git refuses, whose objects git's own object checks reject, or that runs out of time, a tree holding more than module-export-entry-limit entries or module-export-byte-limit bytes, or a tree holding a link or anything else that cannot be copied safely.
Variables
*module-export-byte-limit*
The most bytes the files of a tree fetched from git may hold together. A tree past it is refused before it is unpacked, so a release cannot fill the disk the node stages on.
*module-export-entry-limit*
The most entries, files and directories alike, a tree fetched from git may hold. A tree past it is refused before it is unpacked, so a release cannot exhaust the inodes the node stages on or the time spent copying it.
*module-fetch-scratch-parent*
The directory a release fetched from git is unpacked in before it is staged, or NIL for the system's temporary directory. It must lie outside the module source root, since everything under the root's staging directory that no landing holds is swept away.
*module-fetch-time-limit*
Seconds a fetch of a release from git may take, from its first git process to its tree being unpacked, before it is refused and every process it started is killed. A remote that stops answering would otherwise hold a request, and the directory it unpacks in, for as long as the connection stays open.
Package valis/src/plugin/module-supersede
Classes
module-supersede-result
What became of a supersede.
STATUS is :tentative when the new version loaded, :refused when nothing of it was compiled here, or :load-failed when some of it may have been compiled and the load did not finish, so the image now holds part of it. A supersede is never more than tentative, because only a restart into the new version shows the old one gone.
REASON is NIL for a tentative supersede. For a refusal it is :not-carried, :same-identity, :install-refused, :restart-required, :dry-run-failed, :dry-run-unavailable or :change-in-progress, and for a failed load it is :load-failed. On a node serving real names there is no result: supersede-module signals module-install-refused-on-serving-node instead, before anything is compared or started. MESSAGE is a sentence for the owner and the log.
CHANGES holds the module-change entries the comparison found. When the comparison refused, they are the changes that need a restart. Otherwise they are the changes it only states, such as a function or method the new version omits, which stays in the image.
REDEFINED names what the new version redefined in the dry run. RESIDUE is what the residue scan found alive of the old version, a report that can be incomplete, or :unscanned when the scan itself failed. IDENTITY and OLD-IDENTITY are the new and old versions' identities, NIL when not computed. LOADER-RAN is true when any of the new version may have been compiled into this image. RECORDED is true when the module manifest now names the new version, so a restart stands it up; when it is NIL, a restart returns to whatever the manifest names.
Functions
module-supersede-result-changes
(module-supersede-result-changes instance)
Undocumented: this exported symbol needs a docstring.
module-supersede-result-identity
(module-supersede-result-identity instance)
Undocumented: this exported symbol needs a docstring.
module-supersede-result-loader-ran
(module-supersede-result-loader-ran instance)
Undocumented: this exported symbol needs a docstring.
module-supersede-result-message
(module-supersede-result-message instance)
Undocumented: this exported symbol needs a docstring.
module-supersede-result-old-identity
(module-supersede-result-old-identity instance)
Undocumented: this exported symbol needs a docstring.
module-supersede-result-reason
(module-supersede-result-reason instance)
Undocumented: this exported symbol needs a docstring.
module-supersede-result-recorded
(module-supersede-result-recorded instance)
Undocumented: this exported symbol needs a docstring.
module-supersede-result-redefined
(module-supersede-result-redefined instance)
Undocumented: this exported symbol needs a docstring.
module-supersede-result-residue
(module-supersede-result-residue instance)
Undocumented: this exported symbol needs a docstring.
module-supersede-result-status
(module-supersede-result-status instance)
Undocumented: this exported symbol needs a docstring.
supersede-module
(supersede-module system new-identity-directory &key source-root (store *admit-vouch-store*) (manifest-store (durable-manifest-store)) (declared-names (function resolve-edge-domains)) (owned-zones (function owned-zone-origins)))
Replace the version of the module SYSTEM this image carries with the one in NEW-IDENTITY-DIRECTORY, in place, and return a module-supersede-result. The new directory is checked as an install checks it, under SOURCE-ROOT, and the new version must carry its publisher's vouch and the owner's signature over its own identity in STORE. MANIFEST-STORE, DECLARED-NAMES and OWNED-ZONES are as for install-module, and default the same way.
The steps run in this order, and nothing of the new version is compiled into this image before the last of them. The new directory is checked. When a record would be written and this node serves real names, module-install-refused-on-serving-node is signalled. The version the image carries is found: an admitted module whose loaded directory, admission record and manifest record agree, and none that an earlier supersede left partly loaded, which needs a restart. The bytes of the two versions are compared, and a change which a running image cannot make in place is refused as restart required. A dry run in another process loads the old version and then the new one. Only then does install-module, which checks both signatures, admit and load the new version from its own identity directory and record it in place of the old one; the old version's directory is left as it was. The whole of it holds the module change lock, so no other install or supersede loads module code meanwhile.
The module's edge adapter, listener and ports are not touched and nothing is announced: the code they call changes at its next call. The image's divergence record gets a :superseded entry before install-module is called, and the entry is completed with the outcome: tentative with the residue the scan reported, :load-failed when the load did not finish or a condition escaped it after the loader ran, or :not-loaded when install-module refused before compiling anything, which image-diverged-p does not count. A condition install-module signals before its loader runs, such as module-install-refused-on-serving-node, reaches the caller as it is. When another module change holds the lock for longer than module-change-lock-timeout seconds, the supersede is refused as :change-in-progress with nothing done. A supersede that loads is :tentative, always, because code the old version built can go on running unseen and only a restart into the new version shows it gone.
Package valis/src/plugin/publisher-signing-custody
Classes
creating-identity-custody
A signing custody that signs as the publisher's creating identity, derived from the node's own seed each time it signs. It holds only the node's custody store.
mercer-signing-custody
A signing custody that signs as the publisher's subordinate signing key, which mercer holds. It holds only mercer's handle.
Conditions
retired-delegation-unreadable
Signalled when mercer reports a retired delegation whose revocation hash is not 64 lowercase hex characters, so no revocation target can be named for it. Nothing is returned for any retired delegation.
signing-delegation-unreadable
Signalled when mercer hands back a signing delegation that is not "token:" wire text, so what follows cannot be taken for a token. No delegation is decoded or reported.
Generic functions
mercer-signing-custody-store
(mercer-signing-custody-store object)
Undocumented: this exported symbol needs a docstring.
retired-delegation-unreadable-reason
(retired-delegation-unreadable-reason condition)
Undocumented: this exported symbol needs a docstring.
signing-delegation-unreadable-reason
(signing-delegation-unreadable-reason condition)
Undocumented: this exported symbol needs a docstring.
Functions
current-signing-delegation
(current-signing-delegation mercer)
The delegation from the creating identity to MERCER's current signing key, as the capability token sign-module takes, or NIL when that key has none. Signals signing-delegation-unreadable when what mercer holds is not "token:" wire text.
delegate-signing-key
(delegate-signing-key creating mercer expiry)
Have CREATING, the creating identity's custody, delegate :delegate and :admit over /module to MERCER's current signing key until EXPIRY, a Unix time, and have mercer store the delegation. Returns (values hash expiry): HASH is the 32-octet revocation hash that revoking the delegation takes, EXPIRY the delegation's own. mercer checks the signature against the creating identity before storing anything, and signals publisher-custody-error when it does not verify, the key is absent or replaced meanwhile, or the expiry has passed. Signals signing-delegation-unreadable when mercer hands back anything but "token:" wire text.
make-creating-identity-custody
(make-creating-identity-custody store)
A signing custody over the creating identity derived from STORE, a valis custody store.
open-mercer-signing-custody
(open-mercer-signing-custody &key directory)
A signing custody over mercer's publisher store at DIRECTORY, or at mercer's own default when DIRECTORY is NIL. mercer creates the store if it is absent and refuses one it cannot trust, signalling publisher-custody-error.
retired-signing-delegations
(retired-signing-delegations mercer)
The delegations MERCER keeps for signing keys it has retired, oldest first, each a plist of :HASH, the 32-octet revocation hash a revoke or a publisher list names, :SIGNING-KEY-DID, the retired key, and :EXPIRY, the delegation's Unix expiry. NIL when no retired key had a delegation.
Package valis/src/plugin/social-admission
Functions
query-reachable
(query-reachable vouch start-did &rest traversal-args)
The /social-gated entry point to the social-graph traversal: verify VOUCH grants :read over /social, and only then run the recursive-CTE traversal from START-DID (forwarding TRAVERSAL-ARGS, e.g. :rel-type, :max-depth, to traverse-reachable). Returns the reachable-DID list on success; (values nil reason-string) when the vouch does not authorize the read.
This is the ONLY caller-facing entry to the UNAUTHENTICATED traverse-reachable primitive. Fail-closed: without a valid /social :read vouch the traversal never runs: the raw primitive is never reached, so a refused query touches no graph data.
verify-social-vouch
(verify-social-vouch vouch)
Decide whether VOUCH authorizes a social-graph read. Returns (values t nil) when an owner-rooted, unrevoked, unfenced authority grants :read over exactly the /social designation; otherwise (values nil reason-string).
VOUCH is the held bearer credential, in any of the carrier forms the cap verify path accepts: a capability-token object, a "valis:""name:" bearer name string, or a "token:" serialized-token string. The trust check rides verify-token-chain / verify-capability-name-sig against the owner root and the shared revocation store: no parallel trust path, no second revocation check.
Fail-closed: any signalled condition becomes (values nil "<message>"); a refusal always carries a reason and never a bare nil, and no signal escapes. An absent (nil) vouch is refused fail-closed: the query is never ungated.
Variables
+social-designation+
The dedicated capability designation that gates social-graph reads. A /social :read grant is the sovereignty boundary for the social axis: independently grantable and revocable, never an ambient operator-wide power and never piggybacked on a /proto or /names grant. The gate matches this designation EXACTLY (string=, not a prefix), so no broader subtree grant can satisfy it.