Capabilities

The parts of the Endpoints layer a server holds out, stated in the capabilities object of its info response: the client image store, the registries it pulls from, and each volume mode with what may hold a volume of it; and, for every capability, the endpoints it governs and exactly what the server sends for a request past a capability it does not hold out.

A server serves every endpoint of the Endpoints layer: it reads every request and answers it as that layer provides. Some of what a request may ask, a server may not do: keep a client image store, pull from a registry, keep volumes of a mode, let more than one party hold a volume at once. Each such part is a capability. The server states the capabilities it holds out in the capabilities object of its info response, and this layer defines that object and binds the server by it: for every capability, the endpoints it governs, what the server does where it holds the capability out, and exactly what the server sends for a request past a capability it does not hold out.

  • The object is a fact of the server. The server sends the same capabilities object in every info response it sends, on every connection, for as long as it runs. The object does not depend on the identity under which a connection was authorized, on the request, or on the time.
  • A capability held out is served. A server that holds out a capability serves the endpoints it governs as the pages of the Endpoints layer state, in full, and sends none of the responses this layer defines for the capability’s absence.
  • A capability not held out is refused as this layer states. A server that does not hold out a capability answers every request past it with exactly the response this layer names for that endpoint, as the first and only response on the scope, followed by the response finish, serves no channel on the scope, looks nothing up for the request, holds nothing, fetches nothing, deploys nothing and changes nothing. The server sends no error, no other refusal and no bare finish in place of the response named.
  • Every page of the Endpoints layer is read with this one. A requirement of an endpoint page that a capability governs holds where the server holds the capability out; where it does not, the response this layer names is the whole of the server’s answer, and the endpoint page’s requirements on what follows the request do not arise.

The object#

The capabilities member of the info response is one JSON object, read as Notation provides, with the members tabulated. Every member is optional. An absent member has the meaning the table gives its absence, and a server that does not hold out a capability may send the member with that value or omit it; the two are one statement. The server sends no member this revision does not define.

MemberTypeAbsentGoverns
images_storebooleanfalseimages::list, images::stat, images::build, images::delete, images::oci, and the client source of images::check
images_registriesarray of strings[]the registries source of images::check, and where a run obtains its image
volumes_persistentobject, the mode objectevery member false: no volume of the modethe eleven volume endpoints, and the volume mounts of the two runs, for a volume whose mode is persistent
volumes_ephemeralobject, the mode objectevery member false: no volume of the modethe same, for a volume whose mode is ephemeral
volumes_read_onlyobject, the mode objectevery member false: no volume of the modethe same, for a volume whose mode is read_only

The object is defined by diverge-sdk-rs/src/provider/endpoints/info/server/response/capabilities.rs:

//! What a provider does, of everything this specification lets a
//! provider do.

use serde::{Deserialize, Serialize};

use super::VolumeCapabilities;
use crate::provider::endpoints::volumes::Mode;

/// Which of the specification's optional parts this provider serves.
///
/// Every provider serves every endpoint's wire — it reads every
/// request and answers it — but not every provider does everything a
/// request may ask: one has no image store, one pulls from no
/// registry, one has no FUSE on its host, one keeps volumes of some
/// modes only, and keeps them more narrowly than the mode allows.
/// Each such part is one member here, and a request past it is not
/// a failure but a refusal the endpoint has a variant of its own for,
/// named below member by member. A caller that read these composes
/// no such request; one that did not reads the refusal.
///
/// # Flat, and tolerant
///
/// One object, every member at the top, each a boolean, a list or an
/// object of booleans; no member nests a capability inside another
/// but the three volume modes, whose object says what may be done
/// with a volume of the mode. A member absent is the capability
/// absent — `false`, the empty list, the object of every member
/// `false` — so a provider states only what it does. A member a
/// reader does not know is ignored: see [`Frame`](super::Frame).
///
/// # No mode is no volumes
///
/// A provider with none of the three volume modes keeps no volumes,
/// and every endpoint of
/// [`volumes`](crate::provider::endpoints::volumes) — the eleven —
/// answers `Unsupported`: nothing is listed, nothing is made, and
/// there is no volume for a run or a build to name. [`volumes`](Self::volumes)
/// is that test.
///
/// # What is not here
///
/// The volume endpoints themselves, which every provider that keeps
/// volumes serves;
/// [`daemons`](crate::provider::endpoints::daemons), which is the
/// provider's configuration and refused as such; and every quantity
/// — how many containers, how much disk — which is a limit, not a
/// capability, and answered where it binds.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
#[derive(schemars::JsonSchema)]
pub struct Capabilities {
    /// The provider keeps an image store for its callers: it answers
    /// [`images::list`](crate::provider::endpoints::images::list),
    /// [`stat`](crate::provider::endpoints::images::stat),
    /// [`build`](crate::provider::endpoints::images::build),
    /// [`delete`](crate::provider::endpoints::images::delete) and
    /// [`oci`](crate::provider::endpoints::images::oci), and an
    /// [`images::check`](crate::provider::endpoints::images::check)
    /// asking the `client` source looks in it. Absent — `false` —
    /// each of the five answers `Unsupported`, and a check asking only
    /// `client` answers `NoSource`.
    #[serde(default)]
    pub images_store: bool,
    /// The registries the provider pulls from: each a host as a
    /// [`Reference`](crate::shared::containers::request::Reference)
    /// names it — `docker.io`, `ghcr.io`, `registry.example.com:5000`
    /// — and the whole of them. An
    /// [`images::check`](crate::provider::endpoints::images::check)
    /// asking the `registries` source consults exactly these, and a
    /// reference naming any other is ignored; a run's image whose
    /// references name none of these, and that no store the provider
    /// has holds, cannot be supplied. Absent — empty — the provider
    /// pulls from no registry, and a check asking only `registries`
    /// answers `NoSource`.
    #[serde(default)]
    pub images_registries: Vec<String>,
    /// What may be done with a volume in the
    /// [`Persistent`](Mode::Persistent) mode. Every member `false` —
    /// the object absent — the provider keeps no volume in the mode:
    /// no listed volume is in it, and a
    /// [`create`](crate::provider::endpoints::volumes::create) or an
    /// [`edit`](crate::provider::endpoints::volumes::edit) naming it
    /// is answered `ModeUnsupported`. See
    /// [`kept`](VolumeCapabilities::kept).
    #[serde(default)]
    pub volumes_persistent: VolumeCapabilities,
    /// What may be done with a volume in the
    /// [`Ephemeral`](Mode::Ephemeral) mode. Every member `false`, as
    /// for [`volumes_persistent`](Self::volumes_persistent).
    #[serde(default)]
    pub volumes_ephemeral: VolumeCapabilities,
    /// What may be done with a volume in the
    /// [`ReadOnly`](Mode::ReadOnly) mode. Every member `false`, as
    /// for [`volumes_persistent`](Self::volumes_persistent).
    #[serde(default)]
    pub volumes_read_only: VolumeCapabilities,
}

impl Capabilities {
    /// Whether the provider keeps volumes at all: any mode among the
    /// three. `false` is every volume endpoint answering
    /// `Unsupported`.
    pub fn volumes(&self) -> bool {
        self.volumes_persistent.kept() || self.volumes_ephemeral.kept() || self.volumes_read_only.kept()
    }

    /// What may be done with a volume of `mode` here: `None` for a
    /// mode the provider keeps no volumes in — its object every
    /// member `false`.
    pub fn volume(&self, mode: Mode) -> Option<&VolumeCapabilities> {
        let object = match mode {
            Mode::Persistent => &self.volumes_persistent,
            Mode::Ephemeral => &self.volumes_ephemeral,
            Mode::ReadOnly => &self.volumes_read_only,
        };
        object.kept().then_some(object)
    }
}

images_store#

The server keeps a client image store: images the client builds on it by images::build, under names of the client’s, held for the client and for no other identity, as the pages of those endpoints define.

  • Held out. The server serves images::list, images::stat, images::build, images::delete and images::oci as their pages state, and the client source of images::check is a source the server has.
  • Not held out. The server has no client image, and the client source of images::check is not a source the server has. The server answers:
    • an images::list request with exactly one response, the byte 4 and nothing else, and the response finish; it sends no image, no byte 2 and no byte 3;
    • an images::stat request with exactly one response, the byte 1 and nothing else, and the response finish;
    • an images::build request with exactly one response, the byte 4 and nothing else, and the response finish; it sends no line of output before it, and builds nothing;
    • an images::delete request with exactly one response, the byte 2 and nothing else, and the response finish;
    • an images::oci request with exactly one response on the main stream, the byte 0 and nothing else, and the response finish; it answers no channel the client opens on the scope;
    • an images::check request that asks for the client source and not for the registries source, or that asks for the registries source where images_registries is empty, with exactly one response, the byte 2 and nothing else, and the response finish; a request that also asks for the registries source where images_registries is not empty is answered from that source alone.
  • The images_disk member of a capacity response. The server sends the images_disk member of every capacity response whether or not it holds out images_store. Where it does not, the member carries any value, and a client does not read it.

images_registries#

The registries the server pulls images from: an array of strings, each the host of one registry as a reference’s registry member names it — docker.io, ghcr.io, registry.example.com:5000 — and every registry the server pulls from is in the array.

  • The registries source. An images::check request that asks for the registries source is answered by consulting exactly the registries in the array that a reference of the request names, as <registry>/<name>@<digest>. A reference naming a registry not in the array is ignored. The source is one the server has when the array is not empty.
  • A run’s image. A server obtains the image of a containers::agents::run or a containers::tools::run from a registry only where the registry is in the array and a reference of the request names it; it obtains an image from no registry the array does not list.
  • Empty. An empty array, or an absent member, states that the server pulls from no registry. The registries source is not a source the server has: an images::check request that asks for it and for no source the server has is answered with exactly one response, the byte 2 and nothing else, and the response finish; one that also asks for the client source where images_store is true is answered from that source alone. A run’s image is obtained from what the server holds or from the client, and from no registry.

What every server does#

Every server makes every FUSE mount a run request names, and every mount a client asks for on the fuse-mount channel of a run, and serves every containers::fuse_serve request, as those pages state: no capability governs them, and the capabilities object has no member for them.

The volume modes#

Each of volumes_persistent, volumes_ephemeral and volumes_read_only is the mode object for volumes whose mode is, respectively, persistent, ephemeral and read_only, as volumes::create defines the three modes: what the server does with a volume of the mode. A mode object with at least one member true is a mode the server holds out, and keeps volumes in; a mode object with every member false — or absent, which is the same statement — is a mode the server does not hold out.

  • Held out. The server keeps volumes of the mode. A volumes::list listing of the server may contain a volume of the mode, a volumes::create request may state the mode, and a volumes::edit request may change a volume into the mode. What a running container, a FUSE serve and an operation may do with a volume of the mode is the mode object.
  • Not held out. The server keeps no volume of the mode. No listing of the server contains a volume of the mode. The server answers a volumes::create request whose mode is the mode with exactly one response, the byte 2 and nothing else, and the response finish, and creates nothing. The server answers a volumes::edit request whose change states the mode — as Mode, or as Both — and names a volume in the client’s listing with exactly one response, the byte 3 and nothing else, and the response finish, and changes nothing; a request naming a volume not in the listing is answered with the error the edit page states, whatever the change.
  • None held out. A server that holds out none of the three keeps no volume at all, and the eleven volume endpoints are not served. The server answers every request of each with exactly one response, the byte tabulated and nothing else, and the response finish; it sends nothing else on the scope, serves no channel, and neither lists, examines, reads, writes, walks, serves, measures, creates, edits nor deletes anything for the request. Such a server has no volume a run can name: a run request whose volume_mounts has an entry is answered with the error the run pages state for a volume not in the client’s listing, and an images::build request naming a context is answered with the error its page states for a volume not in the client’s listing.
EndpointThe byte, alone
volumes::list4
volumes::stat4
volumes::read4
volumes::write4
volumes::filetree4
volumes::fuse_serve5
volumes::create_capacity1
volumes::create3
volumes::edit_capacity1
volumes::edit7
volumes::delete4
  • The volumes_disk member of a capacity response. The server sends the volumes_disk member of every capacity response whether or not it holds out a mode object. Where it holds out none, the member carries any value, and a client does not read it.

The mode object#

The value of each of volumes_persistent, volumes_ephemeral and volumes_read_only is one JSON object of five boolean members, each optional, an absent member being false. The object with every member false is the mode not held out, and is what an absent mode member means; every rule below that speaks of a mode object absent speaks of that object too.

Membertrue states that
mounta running container of the client may mount a volume of the mode
fuse_servea volumes::fuse_serve scope of the client may hold a volume of the mode
mount_manymore than one running container may mount a volume of the mode at once
fuse_serve_manymore than one FUSE serve may hold a volume of the mode at once
mount_and_fuse_servea running container and a FUSE serve may hold a volume of the mode at once
  • The mode allows at most. A mode allows at most what volumes::create states for it: a persistent volume may be mounted in any number of running containers at once, is held by at most one FUSE serve at a time, and is never held by a FUSE serve and a running container at once; an ephemeral or a read_only volume may be held by any number of running containers and FUSE serves at once. A server holds out no more than the mode allows: the volumes_persistent object has fuse_serve_many and mount_and_fuse_serve false, absent or present.
  • Consistency. A server that sends mount_many true sends mount true. A server that sends fuse_serve_many true sends fuse_serve true. A server that sends mount_and_fuse_serve true sends mount true and fuse_serve true.
  • One running container is one holder. A running container that mounts a volume at several container_paths of one request is one running container holding the volume. An images::build that names a directory of the volume as its context holds the volume as one running container does, for the life of the build.

The hold#

The server holds a volume for a running container from the moment it accepts the run request that names the volume until the run ends; for a FUSE serve from the moment it sends Serving until the response finish; for a build naming the volume as its context from the moment it accepts the build request until the response finish; and to itself for a volumes::stat, volumes::read, volumes::write, volumes::filetree, volumes::edit or volumes::delete, an operation, from the moment it accepts the request until the response finish, a delete never giving the hold back. Whether a request that would hold a volume is served is decided by the volume’s mode, by the mode object the server holds out for the mode, and by who holds the volume at the time of the request, in the order stated below, the first rule that applies deciding; the decision and the hold it takes are one act, between which no other hold of the volume is taken or given back and no edit of the volume lands.

  • A run’s volume mount. For each distinct volume_name a run request names, in the order first named: (1) a volume not in the client’s listing is the run’s error; (2) a volume whose mode is not the mode of every mount naming it is VolumeMode, carrying the name and the mode the volume is in; (3) a volume of a mode whose object is absent, or whose mount is false, is VolumeMountUnsupported, carrying the name; (4) a volume an operation holds is VolumeHeld, carrying the name; (5) a volume a running container holds, where mount_many is false, is VolumeMounted, carrying the name; (6) a volume a FUSE serve holds, where mount_and_fuse_serve is false, is VolumeFuseServed, carrying the name; (7) otherwise the server holds the volume for the run. A refusal of any volume gives back every volume held for the request before it, and the server fetches nothing and deploys nothing for the request. The run pages state each response’s form.
  • A FUSE serve. For the volume a volumes::fuse_serve request names: (1) a volume not in the client’s listing is the error; (2) a volume whose mode is not the request’s mode is VolumeMode, carrying the name and the mode the volume is in; (3) a volume of a mode whose object is absent, or whose fuse_serve is false, is Unsupported; (4) a volume an operation holds is Held; (5) a volume another FUSE serve holds, where fuse_serve_many is false, is FuseServed; (6) a volume a running container holds, where mount_and_fuse_serve is false, is Mounted; (7) otherwise the server holds the volume for the FUSE serve. That page states each response’s form.
  • An operation. For the volume an operation names: (1) a volume not in the client’s listing is the error; (2) a volume another operation holds is Held; (3) a volume a running container holds is Mounted; (4) a volume a FUSE serve holds is FuseServed; (5) otherwise the server holds the volume to itself for the operation. Each operation’s page states each response’s form.
  • A build’s context. For the volume an images::build request names as its context: (1) a volume not in the client’s listing is the error; (2) a volume an operation holds, a volume a running container holds where the mode object is absent or its mount_many is false, and a volume a FUSE serve holds where the mode object is absent or its mount_and_fuse_serve is false, are each ContextHeld; (3) otherwise the server holds the volume for the build. That page states each response’s form.
  • Who holds, as of the request. A running container holds a volume from the moment the server holds it for the run until the run ends, whether or not the id has been sent. A FUSE serve holds a volume from Serving until the response finish. An operation holds a volume from the moment the server accepts the request until its response finish.

The mode object is defined by diverge-sdk-rs/src/provider/endpoints/info/server/response/volume_capabilities.rs:

//! What may be done with a volume of one mode, on one provider.

use serde::{Deserialize, Serialize};

/// What a provider does with a volume of one
/// [`Mode`](crate::provider::endpoints::volumes::Mode): whether a run
/// may mount it, whether a FUSE serve may hold it, and how many of
/// each, and both, at once.
///
/// The mode itself says the MOST that may be done with a volume in
/// it — any number of running containers but one FUSE serve, never
/// both, for a persistent volume; any number of each, beside one
/// another, for an ephemeral or a read-only one — and this says how
/// much of that the provider does. Each member is a boolean, absent being `false`;
/// the object of every member `false` — the member absent, the same
/// statement — is a mode the provider keeps no volume in, see
/// [`kept`](Self::kept); and
/// the later ones extend the earlier: `mount_many` means nothing
/// without `mount`, `fuse_serve_many` nothing without `fuse_serve`,
/// `mount_and_fuse_serve` nothing without both; a provider states
/// them consistently, and a reader reads each as the plain fact it
/// is. A request past one is answered by the endpoint's own variant,
/// named below.
///
/// # What a mode allows, at most
///
/// | mode | `mount_many` | `fuse_serve_many` | `mount_and_fuse_serve` |
/// |------|--------------|-------------------|------------------------|
/// | persistent | may | never | never |
/// | ephemeral | may | may | may |
/// | read only | may | may | may |
///
/// A provider holds out no more than the mode allows: a persistent
/// volume's object has `fuse_serve_many` and `mount_and_fuse_serve`
/// `false`.
#[derive(
    Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize, schemars::JsonSchema,
)]
pub struct VolumeCapabilities {
    /// A run may mount a volume of the mode — a
    /// [`VolumeMount`](crate::shared::containers::request::VolumeMount)
    /// naming one is honored. Absent — `false` — a run naming one is
    /// answered
    /// [`VolumeMountUnsupported`](crate::shared::containers::response::VolumeMountUnsupported).
    #[serde(default)]
    pub mount: bool,
    /// A [`fuse_serve`](crate::provider::endpoints::volumes::fuse_serve)
    /// may hold a volume of the mode. Absent — `false` — one asking
    /// is answered `Unsupported`.
    #[serde(default)]
    pub fuse_serve: bool,
    /// More than one running container may mount a volume of the
    /// mode at once. Absent — `false` — a run naming one that a
    /// running container has is answered
    /// [`VolumeMounted`](crate::shared::containers::response::VolumeMounted).
    #[serde(default)]
    pub mount_many: bool,
    /// More than one FUSE serve may hold a volume of the mode at
    /// once. Absent — `false` — a FUSE serve of one that another FUSE
    /// serve holds is answered `FuseServed`.
    #[serde(default)]
    pub fuse_serve_many: bool,
    /// A running container and a FUSE serve may hold a volume of the
    /// mode at once. Absent — `false` — a run naming one that a FUSE
    /// serve holds is answered
    /// [`VolumeFuseServed`](crate::shared::containers::response::VolumeFuseServed),
    /// and a FUSE serve of one that a running container has is
    /// answered `Mounted`.
    #[serde(default)]
    pub mount_and_fuse_serve: bool,
}

impl VolumeCapabilities {
    /// Whether the provider keeps volumes of the mode at all: any
    /// member `true`. Every member `false` is the mode not kept, and
    /// is what an absent member means.
    pub fn kept(&self) -> bool {
        self.mount || self.fuse_serve || self.mount_many || self.fuse_serve_many || self.mount_and_fuse_serve
    }
}