valis / Reference / API reference

Namespace - API reference

Exported surface for the namespace subsystem. Part of the API reference.

Package valis/src/namespace/acme

Classes

acme-dir-root

The /acme axis root directory node. Its one fixed leaf is ctl; node-walk is a cond over the leaf name and returns nil for anything else (sovereignty-as-absence). A certs listing leaf can join it once the store enumeration surface is settled.

Functions

make-acme-dir-root

(make-acme-dir-root)

Construct the /acme axis root. The fabric instantiates this and binds acme-dir-root to it at start. The owner's frame mounts it directly, as it mounts /config; the namespace root never names it, so no grant can reach it.

Variables

*acme-dir-root*

The /acme axis node. Set by the fabric at start-fabric; nil before the fabric is up or after teardown. Only the owner's frame mounts this door, as it mounts /config; the namespace root deliberately never names it, so no grant can designate /acme. A nil value means the axis is absent (sovereignty-as-absence).

Package valis/src/namespace/admit-ctl

Classes

admit-bound-cap-dir

One session's /cap: the shared capability service with the admit door, and any other owner door, added beside it. It exists only in a namespace entitled to the doors, so the shared /cap never carries their names.

admit-ctl-node

The /cap/admit file: the owner's dedicated door for minting permission to insert code into a running node. It mints :admit alone over one module designation, so that right never shares a token with another.

Conditions

admit-command-malformed

Signalled when a line written to the admit door is not a command it takes: an unknown verb, the wrong number of arguments, a rights group where none belongs or one asking for more than :admit, or an expiry or grant hash that does not parse. Nothing was minted, filed or changed.

owner-grant-beyond-filing-bound

Signalled when an owner grant offered for filing expires more than owner-grant-filing-bound-seconds after the filing time. EXPIRY is the grant's Unix expiry and BOUND the latest this filing allows.

publisher-active-grant-not-unique

Signalled when the vouch store holds more than one active standing grant for PUBLISHER, which only a store written before the one-active rule can; GRANT-HASHES are their hashes as hex. Nothing is chosen among them.

publisher-designation-refused

Signalled when a publisher designation names no publisher the owner has filed, so nothing is minted or filed over it.

publisher-grant-already-active

Signalled by grant-publisher, before anything is signed, when PUBLISHER already holds an active standing grant; GRANT-HASH is that grant's hash as hex.

publisher-list-refused

Signalled when a publisher list offered for filing does not verify, or would replace a list from the same publisher that is not older. REASON says which, naming the list. Nothing was filed.

standing-grant-not-active

Signalled when a review names a held standing grant that no longer admits anything: STATE is :withdrawn, :backed-out (withdrawn and revoked), or :revoked (its hash revoked through /cap/ctl). GRANT-HASH is the hash as hex. Nothing was stamped.

standing-grant-not-held

Signalled when a withdrawal or back-out names, by GRANT-HASH as written, a standing grant this node does not hold. Nothing was changed.

standing-grant-refused

Signalled when a standing grant offered for filing is not one this node files: not issued by its owner, not addressed to it, already expired, or not exactly one :admit over a publisher designation. Nothing was filed.

standing-grant-review-unrecorded

Signalled when a review's reviewed entry cannot be appended to the grant audit trail. The stamp exists only in the trail, so nothing was stamped. REASON is the trail's reason and never carries its path.

Generic functions

admit-bound-cap

(admit-bound-cap object)

Undocumented: this exported symbol needs a docstring.

owner-grant-beyond-filing-bound-bound

(owner-grant-beyond-filing-bound-bound condition)

Undocumented: this exported symbol needs a docstring.

owner-grant-beyond-filing-bound-expiry

(owner-grant-beyond-filing-bound-expiry condition)

Undocumented: this exported symbol needs a docstring.

publisher-active-grant-not-unique-grant-hashes

(publisher-active-grant-not-unique-grant-hashes condition)

Undocumented: this exported symbol needs a docstring.

publisher-active-grant-not-unique-publisher

(publisher-active-grant-not-unique-publisher condition)

Undocumented: this exported symbol needs a docstring.

publisher-grant-already-active-grant-hash

(publisher-grant-already-active-grant-hash condition)

Undocumented: this exported symbol needs a docstring.

publisher-grant-already-active-publisher

(publisher-grant-already-active-publisher condition)

Undocumented: this exported symbol needs a docstring.

publisher-list-refused-reason

(publisher-list-refused-reason condition)

Undocumented: this exported symbol needs a docstring.

standing-grant-not-active-grant-hash

(standing-grant-not-active-grant-hash condition)

Undocumented: this exported symbol needs a docstring.

standing-grant-not-active-state

(standing-grant-not-active-state condition)

Undocumented: this exported symbol needs a docstring.

standing-grant-not-held-grant-hash

(standing-grant-not-held-grant-hash condition)

Undocumented: this exported symbol needs a docstring.

standing-grant-review-unrecorded-grant-hash

(standing-grant-review-unrecorded-grant-hash condition)

Undocumented: this exported symbol needs a docstring.

standing-grant-review-unrecorded-reason

(standing-grant-review-unrecorded-reason condition)

Undocumented: this exported symbol needs a docstring.

Functions

active-standing-grant-for-publisher

(active-standing-grant-for-publisher store anchor-did)

The one active standing grant STORE, the vouch store, holds for the publisher ANCHOR-DID names, as its text, or NIL when it holds none or ANCHOR-DID is NIL. A publisher holds one active standing grant at a time, so there is never a choice to make; a store holding two, written before that rule, signals publisher-active-grant-not-unique rather than pick one. It judges nothing: admission verifies whatever grant it is offered.

bind-admit-door

(bind-admit-door cap-node &key doors)

Return CAP-NODE as an entitled session sees it: the same capability service with the admit door at /cap/admit, and each of DOORS, an alist of name and a function making a fresh door, at /cap/<name>. Call it only while assembling a namespace entitled to insert code; every other namespace keeps CAP-NODE as it is and so has none of these files.

make-admit-ctl-node

(make-admit-ctl-node)

Construct a fresh admit door.

mark-standing-grant-reviewed

(mark-standing-grant-reviewed hex)

Stamp the held, active standing grant HEX names as reviewed now, by appending a reviewed entry over its hash to the grant audit trail, and answer the reply the reviewed verb serves: reviewed standing grant HEX at TIME; the review is advisory and admits nothing. The stamp lives only in the trail and admits or refuses nothing. The check and the append share one hold of the vouch store's lock. Every refusal leaves the trail as it was. Signals admit-command-malformed when HEX is not a grant hash. Signals standing-grant-not-held when no held standing grant has that hash. Signals standing-grant-not-active when the grant is withdrawn, backed out, or revoked. Signals standing-grant-review-unrecorded when the entry cannot be appended.

preview-standing-grant-change

(preview-standing-grant-change act hex)

Answer what ACT, withdraw or back-out (a string designator, case ignored), would do to the held standing grant HEX names, without doing it. The first line is preview ACT HEX: nothing was changed, and the second is state STATE, the grant's state as the grant inventory names it. Where the act would change nothing, a withdrawal of a grant already withdrawn or a back-out of one already backed out, one line says so. Otherwise the consequence lines follow: a back-out preview carries every line after the first of the back-out's own reply; a withdrawal preview carries the sentence the withdrawal replies with, then the modules that stay and what a fresh install from the publisher then needs. Reads the vouch store under its lock, the designations admitted under the grant, and the revocation store; writes no store and no trail. Signals admit-command-malformed for any other ACT, or for HEX that is not a grant hash. Signals, as the back-out does, when ACT is back-out and no revocation store is open. Signals standing-grant-not-held when no held standing grant has that hash.

standing-grant-publisher

(standing-grant-publisher grant)

The did:key of the publisher GRANT, a standing grant as token: text, names over /publisher/<did:key>, the same did:key its publisher anchor is filed under; NIL when GRANT names no publisher, does not decode, or has been backed out, since none of those stands for a publisher. With no revocation store open nothing counts as backed out.

Variables

*admit-vouch-store*

The vouch store the owner's filing verbs write to: set by start-fabric, NIL while no fabric is up, and bound by tests.

Constants

+owner-grant-filing-bound-seconds+

The furthest after filing, in seconds, an owner grant may expire: a year of 365.25 days plus 300 seconds of clock skew.

+standing-grant-lifetime-seconds+

How long, in seconds, a standing grant minted by grant-publisher lasts: a century of 365.25-day years.

Package valis/src/namespace/assembler

Classes

live-view-entry

Per-view state tracked in the live-view registry for revocation eviction. LOCK is a per-view mutex held during mount-table structural mutations; reads are lock-free (SBCL setf of a slot value is atomic on x86-64).

ID names the session to an operator, who has to be able to type it: the registry is keyed by view-root object identity, which is unspeakable outside the image. HANDSHAKE is the auth conversation the session was admitted through, kept so a fence can survive a re-attach on the same transport. ADMITTED-AT and LAST-ACTIVITY are Unix seconds; LAST-ACTIVITY is stamped on the request path and so is written without the lock. FENCED-AT is nil for a live session and the Unix second of the fence otherwise, which is what distinguishes a session emptied on purpose from one that simply holds nothing.

Conditions

reassembly-unreachable

Signalled when a manifest mount's referenced subtree root is unreachable during pre-assembly reachability verification. A namespace-tier fault kept distinct from the store-tier MANIFEST-CORRUPT: the whole assembly aborts fail-closed (no partial mount table) before any view-root is built.

unhashable-grant

Signalled when a capability that has already verified nonetheless yields no grant hash. The hash is what revocation evicts by, so a mount carrying none would keep serving after its grant was revoked. The view or mount is refused instead: an ungovernable grant is worse than an absent one.

Generic functions

reassembly-unreachable-name

(reassembly-unreachable-name condition)

Undocumented: this exported symbol needs a docstring.

unhashable-grant-carrier

(unhashable-grant-carrier condition)

Undocumented: this exported symbol needs a docstring.

Functions

build-base-view

(build-base-view root auth-identity)

Dispatch on AUTH-IDENTITY's principal class to build the base namespace view for a new session (phase one of two-phase resolution).

AUTH-IDENTITY is the principal resolved by node-attach-identity at Tattach. It is never nil for an admitted session (either a keyed principal or the anonymous sentinel).

Returns a view-root under a three-way principal-class policy:

owner DID → the full canonical frame (assemble-canonical-frame): proto, bus, id, cap, pub, edge, and /ctl. Its /cap alone carries /cap/admit, the door that mints permission to insert code, and /cap/sign, the door that signs as the node's publisher. Its /names alone carries /names/cert, the door that orders a certificate from the CA. anonymous → the read-only /pub published floor (%assemble-published-floor): exactly /pub, projected read-only. This mirrors the anonymous HTTP edge, where an unauthenticated GET resolves to the /pub published view; an unauthenticated 9P mount now resolves to the same surface. The credential-gated axes stay absent. non-owner keyed → an empty view. Sovereignty as absence: a keyed principal that is not the owner widens to nothing: not the owner frame, and not the anonymous /pub floor.

Ahead of all three: an attach whose auth conversation has been fenced gets an empty view whatever its principal class. Without that check the fence lasts until the client asks for another attach, which a client that has just lost its namespace does immediately.

The owner arm checks the owner predicate again once the frame is registered, and fences the new entry when it now fails. Retirement orders the durable append, the live-index update, then the sweep, so an attach either registers before the sweep and is fenced by it, or checks again after the index update and fences itself. With no registry up it returns an empty view.

Every arm records AUTH-IDENTITY on the view it returns, as that view's principal, so a session served from the view itself records the principal the view was built for.

decorate-bearer-view

(decorate-bearer-view view auth-identity grant-string)

Finish a view-root built for a verified bearer-name attach.

Stamps GRANT-STRING's grant hash on VIEW's existing mount entries so the revocation eviction sweep reaches live bearer views: revoking the grant evicts the mount from sessions already holding it, not just future attaches. Appends the per-view ctl door (when ctl-node-factory is wired) so the session can issue phase-two mount/unmount commands over 9P. Registers VIEW in the live-view registry under AUTH-IDENTITY; the registry is weak-keyed, so an abandoned view is collected when its session dies. Returns VIEW.

Signals UNHASHABLE-GRANT when GRANT-STRING yields no hash: the sweep matches on that stamp, so an unstamped view would keep serving after its grant was revoked, and a session revocation cannot reach is worse than a refused attach.

Installed into root's bearer-view-decorate-fn by start-fabric, the same downstream-installs-the-hook pattern as ctl-node-factory, and for the same reason: root cannot import the assembler back.

deregister-view

(deregister-view view-root)

Remove VIEW-ROOT from LIVE-VIEW-REGISTRY. Returns NIL silently when LIVE-VIEW-REGISTRY is NIL (fabric not up). Acquires LIVE-VIEW-REGISTRY-LOCK when present to guard the remhash against concurrent evict-by-hash maphash.

evict-by-fence-epoch

(evict-by-fence-epoch superseded-generation)

Sweep every live write-capable, capability-backed grant out of active sessions on an authority transfer. SUPERSEDED-GENERATION is accepted for symmetry with the revocation-log member and the verifier consult; the sweep is by the write-right bit, not by the fence-epoch hash. A grant's hash is the digest of its canonical bytes and signature, never the epoch hash, so a hash match would sweep nothing. Base-policy entries carry a nil grant-hash and are immune; capability-backed read-only entries keep their write bit clear and survive.

For simple entries an entry is removed when it carries a non-nil grant-hash and the write right. For union-node entries the sweep operates per-member: each capability-backed member carrying the write right is removed independently, preserving sibling members; a union that loses its last member is removed from the mount table entirely.

This runs on the bump-caller's thread. The registry is snapshotted under LIVE-VIEW-REGISTRY-LOCK and the lock is released before acquiring any per-view LIVE-VIEW-ENTRY-LOCK, maintaining the registry-lock to per-view-lock acquisition order. Returns no meaningful value.

evict-by-hash

(evict-by-hash hash-32-bytes)

Sweep all live view-roots and remove capability grants matching HASH-32-BYTES. Base-policy entries (nil grant-hash) are unaffected.

For simple (non-union) entries the entry is removed when its GRANT-HASH matches. For union-node entries the sweep operates per-member: each member whose per-member grant hash matches is removed independently, preserving sibling members whose grants were not revoked. A union that loses its last member is then removed from the mount table entirely.

This runs on the revocation-caller's thread. The registry is snapshotted under LIVE-VIEW-REGISTRY-LOCK and then the lock is released before acquiring any per-view LIVE-VIEW-ENTRY-LOCK, maintaining the registry-lock → per-view-lock acquisition order. Returns no meaningful value.

fence-owner-sessions-on-authority-transfer

(fence-owner-sessions-on-authority-transfer superseded-generation)

Fence every live owner session when write authority moves to another locus. SUPERSEDED-GENERATION is accepted for symmetry with the capability sweep that runs beside it; the decision here is by principal, not by generation.

This is a separate step from evict-by-fence-epoch on purpose. That sweep removes capability-backed write grants and leaves base-policy entries alone, which is right for what it does and is relied on elsewhere. But an owner's namespace is base policy from end to end, so the sweep cannot reach it: an owner admitted to the surrendering instance would go on naming the full frame after the transfer, and the instance that gave up authority would go on serving them.

Fencing rather than pruning is what the escape architecture needs. An owner whose instance has relocated has to be able to tell whether they are talking to the real one, and a session that quietly survives the move is the thing that makes that question unanswerable. Cutting the session makes them re-establish it, which is where that question gets asked and answered.

Returns no meaningful value. Runs on the caller's thread, and takes the per-view locks only after releasing the registry lock, in the order the registry documents.

fence-session

(fence-session session-id)

Fence the session named by SESSION-ID, returning its registry entry, or NIL when no live session carries that id.

Fencing the session the caller is using is supported and does what it says: the command lands, and the next name that session reaches for is not there. Getting back in means authenticating again, which is the point. An operator who has lost control of a session should not have to weigh whether they are about to cut themselves off, and an attacker sharing that session should not be able to keep it by being the one still holding it.

fence-sessions-presenting-key

(fence-sessions-presenting-key dids)

Fence every unfenced live session whose principal presents one of DIDS, a list of DID strings, and return the number fenced.

Matches the given DIDs, never the owner predicate: once a key is retired the predicate no longer matches it, so a sweep that asked it would find nothing to fence.

Runs on the caller's thread and takes the per-view locks only after releasing the registry lock, in the order the registry documents.

live-sessions

(live-sessions)

Return the live-view registry entries, ordered by session id.

The ordering is for the operator reading the listing: ids are issued in admission order, so sorting by id puts the oldest session first and keeps a line in the same place between two reads.

live-view-entry-admitted-at

(live-view-entry-admitted-at instance)

Undocumented: this exported symbol needs a docstring.

live-view-entry-fenced-at

(live-view-entry-fenced-at instance)

Undocumented: this exported symbol needs a docstring.

live-view-entry-id

(live-view-entry-id instance)

Undocumented: this exported symbol needs a docstring.

live-view-entry-last-activity

(live-view-entry-last-activity instance)

Undocumented: this exported symbol needs a docstring.

live-view-entry-principal

(live-view-entry-principal instance)

Undocumented: this exported symbol needs a docstring.

live-view-entry-view-root

(live-view-entry-view-root instance)

Undocumented: this exported symbol needs a docstring.

make-live-view-registry

(make-live-view-registry)

Construct the weak hash table used as the live-view registry. An entry leaves when the session that registered it ends, arranged by register-view through at-session-end-fn. The table is weak on its key (make-hash-table :weakness :key, an SBCL extension) as the backstop for a view registered outside any served session, where no session end arranges the removal and the view-root is collected once nothing else holds it.

mount-capability

(mount-capability view cap-string &optional (bind-flag :after))

Verify CAP-STRING (a "valis:…" bearer name or "token:…" wire token) and mount the designated subtree into VIEW under its leaf name.

Returns (values T NIL) on success; (values NIL reason-string) on any failure. BIND-FLAG is :replace / :before / :after (default :after). A read-only grant wraps the target in a read-only-projection-node.

The entire body is wrapped in a handler-case; any unhandled condition becomes a (values nil reason-string) return so this function is fail-closed.

name-grant-hash

(name-grant-hash name-string)

Public entry to the bearer-name grant-hash computation: SHA-256(canonical-bytes || sig-64) of the decoded name. Returns a 32-byte octet vector, or NIL when NAME-STRING does not decode. Exported so consumers (the edge seam's revocation check, tests asserting store membership) test against exactly the hash this package and the verifier use, rather than re-deriving the format.

NIL means the string is not a capability name at all, never that the hash could not be computed: a fault anywhere past the decode propagates. A caller deciding whether a grant is still good must treat NIL as unknown standing and refuse, not as clearance.

note-session-activity

(note-session-activity view-root)

Stamp VIEW-ROOT's session as active, or fence it when it has gone idle past SESSION-MAX-IDLE-SECONDS.

Called from the request path, so it does its work with a slot read, a comparison and a slot write, and takes no lock: an integer slot assignment is atomic on the platforms this runs on, and a torn read here would at worst delay one fence by one operation.

Checking the bound here rather than from a sweeper is what makes the bound mean something after a relocation. An instance that has moved leaves sessions behind that believe they are still attached to it, and the moment worth catching is the one where such a session is next used, not some arbitrary tick before or after. A session nobody touches costs nothing and reaches nothing.

register-view

(register-view view-root principal)

Add VIEW-ROOT and its PRINCIPAL to LIVE-VIEW-REGISTRY for eviction sweep. Returns the registry entry, or NIL when LIVE-VIEW-REGISTRY is NIL (fabric not up). Acquires LIVE-VIEW-REGISTRY-LOCK when present to guard the structural setf gethash against concurrent evict-by-hash maphash.

The entry is also stored on the view itself, so the request path can stamp its activity mark from a slot read rather than a lookup on the weak registry. The auth conversation is read from PRINCIPAL here, at the one moment it is still known, so a later fence has something durable to name.

When AT-SESSION-END-FN is set, VIEW-ROOT's removal is arranged to run when the session registering it ends. On a thread serving no session, or before the fabric is up, no removal is arranged and the weak key is the only backstop.

session-descriptors

(session-descriptors)

Describe the live sessions as plain plists, one per session, carrying :ID, :PRINCIPAL, :ADMITTED-AT, :LAST-ACTIVITY and :FENCED-AT.

Plists rather than registry entries because the door that publishes this sits upstream of the registry and cannot import it. Handing plain data across that seam also keeps the wire format a decision of the door, which is where a reader's needs are actually known.

session-handshake-fenced-p

(session-handshake-fenced-p handshake)

Return true when HANDSHAKE names an auth conversation that has been fenced. A nil HANDSHAKE is never fenced: an unauthenticated attach carries no conversation, and treating its absence as a match would fence every anonymous arrival at once.

unmount-name

(unmount-name view name &optional member-index)

Remove the mount-table entry for NAME from VIEW.

If MEMBER-INDEX is NIL, remove the entire entry. If MEMBER-INDEX is an integer and the entry's node is a union-node, remove that member from the union; if the union becomes empty, remove the entry entirely. If the node is not a union-node, MEMBER-INDEX is ignored.

Refuses to unmount "ctl": a self-narrowing unmount must never sever the view's own control channel.

Returns (values T NIL) on success; (values NIL reason-string) on failure.

Variables

*assembler-root*

The valis-root the assembler resolves designations against. Set by start-fabric; cleared by stop-fabric. Nil before fabric is up.

*at-session-end-fn*

A function of one thunk that arranges for it to run when the 9P session served on the calling thread ends, or nil. Set by start-fabric to the transport's session-end arrangement; cleared at both stop paths. register-view reaches that arrangement only through this seam, so the namespace layer never imports the transport layer, and a unit test without a fabric keeps the weak-key behaviour with no arrangement made.

*ctl-node-factory*

A function of one argument (the view-root) that constructs and returns the /ctl node for that view. Set by start-fabric to #'make-view-ctl-node; cleared by stop-fabric. Nil means no ctl node is mounted: the canonical frame lacks /ctl, which is the correct behavior outside a running fabric (unit tests that call build-base-view without a fabric should not pull in view-ctl).

This seam breaks the compile-time circularity: view-ctl imports assembler (for mount-capability / unmount-name), so assembler cannot import view-ctl back; fabric is downstream of both and installs the factory at start time.

*durable-head-reader*

The block-store the assembler reads the namespace manifest from. Set by start-fabric from the live store-store's block-store accessor; nil before the fabric is up. Then assemble-canonical-frame falls back to the default manifest, so a unit test that calls build-base-view without a fabric gets today's behavior unchanged. This is the store current-head / read-manifest / read-block take on the read path; the write-side publish is driven by the fabric from the store's head handle directly, so only the block-store is held here.

*fenced-handshakes*

The auth conversations whose sessions have been fenced. Weak on the key, so a row goes away with the conversation it names.

Emptying a view stops the session that holds it, but a client can ask for another one: a second Tattach over the same transport would otherwise assemble a fresh owner frame and hand the fence straight back. Consulting this table at attach closes that door, and closes it exactly as far as intended, because re-running the handshake yields a conversation this table has never seen.

*live-view-registry*

Weak hash table (view-root to live-view-entry) for revocation eviction. An entry leaves when the session that registered it ends: register-view arranges its removal through at-session-end-fn on the serving thread. The table stays weak on its key (make-hash-table :weakness :key, an SBCL extension), the backstop for a view registered outside any served session, as a unit test or the edge's request thread builds, where no session end arranges the removal and the collector reclaims the entry with its view-root.

*live-view-registry-lock*

Mutex protecting structural mutations of LIVE-VIEW-REGISTRY: setf gethash in register-view, remhash in deregister-view, and maphash in evict-by-hash's snapshot step. Set by start-fabric; cleared by stop-fabric.

Lock order: acquire LIVE-VIEW-REGISTRY-LOCK FIRST, then per-view LIVE-VIEW-ENTRY-LOCK. Never hold both simultaneously: evict-by-hash releases the registry lock before acquiring any per-view lock.

*session-max-idle-seconds*

How many seconds a session may go untouched before its next operation fences it, or nil for no bound at all. Nil is the default and preserves the behavior a deployment already has: a session lives until it is fenced or its transport dies. That default is deliberate, because the alternative is a lockout nobody asked for on an upgrade.

The bound is enforced when a session is next used, not by a sweeper. A dormant session issues no requests and can harm nothing; the case worth catching is someone picking up an abandoned one, and that arrives as an operation.

+recommended-session-max-idle-seconds+

The idle bound a deployment is advised to adopt for owner sessions. It is not applied on its own: a launcher or an operator chooses it explicitly by setting SESSION-MAX-IDLE-SECONDS. Fifteen minutes is short enough that an unattended browser tab stops being a live management console over a lunch break, and long enough that ordinary editing is never interrupted.

Package valis/src/namespace/backup

Classes

backup-dir-root

The /backup axis root directory node. Its one fixed leaf is ctl; node-walk is a cond over the leaf name and returns nil for anything else (sovereignty-as-absence).

Functions

make-backup-dir-root

(make-backup-dir-root)

Construct the /backup axis root. The fabric instantiates this and binds backup-dir-root to it at start. The owner's frame mounts it directly, as it mounts /config; the namespace root never names it, so no grant can reach it.

Variables

*backup-dir-root*

The /backup axis node. Set by the fabric at start-fabric; nil before the fabric is up or after teardown. Only the owner's frame mounts this door, as it mounts /config; the namespace root deliberately never names it, so no grant can designate /backup. A nil value means the axis is absent (sovereignty-as-absence).

Package valis/src/namespace/cap

Classes

cap-dir

The /cap directory node: ctl and verify child files exposing the capability service over 9P.

Generic functions

cap-dir-ctl

(cap-dir-ctl object)

Undocumented: this exported symbol needs a docstring.

cap-dir-verify

(cap-dir-verify object)

Undocumented: this exported symbol needs a docstring.

Functions

decode-wire-token

(decode-wire-token payload-bytes)

Decode PAYLOAD-BYTES (the raw (canonical-bytes || sig-64) byte vector obtained by base58-decoding the payload after the "token:" prefix) into a CAPABILITY-TOKEN object.

This is a thin public wrapper around the internal wire decoder so the namespace assembler can reconstruct a token from a bearer string without reaching into cap.lisp internals via ::. Signals on any structural mismatch.

decode-wire-token-chain

(decode-wire-token-chain wire-string)

Decode WIRE-STRING, a whole "token-chain:" string, into its leaf CAPABILITY-TOKEN, carrying every parent as its proofs, immediate parent first through to the root. Each parent carries its own parents the same way. hekate's decoder bounds the string before decoding any of it and refuses a one-link chain, a duplicated link and any structural fault; those refusals signal here. Links are unmodified single tokens, so each has the grant hash it has when it travels alone.

make-cap-dir

(make-cap-dir &key custody revocation-store)

Construct a fresh cap-dir with ctl and verify child nodes. CUSTODY and REVOCATION-STORE are captured on the instance for a future per-instance service; the live lookup path goes through cap-custody and cap-revocation-store, which fabric.lisp sets at start.

mint-name-bearer

(mint-name-bearer custody designation rights expiry-seconds)

Mint one owner-issued bearer capability name: CUSTODY signs a grant over DESIGNATION carrying RIGHTS, expiring EXPIRY-SECONDS from now. Returns the "valis:" name string /cap/verify accepts. This is the single owner-issued name mint path, the bearer-name counterpart of mint-token-bearer, so every door that mints a name signs the same bytes the same way. The signing key never leaves CUSTODY.

mint-token-bearer

(mint-token-bearer custody audience-did designation rights expiry-seconds)

Mint one owner-issued capability token: CUSTODY signs a grant to AUDIENCE-DID over DESIGNATION carrying RIGHTS, expiring EXPIRY-SECONDS from now.

Returns (values BEARER TOKEN): BEARER is "token:" + base58 of (canonical-bytes

  signature), the wire form /cap/verify accepts, and TOKEN is the constructed

CAPABILITY-TOKEN, so a caller can read the grant back (to record its grant hash, say) without re-decoding the bearer.

This is the single owner-issued mint path: both the /cap ctl parser and the ssh-reality proof-of-reality driver construct the token here, so a maximal Tier-1 grant is byte-identical to a /cap ctl mint and no second token-construction copy exists. The signing key never leaves CUSTODY; the issuer is always the owner custody DID, so this path cannot mint a self-issued token whose issuer is its own audience.

split-ws

(split-ws s)

Split S on runs of ASCII whitespace (space, tab, newline, carriage return) and return a list of nonempty tokens. All input here is attacker-controlled bytes; no Lisp reader is involved. Exported so view-ctl.lisp can share the implementation.

Variables

*cap-custody*

The active custody-store the capability service signs and verifies with. Set by fabric.lisp at start-fabric time (it points to the same store factotum-custody holds); nil before the fabric is up. The ctl and verify nodes guard against nil at open so a pre-fabric caller gets a clear error rather than a nil-deref mid-request. Tests bind it dynamically.

*cap-revocation-store*

The active revocation-store consulted and appended by the capability service. Set by fabric.lisp at start-fabric time; nil before. Tests bind it dynamically.

*cap-service*

The active cap-dir node serving the /cap axis. Set by fabric.lisp at start-fabric time; nil before the fabric is up or after stop-fabric. root.lisp walks /cap through this variable once the axis is wired.

Package valis/src/namespace/config

Classes

config-dir-root

The /config axis root. Its fixed leaves are list, modules and ctl; any other name walks to nothing.

Functions

make-config-dir-root

(make-config-dir-root)

Construct the /config axis root. The fabric binds config-dir-root to one at start; the assembler mounts it in the owner's frame only.

Variables

*config-dir-root*

The /config axis node. Set by the fabric at start and cleared at stop; nil means the owner's frame has no /config.

Package valis/src/namespace/ctl-staging

Classes

ctl-staging-file

Shared staging base for the owner-gated /ctl write nodes. A verb line written across node-write calls accumulates in BUFFER; nothing is committed until the clunk dispatches it through commit-ctl-verb. A fid abandoned without a clunk commits nothing: complete-or-absent, structural.

GENERATION-READER, when supplied, returns the live store write-epoch so each commit fences against the current generation. A node whose state has no durable store behind it supplies none and commits unfenced.

Generic functions

commit-ctl-verb

(commit-ctl-verb node verb)

Commit the operator VERB (a trimmed string) staged on a /ctl NODE. Specialised per node: the cutover flip, the axis-level declaration, the record verbs, the cert-obtain door, the zone-create door, the served-set verbs. Signals a domain condition on an illegal verb (or a plain nine-p-error on an unknown one); the node's clunk translates the domain conditions to a clean nine-p-error so a refusal returns an Rerror rather than crashing the serve path.

ctl-buffer

(ctl-buffer object)

Undocumented: this exported symbol needs a docstring.

ctl-generation-reader

(ctl-generation-reader object)

Undocumented: this exported symbol needs a docstring.

Functions

ctl-verb-lines

(ctl-verb-lines payload)

Split a staged /ctl verb PAYLOAD into its lines: the verb on the first line, then one field per line, a single terminal empty line (from a trailing newline) dropped and a per-line carriage return tolerated.

Newline framing is the rule every verb on every axis follows, and it is what lets a field carry a space. Whitespace tokenization cannot do that: a DNS rdata field and a host name both carry interior spaces, and a verb framed on whitespace would split one field into several and silently mean something else.

staged-ctl-payload

(staged-ctl-payload node)

NODE's staged octets decoded as a UTF-8 string, with no trimming. A verb whose framing is significant (one field per line) reads its payload here, because trimming would eat a field's own leading space.

staged-ctl-verb

(staged-ctl-verb node)

NODE's staged verb: the decoded payload with surrounding whitespace trimmed, which is what a single-line verb wants.

Variables

*max-ctl-verb-bytes*

The upper bound on a staged /ctl verb line, in bytes. A verb is tiny; this ceiling forecloses an authenticated writer streaming unbounded octets into one /ctl fid, the same accumulation-abuse guard the zone-submission node applies.

Package valis/src/namespace/edge

Classes

edge-dir

The /edge status directory node. node-entries and node-walk call edge-bound-ports fresh on every invocation; opening /edge in a 9P client sees the ports the controller has bound at that moment, plus the anonymous status file.

Functions

make-edge-dir

(make-edge-dir)

Construct a fresh edge-dir node.

Package valis/src/namespace/grant-views

Classes

audit-read-file

/cap/audit: the grant audit trail, read-only, for the owner alone.

grants-read-file

/cap/grants: the owner's inventory of the grants he holds, read-only, for the owner alone.

Conditions

grant-inventory-unavailable

Signalled by grant-inventory when it has no vouch store to read; REASON says why.

Generic functions

grant-inventory-unavailable-reason

(grant-inventory-unavailable-reason condition)

Undocumented: this exported symbol needs a docstring.

Functions

grant-inventory

(grant-inventory &key (store *admit-vouch-store*) (revocations *cap-revocation-store*) (entries nil entries-p) (now (unix-now)) (review-days (resolve-standing-grant-review-days)))

The grants the owner holds in STORE, the vouch store, and the publishers he trusts, as two lists of plists.

The first value holds one plist per standing grant, ordered by grant hash, then one per per-module owner grant, ordered the same way, then one per held owner credential that cannot be hashed. Each carries: :grant, the grant hash as hex, or NIL when the credential cannot be hashed; :kind, :standing or :module; :state, :active, :withdrawn, :backed-out (withdrawn and revoked), :revoked (revoked through /cap/ctl while still filed), or :unknown (a per-module credential that cannot be hashed); :publisher, the did:key the standing grant names, or for a per-module grant the filed anchor its module's vouch is rooted at, or NIL; :last-admission, the latest Unix time a module was admitted under it, or NIL; :modules, the designations admitted under it, sorted. A standing plist also carries :filed-at, the Unix time of the grant's latest filing in the trail or NIL; :reviewed-at, its latest review in the trail or NIL; and :review, :due or :ok, judged at NOW against REVIEW-DAYS. A per-module plist carries :designations, every module it is filed for, and :designation-filed-at, the latest filing of an owner grant for any of those designations, which is not always this credential's filing.

The second value holds one plist per filed publisher, ordered by did:key. Each carries: :publisher, its did:key; :list-issuer, :list-sequence, :list-issued-at, and :list-age-seconds, each NIL when it has filed no list; :list-fresh, true when its list is within the seven days an install allows; :withdrawn-targets, the count of targets its list withdraws; :remembered-revocations, the count of revocations remembered under it; :active-standing-grants, the hashes of its active standing grants.

REVOCATIONS is the revocation store, ENTRIES the trail's entries (those of the open trail by default), NOW a Unix time, and REVIEW-DAYS the module.standing-grant-review-days key, read at each call. The store is read under its lock, so the answer reflects one state of it. No plist holds the text of a grant or a vouch. A held credential that cannot be hashed is logged at warn. Signals grant-inventory-unavailable when STORE is NIL, and the trail's conditions when ENTRIES must be read and cannot be.

make-audit-read-file

(make-audit-read-file)

A fresh /cap/audit file.

make-grants-read-file

(make-grants-read-file)

A fresh /cap/grants file.

render-grant-inventory

(render-grant-inventory grants &optional publishers)

GRANTS and PUBLISHERS, plists as grant-inventory answers them, as the text /cap/grants serves: one stanza of key and value lines per grant, then one per publisher, stanzas separated by one blank line.

unreviewed-standing-grant-count

(unreviewed-standing-grant-count &key (store *admit-vouch-store*) (revocations *cap-revocation-store*) (entries nil entries-p) (now (unix-now)) (review-days (resolve-standing-grant-review-days)) (wait t) (warn t))

How many standing grants STORE, the vouch store, holds that read review due at NOW, and as a second value REVIEW-DAYS, the interval they were judged against. Each grant is judged as grant-inventory judges it: from its state given REVOCATIONS, and from its filing and review in ENTRIES, the trail's entries (those of the open trail by default). Only an active grant is ever due. The store is read under its lock. With WAIT false, answers NIL at once when another thread holds that lock. With WARN false, a grant that cannot be hashed is counted as grant-inventory counts it and is not logged. Signals grant-inventory-unavailable when STORE is NIL. Signals the trail's conditions when ENTRIES must be read and cannot be.

Package valis/src/namespace/id

Classes

factotum-dir

The shared /id directory node: the factotum(4)-faithful tree a grant resolves against. It names rpc, proto and owner. It holds the session listing, ctl and session-ctl but never names them, because a grant resolves its designation by walking this node and all three are the owner's alone; the owner's frame mounts bind-id-owner-doors of this node, which names them. Replaces id-stub when the live factotum is wired; root.lisp keeps both so the axis stays traversable either way.

The sessions and session-ctl doors are the owner's view of, and control over, the attachments to this node. They sit on /id because what they act on is who is currently admitted, which is an identity question rather than a capability one: the sessions listed here hold no capability to revoke.

id-stub

Static empty directory node for the /id axis, used when no live factotum subtree is wired.

Functions

bind-id-owner-doors

(bind-id-owner-doors factotum-dir)

Return FACTOTUM-DIR as the owner's frame mounts it: the same /id with the session listing, ctl and session-ctl named beside the shared children. Call it only while assembling the owner's frame. FACTOTUM-DIR itself never names the three owner doors, because a grant resolves its designation by walking it. Any other node, such as the id-stub of an unwired factotum, is returned unchanged.

detach-factotum-keyring

(detach-factotum-keyring)

Set the keyring on factotum-custody to NIL when a store is held, so a reference to the store that outlives the running node admits only the node's own key. Returns no values.

factotum-keyring

(factotum-keyring)

Return the owner keyring on factotum-custody, or NIL when no store is held. From the image, enrol a device with (enrol-owner-key (factotum-keyring) did :label label) and retire it with (retire-owner-key (factotum-keyring) did). An enrolled key reaches the owner management frame until it is retired.

factotum-seed-owner-did

(factotum-seed-owner-did keyfile)

Return the owner DID of the seed at KEYFILE, read by the loader load-factotum-key-store uses, with no keyring loaded. Signals owner-keyfile-unsafe as that loader does. Call it only with a regular file at KEYFILE: the loader creates a seed where none is.

load-factotum-key-store

(load-factotum-key-store keyfile)

Return the factotum's key store for KEYFILE: the custody store load-or-create-keyfile returns, with the owner keyring loaded from beside KEYFILE, bound to that store's owner DID, and attached to it. A keyring-unusable condition propagates unhandled, so nothing is returned and nothing is attached.

make-factotum-dir

(make-factotum-dir &key custody)

Construct a fresh factotum-dir backed by CUSTODY (a custody-store). The directory names the shared children rpc, proto and owner, and holds the owner doors ctl, sessions and session-ctl, which only bind-id-owner-doors of it names. The custody slot is captured here so a multi-owner factotum can be added without rewriting the seam-variable lookup chain.

make-id-stub

(make-id-stub)

Construct a fresh id-stub node.

Variables

*factotum-custody*

The factotum's one key store: the custody-store carrying the owner's Ed25519 master key pair and, attached to it, the owner keyring of enrolled device keys. Set by fabric.lisp at start-fabric time through load-factotum-key-store; nil before the fabric is up or after stop-fabric. The rpc-node's node-open reads this to seed each fresh Noise auth conversation (using the derived X25519 transport key); the /id/ctl conversation renders the key listing from it and enrols and retires on its keyring. Tests bind it dynamically.

Package valis/src/namespace/mail

Classes

mail-dir-root

The /mail axis root directory node. Its children are the four fixed leaves outbox/inbox/queue/ctl, each instantiated fresh per call over the mail block STORE and its fenced HEAD: the child nodes query the live head on every call, so a fresh instance per walk holds no snapshot. Unlike /pub's store-listed slugs, the children are a FIXED set: node-walk is a cond over the four leaf names and returns nil for anything else. STORE/HEAD are nil before the fabric is up; the children then list/serve nothing of their own accord.

Functions

make-mail-dir-root

(make-mail-dir-root &key store head resolver)

Construct the /mail axis root over the mail block STORE and its fenced HEAD, threading the outbound MX RESOLVER the /ctl drain resolves relay recipients through. The fabric instantiates this and binds mail-dir-root to it at start; root.lisp mounts it under /mail as a /pub sibling, and the assembler walks it into the owner's canonical frame. A NIL RESOLVER makes a drain defer every relay entry fail-closed: there is no smarthost fallback.

Variables

*mail-dir-root*

The /mail axis node. Set by the fabric at start-fabric; nil before the fabric is up or after teardown. root.lisp's node-walk walks /mail through this variable, so a nil value means the axis is absent (sovereignty-as-absence), exactly as /pub and /edge behave.

Package valis/src/namespace/mail-ctl

Classes

mail-ctl-node

The /mail/ctl file: owner-only mail-queue control. node-open mints a fresh mail-ctl-conversation closing over STORE and HEAD; node-write parses a status or drain command; node-read returns the result. mode #o600: only the owner's session writes it, and it is mounted only in the owner frame and never named in an operational grant. The base backend-node :after mints a qtfile QID, which is correct for a file node.

Functions

make-mail-ctl-node

(make-mail-ctl-node &key store head resolver)

Construct the /mail/ctl node over the mail STORE and its fenced HEAD, threading the outbound MX RESOLVER a drain step resolves relay recipients through. The /mail axis root instantiates this and mounts it under /mail in the owner frame; node-open mints a conversation closing over STORE, HEAD, and RESOLVER. A NIL RESOLVER makes a drain defer every relay entry fail-closed: there is no smarthost fallback.

Package valis/src/namespace/mail-inbox

Classes

inbox-dir-root

The /mail/inbox directory node. Its children are per-identity Maildir roots keyed under a DID (even with one identity). node-walk gates the DID through the fail-closed valid-did-slug-p charset gate and resolves only a DID with a landed inbox subtree; an unknown DID or a charset-invalid name returns nil: sovereignty- as-absence. STORE/HEAD are the mail block store and its fenced head; both nil before the fabric is up, in which case the inbox lists nothing.

Functions

make-inbox-dir-root

(make-inbox-dir-root &key store head)

Construct the /mail/inbox directory node over the mail STORE and its fenced HEAD. The /mail axis root instantiates this and mounts it under /mail; per-DID Maildirs and their owner read-state mutations resolve through STORE/HEAD on every call against the live head.

Package valis/src/namespace/mail-outbox

Classes

outbox-dir-root

The /mail/outbox directory node, the writable submission bridge. STORE and HEAD are the content-addressed block store and its fenced head the staged submission commits through at clunk; both nil before the fabric is up, in which case node-create fails closed. node-entries / node-walk expose nothing: a submission has no read-state, so the outbox lists and names no child after the create that minted its staging fid.

outbox-submission-file

A staging message file, the qmail tmp/ equivalent. Octets written across node-write calls accumulate in BUFFER; nothing is content-addressed or queued until node-close (the clunk) commits. A fid abandoned without a clunk never reaches node-close, so it lands no queue entry: complete-or-absent, structural. SUBMITTER-DID and AUTHENTICATED-SUBMIT-P were captured from the session principal at the write gate (node-create): the DID, and whether the session is a keyed principal; both are stamped onto the provenance at commit, never parsed from the message bytes.

Functions

make-outbox-dir-root

(make-outbox-dir-root &key store head)

Construct the /mail/outbox directory node over the mail STORE and its fenced HEAD. The /mail axis root instantiates this and mounts it under /mail; the staged submission commits through STORE/HEAD at clunk.

Package valis/src/namespace/mail-queue

Classes

mail-queue-dir-root

The /mail/queue directory node: a synthesized body-free status projection over the transport queue. STORE is the content-addressed block store the queue lives in; HEAD is its fenced head, carried for the construction contract (the live read authority is the on-disk head). node-entries / node-walk query the head on every call, never a snapshot. Read-only by construction: no write paths exist, and the ops grant wraps this node in read-only-projection-node at mount. A nil STORE (fabric not up) lists empty rather than signalling.

Functions

make-mail-queue-dir-root

(make-mail-queue-dir-root &key store head)

Construct the /mail/queue status projection over the mail STORE and its fenced HEAD. The /mail axis root instantiates this and mounts it under /mail; the operational grant wraps it in read-only-projection-node at mount.

Package valis/src/namespace/modules

Classes

modules-dir-root

The /modules directory. Its entries are install and manifest; any other name walks to nothing.

modules-install-file

The owner's install door, /modules/install, made fresh for each walk so one fid's request and outcome are its own.

Functions

explain-module

(explain-module system &key (store *admit-vouch-store*) (manifest (read-durable-module-manifest)) designation)

Which two signatures admitted the module SYSTEM, and when, as a plist. A module MANIFEST records as resident answers (:resident t). Otherwise the plist explains DESIGNATION, or the designation MANIFEST records for SYSTEM when DESIGNATION is NIL, from what the vouch STORE holds for it: :designation, the designation explained; :publisher, the did:key of the publisher anchor its vouch is rooted at, :unreadable for a vouch that does not decode, or NIL for none; :owner-credential, :per-module-grant, :standing-grant, or NIL for no grant or one of neither kind; :grant-hash, the owner grant's hash as 64 lowercase hex, or NIL when it has none; and :admitted-at, the Unix time of the admission, or NIL when none is recorded. Answers NIL when MANIFEST records no SYSTEM and DESIGNATION is NIL. No value holds vouch or grant text.

make-modules-dir-root

(make-modules-dir-root &key fence-check boot-refusals boot-manifest-unreadable)

Construct the /modules directory. FENCE-CHECK, NIL or a function of no arguments that signals fenced-out when a newer instance of this node holds the instance fence, is handed to each committed request. BOOT-REFUSALS and BOOT-MANIFEST-UNREADABLE, each NIL or a function of no arguments, answer what the running node's boot left out, as (system . reason) pairs, and why it could not read its module manifest, or NIL; /modules/manifest renders the kind of each, never its text, beside each record. The fabric binds modules-dir-root to one at start; the assembler mounts it in the owner's frame only.

module-manifest-view

(module-manifest-view &key (manifest-store (durable-manifest-store)) (store *admit-vouch-store*) (source-root (or *modules-source-root* (resolve-module-source-root))) boot-refusals boot-manifest-unreadable)

The module set as /modules/manifest renders it, for use at the REPL: one plist per module the durable manifest of MANIFEST-STORE records, then one per module this image loaded with no record, as %module-views answers them against the vouch STORE and SOURCE-ROOT. BOOT-REFUSALS, a list of (system . reason) pairs, and BOOT-MANIFEST-UNREADABLE, a reason or NIL, stand for what the boot left out; both default to NIL. Signals module-manifest-corrupt or module-manifest-read-failed when the manifest cannot be read.

Variables

*module-change-function*

The function designator a committed request is handed to, resolved at each call, so a redefined composer is the one called: called with the designation, :source-root, :fence-check when the door has one, :source when the request names one, and :apart-from, the node's key and data directories, with a dist source, it returns a module-change-outcome or signals module-change-refused or module-change-failed-after-load. change-module-for-owner by default; a test binds a stub to pin the door without loading code.

*modules-dir-root*

The /modules directory node: set by the fabric at start and cleared at stop; NIL means the owner's frame has no /modules.

*modules-source-root*

The module source root the door installs from: set by the fabric from the root it was started with, NIL for the configured module source root.

Package valis/src/namespace/names

Classes

names-cert-dir

The axis-level cert control directory, names/cert. Its obtain child accepts the ACME first-obtain verb, driven in the resident that serves :53; its list child is the read-only listing of the certificates the node holds. Only the owner's /names carries it (bind-names-owner-doors); the shared :names node grants resolve against never names it. It sits beside the per-zone surfaces rather than under one, since an obtain is not scoped to a single zone (a fresh node obtains its first cert before any zone edit).

names-cert-list-file

The read-only certificate list, names/cert/list. A read returns one newline-terminated line per certificate the node holds in custody, sorted by the name it is filed under, with five tab-separated fields in order: the filed name; its not-after instant; its next-renewal instant; whether the running :443 edge serves it (served, partial, not-served, no-edge or unconfirmed); and the DNS names it carries, comma-joined. Both instants are integers in the encoding the obtain door's next-renewal line carries, or unknown when they cannot be read. A node holding nothing renders an empty file. A field the node cannot read renders unknown or unconfirmed, and custody that cannot be enumerated renders an empty file, so the read never signals for the node's own faults. No key material is rendered. The read answers only the node's owner; any other caller is refused with a nine-p-error. The rendering is memoised for the node's life and the cert directory hands out a fresh node per walk, so a stat's length and a read's octet count agree. Only the owner's /names carries it (bind-names-owner-doors).

names-cert-obtain-file

The axis-level cert obtain /ctl node, names/cert/obtain. The owner writes a newline-framed obtain verb (the identifiers, the account contact, and the optional profile / directory-url) then reads the outcome and clunks. The verb drives an ACME first-obtain IN this resident so the dns-01 challenge TXT is refreshed into the serving index by the in-process commit hook; the outcome is a newline-framed reply body whose FIRST line is the machine token obtained, followed by one domain line per issued identifier, a next-renewal line, an edge line saying whether the running :443 edge serves the certificate now, and, when the edge took the rebuilt names, a name line for each name the certificate carries saying whether the edge hands it that certificate. Because the client reads before it clunks, the read is the commit point: the order runs on first demand and the result is memoised for the fid's lifetime, so read and clunk together drive exactly one order and a fid abandoned without either drives none. A twin of the record /ctl node: it inherits the staging buffer and node-write, but overrides node-close to run the order and map the obtain conditions (a missing contact, a non-authoritative identifier, a live-order failure) to a clean nine-p-error so a refusal returns an Rerror rather than escaping the clunk thread. Only the owner reaches it: the cert directory exists only in the owner's frame (bind-names-owner-doors), and the shared :names node that grants resolve against does not name it, so no grant over /names or naming this door can drive an order.

names-cutover-ctl-file

The per-zone /ctl flip node: the operator writes a cutover verb (answer-as-secondary | promote-to-primary) and clunks to commit the transition for this zone via commit-cutover-transition. Reached as a child of the per-zone read node; the published-by-design read node stays read-only (#o444): control writes ride this separate writable child. Cutover is delegable: the owner and the holder of a write grant over /names may flip a zone here, and a read grant reaches only a read-only projection whose write refuses. The cert door is different: only the owner's frame carries it.

names-cutover-declare-file

The axis-level /ctl declaration node: the operator gate that bootstraps a brand-new HELD secondary BEFORE its first transfer. A cold secondary has no per-zone node yet (its zone does not exist), so declaration lives at the :names axis root, not per-zone. The operator writes "declare-secondary <origin> <master-ref>" and clunks; node-close runs declare-secondary-zone, which creates the held zone shell + the upstream-master allowlist entry in FK order so the first transfer can self-authorize.

names-dir-root

The :names axis root directory node. GENERATION-READER is a thunk returning the live store write-epoch (my-generation) this instance holds; the fabric sets it to read the durable head generation LIVE per call, so every commit / delete asserts the instance fence against the current epoch (never a cached snapshot). node-create mints a master-file submission staging node; node-walk / node-entries surface each existing zone as a per-zone node read from the operator-state PG seam.

names-owner-dir

The owner's /names: the shared :names axis with the cert directory added beside it. Only the owner's frame carries it, so a grant over /names, or one naming /names/cert/obtain, finds no cert door and cannot drive an ACME order.

names-record-ctl-file

The per-zone structured record write node, names/<origin>/records/ctl. The owner writes a newline-framed verb (add-record / delete-record / replace-record) then reads the outcome and clunks. The verb is dispatched to the durable single-rrset-member seam under the session principal's ownerdid, and the outcome is reported as a newline-framed reply body whose FIRST line is a machine token from a closed set (committed-served or committed-unserved) followed by key/value lines. committed-unserved is a SUCCESS: the edit is durable but did not reach the serving side, which is a distinct outcome from a refusal and rides the success body so a client never reads a landed edit as a failure.

Because the client reads before it clunks, the read is the commit point: the staged verb is committed on first demand and the result memoised for the fid's lifetime, so read and clunk together commit exactly once and a fid abandoned without either commits nothing. A twin of the cutover /ctl node: it inherits the staging buffer and node-write, but overrides node-close to map the record-domain conditions (malformed rdata, a structural invariant violation, a not-found replace target, a superseded instance) to a clean nine-p-error. A refusal signals with its own ename and never reaches the reply rendering, so it returns an Rerror rather than crashing the serve path. Record editing is delegable: the owner and the holder of a write grant over /names may edit records here, and a read grant reaches only a read-only projection whose write refuses.

names-record-list-node

The per-zone structured record read node, names/<origin>/records/list. node-read renders the zone's DURABLE records as a table of tab-separated lines (digest owner ttl class type rdata), read through the connection seam from list-durable-zone-records, the published-by-design structured editing surface, with a stable per-member digest identity. The rendering is memoised for the fid's lifetime. A file node, no directory override.

names-records-dir

The per-zone structured record directory, names/<origin>/records. Its `list` child renders the durable records as a digest-keyed table; its `ctl` child accepts the add/delete/replace record verbs, from the owner or a write grant over /names. Reached by an explicit walk off the per-zone node and surfaced in that node's entries, kept distinct from the per-zone master node-read so the exterior-nameserver-seam rendering stays intact.

names-submission-file

A staging master-file, the mail outbox tmp twin. RFC 1035 master text written across node-write calls accumulates in BUFFER; nothing is parsed or written to PG until node-close (the clunk) commits the atomic full-zone replace. A fid abandoned without a clunk never reaches node-close, so it imports nothing: complete-or-absent, structural. SUBMITTER-DID was captured from the session principal at the write gate (node-create) and is stamped as the zone's write- authority owner DID at commit, never parsed from the zone bytes.

names-zone-control-dir

The axis-level zone control directory, names/zone. Its create child mints a new domain and enrols its shared secondary as one act. It lives at the axis root rather than under a per-zone node for the same reason the secondary declaration does: a domain that does not exist yet has no per-zone node to reach, which is exactly the create case.

names-zone-create-file

The axis-level zone create door, names/zone/create. The owner writes a newline-framed create verb – the origin, the instance address, the secondary to enrol, whichever template parameters differ from the defaults, and zero or more typed override records – then reads the outcome and clunks. The verb mints the domain's default record set and enrols the shared secondary as ONE act; the outcome is a newline-framed reply whose FIRST line is a machine token from a closed set, followed by key/value lines and the BIND fragment the secondary side pastes. A twin of the cert obtain door: it inherits the staging buffer and node-write, opens :rdwr, and memoises the terminal outcome for the fid's lifetime so a read and a clunk together create exactly one domain and a fid abandoned without either creates none. Every create-domain condition is mapped to a clean nine-p-error at this boundary, so a refusal returns an Rerror rather than escaping the clunk thread. Zone creation is delegable: the owner and the holder of a write grant over /names may create a domain here, and a read grant reaches only a read-only projection whose write refuses. The cert door is different: only the owner's frame carries it.

names-zone-master-node

One existing zone's flat master-file read node, the `master` child of the per-zone directory (names/<origin>/master). node-read renders the zone's full serving set as canonical master-file lines via %render-zone, the published-by-design, world-readable exterior-nameserver read seam preserved from the pre-directory per-zone node. The rendering is memoised for the fid's lifetime so length / read report one consistent snapshot. A file node, no directory override.

names-zone-node

One existing zone's per-zone node, a directory keyed by canonical ORIGIN and surfaced in the :names listing. It walks as a directory (a qtdir QID override, the same the axis root and the records directory apply) into three children: `master` renders the zone's full serving set as canonical master-file lines (published-by-design, world-readable: reads require no capability); `ctl` accepts the cutover flips; `records` is the structured record-editing directory. node-remove deletes the whole zone (apex/SOA removal = whole-zone removal): removing the zone directory removes the zone. Cutover and removal are delegable: the owner and the holder of a write grant over /names may flip or remove a zone here, and a read grant reaches only a read-only projection whose write and remove refuse. The cert door is different: only the owner's frame carries it.

Conditions

illegal-cutover-transition

Signaled by commit-cutover-transition when the requested cutover flip is not legal per runciter's cutover-transition-legal-p (consumed through the serving seam), or when no held anchor exists to flip. The persisted state is left unchanged; node-close translates this to a clean nine-p-error.

secondary-timer-not-ready

Signaled by commit-cutover-transition when a flip to :answer-as-secondary is attempted without a populated, non-expired persisted expiredeadline. An answering secondary must have a live SOA expiry from its last successful transfer; without one the flip is refused (commits nothing), so a secondary can never be made answerable with a stale or absent timer. node-close translates this to a clean nine-p-error.

zone-already-exists

Signaled by commit-zone-create when the origin already holds a zone. The refusal NAMES the origin, because the correction differs entirely by which domain it is: an operator who meant to edit reaches for the record verbs, and an operator who meant to replace reaches for the import door. commit-zone-records is a full-zone REPLACE, so a create that reached it would destroy the existing zone's records; this is the condition that stops it.

Generic functions

cert-obtain-refusal

(cert-obtain-refusal object)

Undocumented: this exported symbol needs a docstring.

cert-obtain-reply

(cert-obtain-reply object)

Undocumented: this exported symbol needs a docstring.

illegal-cutover-transition-from

(illegal-cutover-transition-from condition)

Undocumented: this exported symbol needs a docstring.

illegal-cutover-transition-origin

(illegal-cutover-transition-origin condition)

Undocumented: this exported symbol needs a docstring.

illegal-cutover-transition-to

(illegal-cutover-transition-to condition)

Undocumented: this exported symbol needs a docstring.

names-owner-shared

(names-owner-shared node)

Return the shared :names axis node beneath the owner's /names. I keep it reachable so that everything the owner does under /names, other than the cert door, lands on the same axis a grant resolves against, and the two views of a zone never diverge.

secondary-timer-not-ready-origin

(secondary-timer-not-ready-origin condition)

Undocumented: this exported symbol needs a docstring.

zone-already-exists-origin

(zone-already-exists-origin condition)

Undocumented: this exported symbol needs a docstring.

zone-create-refusal

(zone-create-refusal object)

Undocumented: this exported symbol needs a docstring.

zone-create-reply

(zone-create-reply object)

Undocumented: this exported symbol needs a docstring.

Functions

bind-names-owner-doors

(bind-names-owner-doors names-node)

Return NAMES-NODE as the owner's frame mounts it: the same :names axis with the cert directory, whose obtain door drives an ACME first-obtain, added beside it. Call it only while assembling the owner's frame. NAMES-NODE itself never names the cert directory, because a grant resolves its designation by walking it, and obtaining a certificate is the owner's alone.

commit-cutover-transition

(commit-cutover-transition origin to-state my-generation)

Commit an operator cutover flip of the zone at canonical ORIGIN to TO-STATE (:answer-as-secondary | :primary), an observable substrate-level state transition. runciter's pure cutover-transition-legal-p decides legality (through the valis-owned serving seam); an illegal flip (or no held anchor to flip) signals illegal-cutover-transition and persists nothing.

A flip to :answer-as-secondary additionally REQUIRES a populated, non-expired persisted expiredeadline: an answering secondary must carry a live SOA expiry from its last successful transfer. Without one (absent or already past the live clock) the flip is refused with secondary-timer-not-ready and persists nothing: a secondary can never be made answerable with a stale or absent timer.

A legal flip persists the new cutoverstate on the ndbentry anchor inside a fenced transaction (assert-fence-touch FIRST: a superseded instance flips nothing); a promotion to :primary additionally flips authoritystate to primary, valis becoming the system of record.

valis maintains no serving view of its own, so the commit reloads nothing: after the durable commit the in-process zone-change signal fires (origin, current serial) so the c3po-dns wire feed and runciter's lazy pull observe the change. Runs inside the :names connection seam. Returns TO-STATE.

commit-zone-create

(commit-zone-create payload my-generation owner-did)

Create the domain PAYLOAD describes and enrol its shared secondary as ONE act, under OWNER-DID and the store write-epoch MY-GENERATION. Returns (values CANONICAL-ORIGIN SERIAL RECORD-COUNT PEER KEY-NAME SECRET-BASE64 MASTER-ADDRESS UNSERVED-REASON) for the reply. The zone rows and the secondary's two allowlist rows share one transaction, so a failure anywhere leaves neither: an allowlist row authorising a peer to pull a zone that does not exist is the residue this ordering exists to prevent. The TSIG custody file is deliberately OUTSIDE that transaction, because a file write cannot be rolled back; the correct state after a failed create is therefore no zone, no allowlist rows, and an orphaned key, and a re-run reuses that key rather than minting a second one.

make-names-cert-dir

(make-names-cert-dir)

Construct the axis-level cert control directory child.

make-names-cert-list-file

(make-names-cert-list-file)

Construct a certificate list node; its content is rendered on first stat or read.

make-names-cert-obtain-file

(make-names-cert-obtain-file)

Construct the axis-level cert obtain /ctl node. The staging base fences on a generation-reader it never consults here (an obtain does not fence against the store epoch), so a constant reader is passed.

make-names-dir-root

(make-names-dir-root &key (generation-reader (constantly 0)))

Construct the :names axis root. GENERATION-READER is a thunk returning the live store write-epoch this instance holds; the fabric passes a reader over the durable head so every commit / delete fences against the current generation. root.lisp mounts the result under :names as a /mail sibling in the owner's canonical frame.

make-names-records-dir

(make-names-records-dir origin generation-reader)

Construct the per-zone structured record directory for canonical ORIGIN over the live GENERATION-READER.

make-names-zone-control-dir

(make-names-zone-control-dir generation-reader)

Construct the axis-level zone control directory over the live GENERATION-READER.

make-names-zone-create-file

(make-names-zone-create-file generation-reader)

Construct the axis-level zone create door over the live GENERATION-READER. Unlike the cert obtain door this one fences: a create is a mutating write into operator state, so it asserts the current store write-epoch and a superseded instance creates nothing.

make-names-zone-master-node

(make-names-zone-master-node origin)

Construct the per-zone flat master-file read node for canonical ORIGIN.

Variables

*max-zone-master-bytes*

The upper bound on a staged zone master file, in bytes. node-write accumulates octets across arbitrarily many Twrite frames into one fid's submission buffer before the clunk commits; seven's per-frame oversize guard bounds a single frame but not the running total, so without this ceiling an authenticated writer could stream unbounded data into one fid and grow image memory without limit. 16 MiB is generous for the largest legitimate zone (a fully DNSSEC-signed zone with many records runs to a few MiB) while foreclosing the unbounded-accumulation abuse.

*names-dir-root*

The :names axis node. Set by the fabric at start-fabric when an operator-state pool is configured; nil before the fabric is up, after teardown, or when no operator-state pool is present. root.lisp's node-walk walks :names through this variable, so a nil value means the axis is absent (sovereignty-as-absence): the DNS management surface only exists where there is a PostgreSQL seam to write into.

*names-with-connection*

The connection seam the :names commit / read / delete paths run their PG work through. In production it is the default that wraps with-operator-state-connection (acquire-late / release-early, on the serve/worker thread, never the epoll listener). A PG acceptance test binds it to a pass-through that runs the thunk on the ambient disposable-schema connection, so the same node path the operator drives is exercised end to end against a throwaway schema.

Package valis/src/namespace/node-ctl

Classes

node-dir-root

The /node axis root. Its fixed leaves are boot and ctl; any other name walks to nothing.

Generic functions

node-dir-boot-identity

(node-dir-boot-identity root)

The boot identity ROOT, a /node axis root, serves at /node/boot: lower-case hex drawn from the kernel when ROOT was made, which the fabric does once at every start. Two reads that differ came from two starts of the node.

Functions

make-node-dir-root

(make-node-dir-root)

Construct the /node axis root with a fresh boot identity. The fabric binds node-dir-root to one at start; the assembler mounts it in the owner's frame only.

restart-reply

(restart-reply outcome)

The reply line, newline included, that answers a restart whose OUTCOME request-host-restart returned.

Variables

*node-dir-root*

The /node axis node. Set by the fabric at start and cleared at stop; nil means the owner's frame has no /node.

Package valis/src/namespace/projection

Classes

read-only-open-wrapper

Wraps an inner open-object after a read-mode open on a projection. seven binds this as the fid's open-object; all subsequent dispatches (node-read, node-write, node-close, node-iounit) go here first. Reads and closes delegate to the inner open-object; writes are denied unconditionally, closing the bypass path that would otherwise let a read-mode Topen on a writable inner node escape the projection.

read-only-projection-node

Deep read-only proxy. Wraps a backend-node subtree exposing walk/read/stat only; write/create/remove/wstat signal nine-p-error. Children walked through a projection are themselves wrapped in fresh projections (lazy, avoids pre-building the whole subtree). QID passes through to the inner node.

NOT a backend-node subclass: node-qid passes through the inner node's QID to preserve resource identity on the wire. A projection wraps a view, not an object.

node-open returns a read-only-open-wrapper rather than the raw inner open-object so that seven's post-open node-write dispatches still reach the deny implementation: the wrapper is the enforcement carrier for the full open lifetime.

Generic functions

projection-inner

(projection-inner object)

Undocumented: this exported symbol needs a docstring.

read-only-open-wrapper-inner-open-obj

(read-only-open-wrapper-inner-open-obj object)

Undocumented: this exported symbol needs a docstring.

Functions

make-projection

(make-projection inner)

Wrap INNER in a read-only-projection-node.

Package valis/src/namespace/proto

Classes

proto-dir

The /proto directory node. node-entries and node-walk call registered-protocols fresh on every invocation; opening /proto in a 9P client snapshots the result at that moment.

Functions

make-proto-dir

(make-proto-dir)

Construct a fresh proto-dir node.

Package valis/src/namespace/pub

Classes

pub-dir-root

The /pub directory node. node-entries and node-walk query the store on every invocation, never a snapshot stored at construction time. A nil pub-store means the store is not yet up; both methods return nil/empty safely rather than signalling.

Functions

make-pub-dir-root

(make-pub-dir-root)

Construct a fresh pub-dir-root node.

Variables

*pub-dir-root*

The /pub axis node. Set by start-fabric; nil before the fabric is up or after stop-fabric. root.lisp's node-walk walks /pub through this variable.

Package valis/src/namespace/root

Classes

dev-auth-node

Development credential stub for the auth seam. Any Twrite completes the dev auth conversation. The live fabric uses a real Noise/factotum credential check (noise-auth-node) in its place.

valis-root

The valis 9P attach root. seven's server hands this node to each Tattach; node-walk dispatches the three top-level axes (/proto, /bus, /id) to the module-level seam variables the fabric controller wires at startup.

Functions

attach-handshake

(attach-handshake principal)

Return the auth conversation PRINCIPAL was admitted through, or nil when none was recorded: the unauthenticated path, or a principal built outside an attach.

current-principal

(current-principal)

Return the resolved principal for the request currently being served.

Reads the opaque identity seven recorded on the 9P session at Tattach via node-attach-identity, surfaced through seven/src/server:current-identity for the dynamic extent of request dispatch. Returns a keyed principal for a session admitted by NoiseXX, or the anonymous sentinel for a NOFID attach. Never returns nil within a request: if the identity slot is empty (called outside dispatch, or on a session whose backend recorded nil), this collapses to the anonymous sentinel so callers can treat the return value uniformly through principal-p / anonymous-p.

Dynamic-extent boundary: current-identity resolves only on the session thread inside %dispatch (see seven/src/server). A standard file node's node-read runs synchronously on that thread, so current-principal is correct during node-open / node-read / node-write. A backend that overrides node-read-async to park off-thread (the /bus pattern) must capture the principal at the synchronous call site and close over it for delivery: current-principal at the delivery site is outside the dynamic extent and will collapse to anonymous.

make-valis-root

(make-valis-root)

Construct a fresh valis-root node.

note-attach-handshake

(note-attach-handshake principal handshake)

Record that PRINCIPAL was admitted through HANDSHAKE, and return PRINCIPAL so the caller can wrap the value it was already producing. A nil HANDSHAKE records nothing: the unauthenticated path has no conversation to name, and a nil row would make every anonymous attach look like the same one.

Variables

*base-view-fn*

Callback set by the namespace assembler to build-base-view. Wired at assembler load time to break the circular package dependency: assembler imports root (for valis-root and %walk-designation), so root cannot import assembler at compile time. When nil (assembler not loaded, unit tests that do not stand up the full fabric), node-attach-root's plain branch returns an empty view, fail-closed: nobody receives an ambient namespace by accident.

*bearer-view-decorate-fn*

A function of (view-root auth-identity grant-string) called on each view-root built for a VERIFIED bearer-name attach; returns the view to bind (normally the same object). Set by start-fabric to the assembler's decorate-bearer-view, which stamps the grant hash on the view's mount entries, adds the per-view ctl door for phase-two mount/unmount, and weak-registers the view for revocation eviction; cleared by stop-fabric. Nil binds bearer views bare, correct outside a running fabric. A function-valued hook because the assembler imports this package, so this package cannot import the assembler back.

*edge-dir*

The /edge status directory node (the live edge controller projection) for this valis image. Set by the fabric controller at start-fabric time; read by valis-root's node-walk / node-entries and mounted into the owner's canonical frame by the assembler. When nil (no fabric started, or a teardown window), the /edge walk misses and the axis is simply absent.

Owned here rather than in src/namespace/edge.lisp to break a load cycle: edge.lisp pulls in the edge controller, which transitively reaches the assembler, which imports this package, so root cannot import edge back. The node class and its constructor live in edge.lisp (which root never imports); the seam variable lives upstream here, and the fabric (downstream of both) calls make-edge-dir to fill it. The same upstream-variable / downstream- constructor split the other axis seam variables use.

*factotum*

The factotum directory node (the live /id subtree) for this valis image. Set by the fabric controller at start-fabric time; read by valis-root's node-walk / node-entries. When set, takes precedence over id-stub under the /id axis; when nil (no fabric started, or unit tests touching the root directly), node-walk falls through to id-stub so the axis remains traversable for tests that do not stand up the full fabric.

*id-stub*

The /id stub directory node for this valis image. Set by the fabric controller at start-fabric time.

*proto-dir*

The /proto directory node for this valis image. Set by the fabric controller at start-fabric time; read by valis-root's node-walk / node-entries.

*valis-bus*

The /bus directory node (seven's bus-dir) for this valis image. Set by the fabric controller at start-fabric time.

Package valis/src/namespace/session-door

Classes

session-ctl-node

The /id/session-ctl file. One command line per write:

fence SESSION-ID

Only the owner writes here: a non-blank write from any session the gate does not admit reads "error not-owner" and runs nothing, and a node built without a gate admits no one.

Fencing empties that session's namespace, so it can name nothing until it authenticates again. Fencing the session that issued the command is allowed and takes effect at once: the result stays readable on the handle already open, and nothing else is reachable afterwards.

Unknown verbs are ignored, because a control file must not become an eval surface. A malformed argument to a known verb produces an "error …" read response and never a signal into the 9P layer.

sessions-node

The /id/sessions file. Read-only; one line per live session giving its id, the principal it was admitted as, when it attached, when it was last used, and whether it has been fenced.

Only the owner reads it: a read from any session the gate does not admit returns zero octets, and a node built without a gate admits no one.

The id in the first column is what an operator writes to /id/session-ctl. It exists because the registry is keyed by an object no one outside the image can name, and an operator looking at a session they want stopped has to have something they can type.

Functions

make-session-ctl-node

(make-session-ctl-node &key caller-permitted-p)

Construct a fresh /id/session-ctl command node. CALLER-PERMITTED-P is a function of no arguments, true when the session writing is the owner; without one the node refuses every command.

make-sessions-node

(make-sessions-node &key caller-permitted-p)

Construct a fresh /id/sessions listing node. CALLER-PERMITTED-P is a function of no arguments, true when the session reading is the owner; without one every read returns zero octets.

Variables

*session-descriptor-fn*

Returns the live sessions as a list of plists carrying :ID, :PRINCIPAL, :ADMITTED-AT, :LAST-ACTIVITY and :FENCED-AT.

Nil when no fabric is running, in which case the listing reads as empty rather than failing. That is the truthful answer: with no fabric there are no sessions.

Installed by start-fabric and cleared by stop-fabric. The registry lives downstream of this file and cannot be imported back into it, which is why the connection is made at start time rather than at compile time.

*session-fence-fn*

Fences the session named by its one argument, and returns true when a live session carried that id.

Nil when no fabric is running; a fence attempted then reports the session as absent, which is accurate. Installed and cleared alongside SESSION-DESCRIPTOR-FN.

Package valis/src/namespace/sign-ctl

Classes

sign-ctl-file

The owner's signing door, /cap/sign, made fresh for each walk so one fid's verb and outcome are its own.

Functions

make-sign-ctl-file

(make-sign-ctl-file)

A fresh signing door.

Variables

*sign-module-source-root*

The module source root a release is staged under while it is signed: set by start-fabric, NIL for the configured module source root, and bound by tests.

*sign-publisher-directory*

The directory of mercer's publisher store the door signs through: set by start-fabric, NIL while no fabric is up, and bound by tests.

*sign-refused-source-directories*

The directories holding this node's keys and data, set by start-fabric: a release under one of them, or holding one, is never staged to be signed. mercer's publisher store is refused whatever this holds.

Package valis/src/namespace/union-node

Classes

union-node

An ordered union of backend-node directories. Implements 9Front bind semantics: the first member whose subtree contains a name wins on walk; all members' entries are concatenated without deduplication on read.

Each member tracks its own grant hash and its own rights bitmask so revocation sweeps can remove individual members without over-evicting sibling grants, and so the union entry's rights-bits can be recomputed from survivors after each per-member eviction.

Generic functions

union-node-member-grant-hashes

(union-node-member-grant-hashes object)

Undocumented: this exported symbol needs a docstring.

union-node-member-rights-bits

(union-node-member-rights-bits object)

Undocumented: this exported symbol needs a docstring.

union-node-members

(union-node-members object)

Undocumented: this exported symbol needs a docstring.

union-node-name

(union-node-name object)

Undocumented: this exported symbol needs a docstring.

Functions

make-union-node

(make-union-node members &key (name "") (member-grant-hashes nil) (member-rights-bits nil))

Construct a union-node with MEMBERS (an ordered list of backend-nodes, first member searched first on walk). NAME is the leaf name reported by node-stat; defaults to the empty string. MEMBER-GRANT-HASHES is an optional parallel list of grant hash octet vectors (or nils) in 1-to-1 correspondence with MEMBERS; when omitted, all hashes default to nil (no revocable grant for any member). MEMBER-RIGHTS-BITS is an optional parallel list of integer rights bitmasks in 1-to-1 correspondence with MEMBERS; when omitted, all rights default to 0.

union-node-evict-member-by-hash

(union-node-evict-member-by-hash union hash)

Remove all members of UNION whose grant hash equals HASH (byte-equalp). Returns three values: the updated UNION, T if at least one member was removed (NIL if no member matched), and the bitwise OR of the surviving members' rights bitmasks (0 when the union is now empty). The caller should update the enclosing mount-entry's rights-bits to this third value so the entry never advertises rights from a revoked grant.

HASH comparison uses EQUALP on octet vectors. A nil member-grant-hash means that member has no revocable grant and is never removed here.

union-node-evict-write-members

(union-node-evict-write-members union)

Remove every capability-backed, write-capable member of UNION. A member is capability-backed when its grant hash is non-nil; it is write-capable when its rights bitmask carries the write right. Members with no grant hash are base-policy and are never removed here.

Returns two values: T if at least one member was removed, and the bitwise OR of the surviving members' rights bitmasks (0 when the union is now empty). The caller updates the enclosing mount-entry's rights-bits to the second value so the entry never advertises rights from a swept grant.

union-node-member-count

(union-node-member-count union)

Return the number of members in UNION.

Package valis/src/namespace/view-ctl

Classes

view-ctl-node

The /ctl file in an assembled view. node-open mints a fresh view-ctl-conversation; node-write parses a mount/unmount command; node-read returns the result. mode #o600: only the session holder should write it.

Functions

make-view-ctl-node

(make-view-ctl-node &key view)

Construct a view-ctl-node whose :view slot points to VIEW (a view-root). VIEW may be NIL at construction time; node-open guards against a nil view.

Package valis/src/namespace/view-root

Classes

mount-entry

One row in a view-root's mount table. NAME is the leaf name under which NODE is reachable; GRANT-HASH is the SHA-256(canonical-bytes || signature) of the capability grant that produced this entry (nil for base-policy entries created at connect-time, which are unaffected by the revocation eviction sweep); RIGHTS-BITS is the integer bitmask from the grant's mount-directive; BIND-FLAG is :replace, :before, or :after (default :after, per 9Front bind semantics).

view-root

A per-attach 9P root whose children are the mounted grants for one session. Returned by node-attach-root on valis-root; seven binds it as the session's attach fid. Mount-table entries are mount-entry structs.

Generic functions

view-root-mount-table

(view-root-mount-table object)

Undocumented: this exported symbol needs a docstring.

view-root-principal

(view-root-principal object)

The principal the view-root OBJECT was built for, or NIL for a view built for no principal.

view-root-session-entry

(view-root-session-entry object)

Undocumented: this exported symbol needs a docstring.

Functions

%make-mount-entry

(%make-mount-entry &key ((:name name) "") ((:node node) nil) ((:grant-hash grant-hash) nil) ((:rights-bits rights-bits) 0) ((:bind-flag bind-flag) :after))

Undocumented: this exported symbol needs a docstring.

make-view-root

(make-view-root &key (mount-table (quote nil)) (principal nil))

Construct a view-root carrying MOUNT-TABLE, a list of mount-entry structs, built for PRINCIPAL (nil for none). An empty list is valid: the session is admitted but can reach nothing.

mount-entry-bind-flag

(mount-entry-bind-flag instance)

Undocumented: this exported symbol needs a docstring.

mount-entry-grant-hash

(mount-entry-grant-hash instance)

Undocumented: this exported symbol needs a docstring.

mount-entry-name

(mount-entry-name instance)

Undocumented: this exported symbol needs a docstring.

mount-entry-node

(mount-entry-node instance)

Undocumented: this exported symbol needs a docstring.

mount-entry-rights-bits

(mount-entry-rights-bits instance)

Undocumented: this exported symbol needs a docstring.

Variables

*session-activity-fn*

A function of one argument (the view-root) called on each session operation that reaches this view: a walk from the session root, or a listing of it. It stamps the session's last-activity mark and, when the deployment configures an idle bound, empties an over-idle session's view before the operation resolves, so an abandoned session cannot be picked up and used.

Nil means no session bookkeeping, which is the correct state outside a running fabric: a unit test that builds a view-root directly gets today's behavior unchanged. Installed by start-fabric and cleared by stop-fabric, the same downstream-installs-the-hook arrangement the rest of the namespace seams use, and for the same reason: the session registry lives downstream of this file and cannot be imported back into it.