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. OWNER is the admission of the module whose load registered it, the modularize module that declared it outside any admission, :node for an active service the node registers for itself, or NIL when no owner can be named. Only a service marked :node that cannot start is the node's fault; any other active module that cannot start is a module's, and every part registered under the same OWNER is retired with it when it has one.
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-owner
(active-module-spec-owner 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 owner (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. OWNER is the modularize module that declared it, which the :active-module option supplies. While a module is being admitted the owner is that module's admission whatever OWNER names, so every part a module registers as it loads, from any of its packages, is that module's. Outside any admission, an OWNER left out is NIL, an owner nobody can name, whose failure to start is still a module's fault and never the node's. An OWNER of :node is refused with node-owner-claimed-by-module, during an admission or not: only the node marks a service as its own, through its own entry point, since a part marked that way ends the boot when it cannot start. 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. What the module registers in the edge, DNS, mail and active-module registries on the loading thread while it loads, from whichever of its packages, is owned by this admission, so the module can later be taken out of service whole and never by another module's fault; a registration made on a thread the module starts is not owned by it, and a part it marks as the node's own refuses 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 other error becomes a refusal, so this chokepoint is fail-closed, except a vouch-store-error, such as a store that cannot be written: that is a fault of the node's store and not of the module, so it is signalled to the caller. 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 source-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 SOURCE-ROOT, the module source root, is found by name; each admission names the one identity directory it admits. A file beneath SOURCE-ROOT compiles beneath COMPILED-ROOT, keyed by its path below the root, so every identity directory compiles to a place of its own. Any other file compiled from here on, such as a library a module needs that the image does not hold and the dependency dist supplies, compiles beneath the deps/ directory of COMPILED-ROOT, keyed by its whole path, so a node never writes into its run account's home. The systems a sealed image holds are never planned, so none of them compiles anywhere. SOURCE-ROOT may be NIL when no root is configured, and then no module can be found to compile. 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.
forget-module-system
(forget-module-system system)
Forget SYSTEM and the systems named under it (SYSTEM/…), when ASDF knows SYSTEM, so a later load of that name really loads and nothing can be built against what ASDF held for it. A system SYSTEM is itself named under, such as foo when SYSTEM is foo/bar, is left alone, since it can be another module that is admitted and running. A module system is also a modularize virtual module, which another system can name as a dependency, so that entry goes too.
forget-registered-system
(forget-registered-system name)
Forget the one system ASDF has registered as NAME, and the modularize virtual module filed for it, so a later load of that name really loads. Nothing named under NAME is touched.
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.
seal-loaded-systems
(seal-loaded-systems)
Freeze every system this image has loaded, ASDF and UIOP always among them, forget their build details, and drop the build host's source registry and central registry. Afterwards ASDF answers a frozen system from the copy the image holds and never plans or compiles it, so a module loaded later compiles only its own files. A system the image has not loaded is still looked for, in whatever configuration the image is given later, and the system search functions are left in place. Returns the number of systems frozen. Run it once, last, before saving an image; it changes the whole image and cannot be undone in it.
Package valis/src/plugin/condense-modules
Conditions
module-refused-at-boot
One recorded module could not be stood back up: its bytes could not be reached, they no longer match the recorded identity, a signature it needs is missing, or the admission gate or its load refused it. It is signalled with the leave-module-out restart in effect, so a caller that will run the node without that module says so by invoking it. When the module got as far as loading, what its load put in this image is taken back before this is signalled: the edge, DNS, mail and active-module registries go back to what they held before it, the settings it declared are dropped, the modularize modules it made are forgotten, the packages it made are deleted when they are named for its primary system or belong to one of those modules, and ASDF forgets its system, the systems named under it, and every system its load registered that is named for its primary system. A package or system it made under another name, and anything else its code did while it loaded, is not undone. A module refused before it loaded ran no code, and nothing is taken back for it. A caller that does not invoke the restart gets the whole set refused.
module-unreachable
Signalled during Pass 2 when a record's source cannot be materialized at the destination, cannot be read there, 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, which refuses every record. Left unhandled it aborts the whole condensation before any admit-module call; a caller that invokes leave-module-out refuses this record alone.
Generic functions
module-refused-at-boot-reason
(module-refused-at-boot-reason condition)
Undocumented: this exported symbol needs a docstring.
module-refused-at-boot-system
(module-refused-at-boot-system condition)
Undocumented: this exported symbol needs a docstring.
module-unreachable-cause
(module-unreachable-cause condition)
Undocumented: this exported symbol needs a docstring.
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, judging each recorded module on its own.
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 is refused, 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.
Every record that cannot be stood up signals module-refused-at-boot, naming it and why, with the leave-module-out restart in effect. A caller that invokes it runs without that module and the rest of the set is still admitted. A module refused once it got as far as loading has, before the signal, its registry entries put back as they were just before its load, its modularize modules and the packages named for its primary system deleted, and ASDF's record of the systems its load registered under that name forgotten, as module-refused-at-boot describes; one refused before it loaded ran no code and has nothing taken back. That is how a node boots, so one broken module never keeps the others, or the node, down. A caller that lets the condition pass refuses the whole set: (values nil reason) for a refusal, with the edge, DNS, mail and active-module registries exactly as they were on entry, or module-unreachable signalled when a record cannot be reached or re-derived. A corrupt manifest names no record that could be judged alone, so it always refuses the whole set.
Returns (values count loaded-systems refused): the count of admitted modules, their system-names and the (system-name . reason) of each module left out, both in canonical order. A manifest holding only resident records returns (values 0 nil nil).
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; what a refused module's load changed in the image is taken back before its refusal is signalled.
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.
leave-module-out
(leave-module-out condition)
Refuse the one recorded module CONDITION, a module-refused-at-boot, names, and go on standing up the rest of the set. Use it from a handler for module-refused-at-boot.
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). OWNER is the admission of the module whose load registered it, the modularize module that declared it outside any admission, or NIL when no owner can be named; a module taken out of service takes this adapter with it.
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-owner
(dns-adapter-spec-owner 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 owner (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. OWNER is the modularize module that declared it, which the :dns-adapter option supplies; while a module is being admitted the owner is that module's admission instead, so an adapter registered from any of its packages is that module's. A module being admitted may not take over a name another owner holds: that signals registration-name-held and registers nothing, since the holder's service would go dark once the module was retired. 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). OWNER is the admission of the module whose load registered it, the modularize module that declared it outside any admission, or NIL when no owner can be named; a module taken out of service takes this adapter with it.
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-adapter-spec-owner
(edge-adapter-spec-owner 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 owner)
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. A module being admitted may not take over a name another owner holds: that signals registration-name-held and registers nothing, since the holder's service would go dark once the module was retired. CONSTRUCTOR is a function of (&key port budget). OWNER is the modularize module that declared it, which the :edge-adapter option supplies; while a module is being admitted the owner is that module's admission instead, so an adapter registered from any of its packages is that module's. 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's name and the targets to try, in order, that speaks the wire and returns one of :delivered / :deferred / :bounced and mutates nothing else; the driver, not the adapter, records the disposition.
DELIVER also accepts the keyword argument :DIAL-CAPABILITY. When it carries a dial capability, the targets are a destination the owner configured (the primary exchanger a backup exchanger forwards to), and the adapter dials them through that capability, never through its own grant. The capability reaches those targets and nothing else, and only until DELIVER returns: keep it and it refuses. With no capability, the targets came from somebody else's records and the adapter dials them under its own grant. Name :DIAL-CAPABILITY in the lambda list. A DELIVER whose lambda list rejects the keyword signals on every forward, so the drain holds the message for a later attempt and logs why. One that takes &REST or &ALLOW-OTHER-KEYS without naming it accepts the keyword silently and dials the forward under its own grant, where a primary on a private network is refused.
Inbound landing is the valis-exported LAND-MESSAGE the adapter calls into, so the spec carries no inbound-handler slot. OWNER is the admission of the module whose load registered it, the modularize module that declared it outside any admission, or NIL when no owner can be named; a module taken out of service takes this adapter with it.
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-adapter-spec-owner
(mail-adapter-spec-owner 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 owner)
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 keeps the contract MAIL-ADAPTER-SPEC states, :DIAL-CAPABILITY included, and returns a disposition keyword. OWNER is the modularize module that declared it, which the :mail-adapter option supplies; while a module is being admitted the owner is that module's admission instead, so an adapter registered from any of its packages is that module's. 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-custodian
Classes
handed-descriptor
A descriptor handed to a module through one of its capabilities. FD is its number and IDENTITY the device and inode of the file it referred to when it was handed out, so that a number the module has since closed and the node has reused is never touched. CLOSER, when not NIL, closes the object the module was handed in place of a bare close of FD. STATE is :open, :shut once it has been shut down, :closed once it was closed, or :gone once it no longer refers to what was handed out.
module-custodian
Everything the node handed one module admission, held so that it can be taken back. SYSTEM is the name of the ASDF system that admission loaded, or NIL for a custodian kept for anything else, so whatever is handed out under it can say which module it was handed to. STATE is :live while the module may be granted more, :retiring once its retirement has begun and nothing more is handed to it, and :retired or :retired-with-survivors once retirement has finished, the second when something the module ran was still running at the end. GRANTS holds one module-grant for each start of one of its active parts, newest first. THREADS are the threads known to have been started by the module's code, those since ended dropped as more are added. CANCELLED is NIL until the module is asked to end its work, and true from then on.
module-grant
The authority one start of an active part was handed. PART is what started, HASH the hash of the owner's grant token it was minted as, which is what the owner names to withdraw it, or NIL when there was none. REVOKED is NIL while the grant holds and the reason it was revoked once it does not. CAPABILITIES are the capability objects built under it, and DESCRIPTORS the handed-descriptor records of what was handed out through them, newest first.
Conditions
module-retired
A part asked to be granted authority by a module whose retirement has begun. Nothing more is handed to a module once it is being retired, so a thread it left running cannot start it again.
Generic functions
module-retired-part
(module-retired-part condition)
Undocumented: this exported symbol needs a docstring.
Functions
close-handed-descriptors
(close-handed-descriptors grants)
Close every descriptor handed out under GRANTS that is not yet closed and still refers to what was handed out. Retirement never calls this. A closed number can be reused at once, and until every thread a module runs is known, nothing can say that no thread of it still holds one; a descriptor that has been shut down is harmless to whoever holds it, so a retired module's are left shut down and open for a restart to reclaim. Returns the records closed.
count-descriptors-left-open
(count-descriptors-left-open grants)
How many descriptors handed out under GRANTS are still open: once retirement has shut them down, those a retired module leaves behind until a restart. One the module has since closed itself is not counted.
custodian-grants-for
(custodian-grants-for custodian part)
The grants CUSTODIAN opened for PART, newest first.
grant-revoked-p
(grant-revoked-p grant)
True once GRANT has been revoked. One read of one slot, so a capability can ask it on every call.
grants-hold-open-descriptors-p
(grants-hold-open-descriptors-p grants)
True when a descriptor handed out under GRANTS is neither closed nor gone.
hand-out-descriptor
(hand-out-descriptor grant fd &key closer)
Record FD as handed out under GRANT and return the record, or, when GRANT has been revoked meanwhile, shut FD down, close it and return NIL, so nothing is handed out under a revoked grant. CLOSER closes the object being handed out in place of a bare close of FD. A capability calls this with interrupts disabled from the moment the call that opened FD returns until the record is made, so FD cannot reach its holder unrecorded. Closing FD here is safe because it has not yet reached anyone.
handed-descriptor-fd
(handed-descriptor-fd instance)
Undocumented: this exported symbol needs a docstring.
handed-descriptor-p
(handed-descriptor-p object)
Undocumented: this exported symbol needs a docstring.
handed-descriptor-state
(handed-descriptor-state instance)
Undocumented: this exported symbol needs a docstring.
make-module-custodian
(make-module-custodian &key system)
Undocumented: this exported symbol needs a docstring.
module-custodian-cancelled
(module-custodian-cancelled instance)
Undocumented: this exported symbol needs a docstring.
module-custodian-grants
(module-custodian-grants instance)
Undocumented: this exported symbol needs a docstring.
module-custodian-lock
(module-custodian-lock instance)
Undocumented: this exported symbol needs a docstring.
module-custodian-p
(module-custodian-p object)
Undocumented: this exported symbol needs a docstring.
module-custodian-state
(module-custodian-state instance)
Undocumented: this exported symbol needs a docstring.
module-custodian-system
(module-custodian-system instance)
Undocumented: this exported symbol needs a docstring.
module-custodian-threads
(module-custodian-threads instance)
Undocumented: this exported symbol needs a docstring.
module-grant-capabilities
(module-grant-capabilities instance)
Undocumented: this exported symbol needs a docstring.
module-grant-descriptors
(module-grant-descriptors instance)
Undocumented: this exported symbol needs a docstring.
module-grant-hash
(module-grant-hash instance)
Undocumented: this exported symbol needs a docstring.
module-grant-p
(module-grant-p object)
Undocumented: this exported symbol needs a docstring.
module-grant-part
(module-grant-part instance)
Undocumented: this exported symbol needs a docstring.
module-grant-revoked
(module-grant-revoked instance)
Undocumented: this exported symbol needs a docstring.
module-grant-system
(module-grant-system grant)
The name of the ASDF system whose admission GRANT was opened for, or NIL when GRANT was opened on a custodian kept for anything other than a module admission.
note-grant-capabilities
(note-grant-capabilities grant capabilities)
Record CAPABILITIES, capability objects built under GRANT, so retirement can find them. Returns GRANT.
open-module-grant
(open-module-grant custodian part hash)
Open and return a new grant on CUSTODIAN for one start of PART, minted as the owner's token whose hash is HASH. Signals module-retired when CUSTODIAN is no longer live.
prune-settled-grants
(prune-settled-grants custodian &key part (keep (constantly nil)))
Forget CUSTODIAN's revoked grants, only PART's when PART is given, that hold no descriptor left open and that KEEP, a function of a grant, does not ask to keep, so a part started and stopped many times does not grow its custodian without bound. A grant holding a descriptor left open is never forgotten, so what retirement left behind can still be counted.
revoke-custodian
(revoke-custodian custodian &optional (reason :revoked))
Begin retiring CUSTODIAN's module: nothing more is granted to it and every grant it holds is revoked. Returns its grants.
revoke-grant
(revoke-grant grant &optional (reason :revoked))
Revoke GRANT, so every capability built under it refuses from now on and nothing more is handed out through it. Revoking a grant twice keeps the first reason. Returns GRANT.
settle-custodian
(settle-custodian custodian survivors-p)
Finish retiring CUSTODIAN's module, as :retired-with-survivors when SURVIVORS-P says something it ran is still running and as :retired otherwise. Returns the state reached.
shut-down-handed-descriptors
(shut-down-handed-descriptors grants)
Shut down, in both directions, every descriptor handed out under GRANTS that is still open and still refers to what was handed out. A thread of the module blocked reading, writing or accepting on one wakes, and the number stays allocated, so nothing else can be given it while that thread may still use it. Returns the records shut down.
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-ownership
Classes
module-admission
One module being, or having been, admitted and loaded. SYSTEM is the name of the ASDF system admission loaded. MODULES is the list of modularize modules that load made, filled in once the load returns. CUSTODIAN holds what the node handed the module's active parts, so it can be taken back when the module is retired. Everything registered on the loading thread while it loaded is owned by this object, so two admissions of the same system are two owners.
Conditions
node-owner-claimed-by-module
A part was registered with the owner :node from outside the node's own entry point, or from inside it while a module was being admitted. A part marked that way ends the boot when it cannot start, so a module claiming it could hold the whole node down with its own failure; the claim is refused instead, and when a module is loading, that fails its load and the module is refused.
registration-name-held
A module being admitted asked to register an edge adapter, DNS adapter or active part under a name another owner already holds. Taking it over would leave that owner's service dark once the module was retired, so the registration is refused, which fails the module's load and refuses the module; the holder goes on answering.
Generic functions
node-owner-claimed-by-module-system
(node-owner-claimed-by-module-system condition)
Undocumented: this exported symbol needs a docstring.
registration-name-held-holder
(registration-name-held-holder condition)
Undocumented: this exported symbol needs a docstring.
registration-name-held-kind
(registration-name-held-kind condition)
Undocumented: this exported symbol needs a docstring.
registration-name-held-name
(registration-name-held-name condition)
Undocumented: this exported symbol needs a docstring.
registration-name-held-system
(registration-name-held-system condition)
Undocumented: this exported symbol needs a docstring.
Functions
call-with-module-admission
(call-with-module-admission system thunk)
Call THUNK, which loads the admitted module SYSTEM, with that module's admission as the owner of every registration made on this thread while THUNK runs. A registration made on another thread, including one the module's own load starts, is not owned by the admission: it is recorded under the owner it names, as one made outside any admission is. Returns THUNK's values. Every thread a bordeaux-threads make-thread starts during the load is tagged as the module's, and once THUNK returns or unwinds, the admission records the modularize modules made during the load, and the threads that appeared during it are recorded against the admission, so a later retirement can wait for them and name those still running. When THUNK unwinds, the module will not run, so it is granted nothing more and its threads are asked to end.
call-with-registering-owner
(call-with-registering-owner owner thunk)
Call THUNK, which runs code of the module OWNER on this thread, such as an active part's :start, and return its values. When OWNER is a module's admission, every registration made on this thread meanwhile is owned by it and held to the same rule as one made while it loaded: it may not take a name another owner holds. A thread that code starts is not held to it. With any other OWNER, THUNK is simply called.
current-module-admission
(current-module-admission)
The module-admission whose load is under way, or NIL when no module is being admitted.
describe-registration-owner
(describe-registration-owner owner)
OWNER, the owner a part is registered under, named as a reader would: the module an admission loaded, the modularize module that declared it, the node, or a part registered outside any module.
describe-threads-left-running
(describe-threads-left-running threads)
THREADS named for a reader on one line, each distinct thread name with how many of THREADS carry it, followed by survivor-restart-offer, such as "threads left running: poller (2), sweeper (1); a restart of the node clears them…", or NIL when THREADS is empty. They are named, never stopped: stopping a thread from outside can leave whatever it held half changed, and a restart clears them.
image-threads
(image-threads)
Every thread in this image now.
module-admission-custodian
(module-admission-custodian instance)
Undocumented: this exported symbol needs a docstring.
module-admission-modules
(module-admission-modules instance)
Undocumented: this exported symbol needs a docstring.
module-admission-p
(module-admission-p object)
Undocumented: this exported symbol needs a docstring.
module-admission-system
(module-admission-system instance)
Undocumented: this exported symbol needs a docstring.
node-thread-p
(node-thread-p thread)
True when THREAD carries a name the node gives a thread of its own, or is the logging library's file watcher, which the node starts once and never as a module's.
note-owner-threads
(note-owner-threads owner threads)
Record THREADS, which appeared while OWNER's code ran, with the custodian of OWNER, so a retirement of OWNER can wait for them and name those still running. An owner of NIL records nothing, since no retirement can name it.
owner-custodian
(owner-custodian owner)
The custodian of what the node hands the parts registered under OWNER: an admission's own, so two admissions of one system never share one, one kept for any other owner, or the one kept for parts with no owner. A custodian retired stays retired, so a module taken out of service, whatever owns it, is granted nothing more until a restart.
owner-threads
(owner-threads owner)
Every thread still running that is known to be the module of OWNER's, as custodian-threads finds them for its custodian: those recorded, whether spawned through the node, tagged as they started, or seen to appear while its code ran, and every other thread whose binding names its custodian. An owner of NIL has none.
owner-threads-left-running
(owner-threads-left-running owner &optional more)
The threads known to be the module of OWNER's, as owner-threads finds them, together with MORE, a list of threads image-threads answered, that are still alive once threads-surviving-grace has given them time to finish, each once.
refuse-name-held-by-another-owner
(refuse-name-held-by-another-owner kind name present-p holder)
While a module is being admitted, signal registration-name-held when NAME, a KIND such as "edge adapter", is already registered, PRESENT-P, under HOLDER and HOLDER is not that module. A module registering its own name again is allowed: the same admission, as the compile and the load of one definition both do, or an earlier admission of the same system, as a supersede does. Outside any admission nothing is refused, since the node itself and a developer at the image register as they always have.
registration-owner
(registration-owner requested)
The owner a registration made now is recorded under. While a module is being admitted it is that module's admission, whatever REQUESTED names, because a module cannot register on behalf of another module; a REQUESTED of :node then signals node-owner-claimed-by-module. Outside any admission it is REQUESTED: the modularize module that declared the part, :node for a service of the node, or NIL when no owner can be named.
same-registration-owner-p
(same-registration-owner-p owner other)
True when OWNER and OTHER, two owners a part is registered under, are the same module: the same owner, or two admissions of the same system, as the compile and the load of one definition and a supersede of that module both are. A name, a port or a serve loop is that module's under either admission, so taking it over is no takeover and taking the module out of service takes it too.
threads-started-since
(threads-started-since before)
The threads alive now that are not in BEFORE, a list image-threads answered earlier, other than the calling thread and those the node names as its own. A module's own threads are told apart from the node's this way, by what appeared while its code ran, since nothing records which code started a thread; leaving out the node's own keeps a connection the edge accepted in that moment from being taken for the module's.
threads-surviving-grace
(threads-surviving-grace threads &key (grace *thread-exit-grace-seconds*))
Those of THREADS still alive after waiting up to GRACE seconds in all for them to finish, never past clock-stop-ceiling, returning as soon as none is alive. Nothing is joined or stopped: a thread that outlives the wait is left as it is.
Variables
*survivor-restart-offer*
What the node tells its owner about threads a retired module left running: that a restart clears them, and how to ask for one. I made the node say this wherever it names them, because a restart is the only thing that ends them safely, and the owner should not have to know that already.
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/module-threads
Conditions
module-cancelled
Signalled in a thread of a module that is being retired, at a wait the node provides or at a check for cancellation. It is a serious condition and not an error, so a handler for ERROR in the module's code lets it pass, and the thread unwinds to where the module handles it or ends. A handler for SERIOUS-CONDITION or T swallows it. A thread of the module's own whose code handles it nowhere ends where it waited, unwinding its own stack as any non-local exit does, so nothing it held is left half changed by it; any other thread is left running.
no-module-custodian
spawn-module-thread was asked to start a thread with no custodian to own it: the caller is not running a module's code and named none. Every thread a module starts through the node belongs to that module, so that retiring the module finds it.
Generic functions
module-cancelled-custodian
(module-cancelled-custodian condition)
Undocumented: this exported symbol needs a docstring.
no-module-custodian-name
(no-module-custodian-name condition)
Undocumented: this exported symbol needs a docstring.
Functions
call-with-custodian
(call-with-custodian custodian thunk)
Call THUNK, which runs code of the module of CUSTODIAN on this thread of the node's own, such as its load or one of its parts' :start, and return its values. Meanwhile CUSTODIAN is the current custodian, and every thread the bordeaux-threads make-thread of either version starts, and every thread those start with bordeaux-threads unless they pass bindings of their own, is tagged as the module's. This thread itself is not: it is the node's, and the module's cancellation never ends it. With a CUSTODIAN of NIL, THUNK is simply called.
cancel-custodian
(cancel-custodian custodian)
Ask every thread of the module of CUSTODIAN to end its work: from now on custodian-cancelled-p is true for it, and every cancel-aware wait in its threads signals module-cancelled within cancel-check-seconds. Returns CUSTODIAN.
check-cancelled
(check-cancelled &optional (custodian *current-custodian*))
Signal module-cancelled when the module of CUSTODIAN, by default the module whose code is running, has been asked to end its work, and otherwise return NIL. Letting it unwind ends a thread of the module's own; in any other thread, such as one of the node's running the module's code, a cancellation nothing took returns true instead, and the caller should end its work. A loop that waits on nothing the node provides calls this once each time round.
current-custodian
(current-custodian)
The custodian of the module whose code this thread runs, or NIL when this is a thread of the node's own. A module that means to start threads later from a thread the node gave it can keep this and pass it to spawn-module-thread.
custodian-cancelled-p
(custodian-cancelled-p &optional (custodian *current-custodian*))
True once the module of CUSTODIAN, by default the module whose code is running, has been asked to end its work. This only answers: a loop that asks it must return when it is true. False when no custodian is current, as in a thread of the node's own.
custodian-threads
(custodian-threads custodian &key (exclude (constantly nil)))
Every thread still running that is known to be the module of CUSTODIAN's: those recorded against it, and every other thread whose binding names it, which is recorded from then on unless it carries a name the node gives its own threads or EXCLUDE, a function of a thread, says it is not the module's. The calling thread is never among them. A thread started at the very moment of the search may not yet carry its binding, and is missed by it; a retirement searches again after it has waited.
join-threads-until
(join-threads-until threads deadline)
Wait until DEADLINE, an internal real time, for each of THREADS to end, and return those still running then. Nothing is stopped: a thread that outlives the wait is left as it is. The calling thread and the main thread are never waited on.
module-condition-wait
(module-condition-wait condition-variable lock &key timeout (custodian *current-custodian*))
Wait on CONDITION-VARIABLE, releasing LOCK, which the caller holds, as a condition wait does, for at most TIMEOUT seconds when that is given, signalling module-cancelled as soon as the module of CUSTODIAN is cancelled. Returns true when woken, which may be spuriously, and NIL when TIMEOUT passed first. Unlike SBCL's own wait, LOCK is held again whenever this returns or anything unwinds out of it, a deadline of the caller's included, so the caller's lock form releases it as usual. Taking LOCK back is bounded by no deadline, so a lock another thread never releases keeps this waiting. On a thread that is not the module's own, a cancelled wait returns NIL at once, so NIL can mean cancelled: a loop there checks check-cancelled or custodian-cancelled-p rather than waiting again.
module-sleep
(module-sleep seconds &key (custodian *current-custodian*))
Sleep for SECONDS, as SLEEP does, signalling module-cancelled as soon as the module of CUSTODIAN, by default the module whose code is running, is cancelled. Returns NIL. On a thread that is not the module's own, a cancelled sleep returns at once, so a loop there checks check-cancelled or custodian-cancelled-p rather than sleeping again.
module-wait-on-semaphore
(module-wait-on-semaphore semaphore &key timeout (custodian *current-custodian*))
Wait on SEMAPHORE, a bordeaux-threads or SBCL semaphore, as its own wait does, for at most TIMEOUT seconds when that is given, signalling module-cancelled as soon as the module of CUSTODIAN is cancelled. Returns true when the count was taken and NIL when TIMEOUT passed first. On a thread that is not the module's own, a cancelled wait returns NIL at once, so NIL can mean cancelled: a loop there checks check-cancelled or custodian-cancelled-p rather than waiting again.
node-thread-p
(node-thread-p thread)
True when THREAD carries a name the node gives a thread of its own, or is the logging library's file watcher, which the node starts once and never as a module's.
note-custodian-threads
(note-custodian-threads custodian threads)
Record THREADS as the module of CUSTODIAN's, dropping those it knew that have ended. Returns no values.
spawn-module-thread
(spawn-module-thread function &key name (custodian *current-custodian*))
Start a thread that calls FUNCTION as code of the module of CUSTODIAN, by default the module whose code is running, and return it. The thread is recorded as the module's before FUNCTION runs, so retiring the module finds it; it, and every thread it starts through bordeaux-threads, carries the custodian, so the cancel-aware waits in it end when the module is cancelled; and a FUNCTION that ends by module-cancelled ends the thread quietly. Signals no-module-custodian when no custodian is named, and module-retired once the module's retirement has begun, so a thread it left running cannot start more.
Variables
*cancel-check-seconds*
How often, in seconds, a cancel-aware wait looks to see whether its module has been cancelled. A module asked to end its work sees it within about this long at any such wait.
*clock-stop-ceiling*
The internal real time no wait for a module's threads or clocks may run past, or NIL when nothing above a single retirement bounds it. The node binds it while it boots, so a boot that leaves out several broken modules still reports itself ready well before its supervisor gives up on it, and while it takes all its active parts down.
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.