io7m | single-page | multi-page | epub | Cardant User Manual 0.0.1

Cardant User Manual 0.0.1

DATE 2023-12-16T17:05:50+00:00
DESCRIPTION User manual for the cardant server.
IDENTIFIER 9e6e25d5-7759-49a9-97b4-d0c645799e42
LANGUAGE en
SOURCE https://www.io7m.com/software/cardant/
TITLE Cardant User Manual 0.0.1
The cardant package provides a server for managing inventories.

1.2. Features

  • Tracks user-defined items within user-defined hierarchical storage locations.
  • Strongly-typed metadata with user-defined types.
  • Rich search functionality; perform complex searches over item metadata with automatic pagination and sorting.
  • Full API access for all operations.
  • Full Java API for performing operations.
  • Fine-grained capability based security model for operations; Safely write external services that can perform operations while maintaining the principle of least privilege.
  • Command-line shell.
  • Complete audit log; every operation that changes the state of the system is logged in an append-only log.
  • Fully instrumented with OpenTelemetry.
  • A small, easily auditable codebase with a heavy use of modularity for correctness.
  • An extensive automated test suite with high coverage.
  • Platform independence. No platform-dependent code is included in any form, and installations can largely be carried between platforms without changes.
  • OSGi-ready
  • JPMS-ready
  • Support for Canonmill keystores.
  • ISC license.
The cardant server package is available from the following sources:
Regardless of the distribution method, the cardant package will contain a command named cardant that acts as the main entrypoint to all of the server and client functionality.
The cardant server requires a PostgreSQL server. The cardant server will create the required tables and database objects on first startup, given the name of a running PostgreSQL database, and a PostgreSQL role and password.
The cardant server delegates all user and password management to idstore and therefore requires a running idstore server in order to work.
A distribution package can be found at Maven Central.
The cardant command requires that a Java 21+ compatible JVM be accessible via /usr/bin/env java.
Verify the integrity of the distribution zip file:

2.4.4. Verify

$ gpg --verify com.io7m.cardant.main-0.0.1-distribution.zip.asc
gpg: assuming signed data in 'com.io7m.cardant.main-0.0.1-distribution.zip.asc'
gpg: Signature made Tue 28 Jun 2022 15:01:56 GMT
gpg:                using RSA key 3CCE59428B30462D10459909C5607DA146E128B8
gpg:                issuer "contact@io7m.com"
gpg: using pgp trust model
gpg: Good signature from "io7m.com (2022 maven-rsa-key) <contact@io7m.com>" [unknown]
Unzip the zip file, and set up the environment appropriately. The cardant command expects an environment variable named CARDANT_HOME to be defined that points to the installation directory.

2.4.6. Extract

$ unzip com.io7m.cardant.main-0.0.1-distribution.zip
$ export CARDANT_HOME=$(realpath cardant)
$ ./cardant/bin/cardant
cardant: usage: cardant [command] [arguments ...]
...
OCI images are available from Quay.io for use with podman or docker.

2.5.1.2. Podman/Docker

$ podman pull quay.io/io7mcom/cardant:0.0.1
$ podman run quay.io/io7mcom/cardant:0.0.1
cardant: usage: cardant [command] [arguments ...]
...
The OCI image includes a cardant-healthcheck command that makes a request to a health endpoint on the Inventory API server. The command expects the server to be accessible on localhost inside the container, and requires that the CARDANT_HEALTHCHECK_PORT environment variable be set to the port used by the Admin API. By default, the container sets CARDANT_HEALTHCHECK_PORT to 30000 and therefore there is no need to set this variable manually if the server is configured with the default settings.
This feature may only be available when running the image under docker due to limitations in the OCI image specification. The functionality of the health check service can be used directly via the Inventory API.

2.5.2.3. Health Example

$ curl http://localhost:30000/health
OK
Given an appropriate configuration file in server.conf, it's necessary to tell cardant to configure the database and create an initial administrator. It's necessary to specify the idstore user ID of a user that will be considered to be the administrator of the cardant server:

2.6.1.2. Initialize

$ cardant initialize \
  --admin-id '92f83bce-3973-4db8-8aaf-d401443a9772' \
  --admin-name 'someone' \
  --configuration server.conf
It is not critical that the username given match the user ID on the idstore server; the name will be updated automatically from that server when a user logs in.
The server can now be run with cardant server:

2.6.2.2. Run

$ cardant server --configuration server.conf
info: [localhost/<unresolved>:30000] Inventory API server started
The server does not fork into the background and is designed to be run under process supervision.
Run cardant shell [1].

2.7.2. Shell

$ cardant shell
[cardant]# version
com.io7m.cardant 0.0.1-SNAPSHOT 20af71248a7784b0e5247eab4b1ebd28de284739
The cardant package uses PostgreSQL for all persistent data.
The cardant package sets up multiple roles during database initialization. The configured roles have different degrees of privileges in order to allow, for example, external systems such as database metrics collectors read-only access to the database. All the defined rules are declared with the built-in PostgreSQL restrictions such as nocreatedb, nocreaterole, etc.
During the startup of the cardant server, the server will connect to the database using the owner role and do any database table initialization and/or schema upgrades necessary. The server will then disconnect from the database, and then connect to the database again using the worker role. The worker role is then used for normal operation of the server; if this role is somehow compromised, the role only has a limited ability to do any damage to the database, and cannot affect the audit log at all.
The owner role is the role that owns the database and is permitted to create tables, create new roles, etc. This role is used by the cardant package when creating the database during the first run of the server, and for upgrading database schemas later. Administrators are free to pick the name of the role, although it is recommended that the role be named cardant_install to make it clear as to the purpose of the role.
If the PostgreSQL OCI image is used, it is common to have the image create this role automatically using the POSTGRES_USER and POSTGRES_PASSWORD variables:

2.8.2.2.3. Example

$ podman run \
  --name some-postgres \
  -e POSTGRES_USER=cardant_install \
  -e POSTGRES_PASSWORD=mysecretpassword \
  -d postgres
The worker role is the role that is used for normal database operation. It is a role that has read/write access to all tables (except for the audit log which is restricted to being append-only), although it is not granted the ability to create new tables, drop tables, or do other schema manipulation. The role is always named cardant, and adminstrators are required to set a password for this role.
The reader role is a role that is permitted read-only access to some of the database. It is effectively an optional role that can be used by various database metrics systems if required. If a password is not specified for the role in the server's configuration file, then logging in is not permitted at all.

Footnotes

1
If running under podman or docker, remember to use the -i and -t options.
References to this footnote: 1
The cardant server is configured using a single XML-formatted configuration file. The format has a fully documented schema and so configuration files can be independently validated, and benefit from autocompletion in most modern IDEs.
The configuration file must consist of a single top-level Configuration element in the com.io7m.cardant:configuration:1 namespace. In modern IDEs, simply creating a file containing this element will immediately fill in all the other required child elements.
The smallest working configuration file, assuming a database at db.example.com and an idstore server at idstore.example.com:

3.2.2. Example

<?xml version="1.0" encoding="UTF-8" ?>

<Configuration xmlns="com.io7m.cardant:configuration:1"
               xmlns:ct="com.io7m.cardant:tls:1">

  <InventoryService ListenAddress="[::]"
                    ListenPort="30000"
                    SessionExpiration="PT30M"
                    ExternalAddress="http://cardant.example.com:30000">
    <ct:TLSDisabled/>
  </InventoryService>

  <Database Kind="POSTGRESQL"
            OwnerRoleName="cardant_install"
            OwnerRolePassword="892a2b68-2ddf-478a-a8ab-37172f6ac2fe"
            WorkerRolePassword="e61135dc-1d3f-4ab2-85ef-95ef49d66285"
            ReaderRolePassword="c2026069-97e7-45b1-85c4-a2349bbb847b"
            Address="db.example.com"
            Port="5432"
            Name="cardant"
            Create="true"
            Upgrade="true"
            Language="english"/>

  <Idstore BaseURI="https://idstore.example.com:50000"
           PasswordResetURI="https://idstore.example.com:50001/reset"/>

  <Limits MaximumFileUploadSizeOctets="10000000"
          MaximumCommandSizeOctets="10000000"/>

  <Maintenance TLSReloadInterval="PT24H"/>

  <OpenTelemetry LogicalServiceName="cardant01">
    <Logs Endpoint="http://logs.example.com:4317"
          Protocol="GRPC"/>
    <Metrics Endpoint="http://metrics.example.com:4317"
             Protocol="GRPC"/>
    <Traces Endpoint="http://traces.example.com:4317"
            Protocol="GRPC"/>
  </OpenTelemetry>

</Configuration>
The InventoryService section of the configuration file configures the main inventory service.
The ListenAddress and ListenPort attributes specify the address and port to which to the HTTP service will bind.
The ExternalAddress attribute specifies the external address that clients will use to connect to this server.
By convention, the inventory service should listen on TCP port 30000.
The InventoryService element must contain either a TLSEnabled or TLSDisabled element specifying whether TLS should be enabled or disabled, respectively. The TLSEnabled element describes the key store and trust store. The cardant server automatically reloads certificates periodically in order to work well in environments using the ACME protocol to issue certificates.
An example service configuration:

3.3.2.2. Example

<?xml version="1.0" encoding="UTF-8" ?>

<InventoryService xmlns="com.io7m.cardant:configuration:1"
                  xmlns:tls="com.io7m.cardant:tls:1"
                  ListenAddress="[::]"
                  ListenPort="30000"
                  SessionExpiration="PT30M"
                  ExternalAddress="http://cardant.example.com:30000">
  <tls:TLSEnabled>
    <tls:KeyStore Type="CANONMILL"
                  Provider="CANONMILL"
                  Password="ignored"
                  File="/cardant/keystore.xml"/>
    <tls:TrustStore Type="JKS"
                    Provider="SUN"
                    Password="changeit"
                    File="/usr/lib/jvm/java-21-openjdk/lib/security/cacerts"/>
  </tls:TLSEnabled>
</InventoryService>
The Idstore section of the configuration file configures the external idstore server.
The BaseURI attribute specifies the base URI of the User API of the external idstore server. This server is used to authenticate users whenever they try to log in to the cardant server.
The PasswordResetURI attribute specifies the URI to which users should be redirected if they want to reset their passwords. This is the URI of the User View service on the external idstore server.
An example service configuration:

3.4.3.2. Example

<?xml version="1.0" encoding="UTF-8" ?>

<Idstore xmlns="com.io7m.cardant:configuration:1"
         BaseURI="https://idstore.example.com:50000"
         PasswordResetURI="https://idstore.example.com:50001/reset"/>
The Maintenance section of the configuration file configures the maintenance service.
The TLSReloadInterval attribute specifies how frequently the server will reload the TLS certificates for the HTTP services; the server will reload certificates every time this interval elapses. The attribute must be formatted as an ISO 8601 duration string.
An example maintenance configuration:

3.5.3.2. Example

<Maintenance TLSReloadInterval="PT30M"/>
The Limits section of the configuration file configures various limits for the server.
The MaximumFileUploadSizeOctets attribute specifies the maximum size allowed for files uploaded the server. Requests to upload files larger than this limit will be rejected.
The MaximumCommandSizeOctets attribute specifies the maximum size allowed for commands sent to the server. Commands larger than this limit will be rejected.
An example configuration:

3.6.4.2. Example

<Limits MaximumFileUploadSizeOctets="10000000"
        MaximumCommandSizeOctets="10000000"/>
The Database section of the configuration file configures the database.
The OwnerRoleName attribute specifies the name of the role that owns the database. Conventionally, this should be cardant_install, but can be set independently by the database administrator.
The OwnerRolePassword attribute specifies the password of the owner role.
The WorkerRolePassword attribute specifies the password of the worker role used for normal database operation.
The ReaderRolePassword attribute specifies the password of the reader role used for read-only database access. If this attribute is not specified, logging in using this role will be prevented.
The Name attribute specifies the database name.
The Create attribute specifies that the database schema should be created on startup.
The Upgrade attribute specifies that the database schema should be upgraded on startup.
The Language attribute specifies the language that will be used internally for full-text searching.
An example database configuration:

3.7.2.2. Example

<Database Kind="POSTGRESQL"
          OwnerRoleName="cardant_install"
          OwnerRolePassword="mydatabase"
          WorkerRolePassword="willquickly"
          ReaderRolePassword="becompromised"
          Address="db.example.com"
          Port="5432"
          Name="cardant"
          Create="true"
          Upgrade="true"
          Language="english"/>
The OpenTelemetry section of the configuration file configures Open Telemetry. This section is optional and telemetry is disabled if the section is not present.
The logical service name should be provided in the LogicalServiceName attribute.
If the OpenTelemetry element contains a Traces element, OTLP traces will be sent to a specified endpoint. The Endpoint attribute specifies the endpoint, and the Protocol attribute can either be GRPC or HTTP.
If the OpenTelemetry element contains a Metrics element, OTLP metrics will be sent to a specified endpoint. The Endpoint attribute specifies the endpoint, and the Protocol attribute can either be GRPC or HTTP.
If the OpenTelemetry element contains a Logs element, OTLP logs will be sent to a specified endpoint. The Endpoint attribute specifies the endpoint, and the Protocol attribute can either be GRPC or HTTP.
An example Open Telemetry configuration:

3.8.5.2. Example

<OpenTelemetry LogicalServiceName="idstore">
  <Logs Endpoint="http://logs.example.com:4317"
        Protocol="GRPC"/>
  <Metrics Endpoint="http://metrics.example.com:4317"
           Protocol="GRPC"/>
  <Traces Endpoint="http://traces.example.com:4317"
          Protocol="GRPC"/>
</OpenTelemetry>
The XSD schema for the configuration file is as follows:

3.9.2. Configuration Schema

<?xml version="1.0" encoding="UTF-8" ?>

<!--
  Copyright © 2023 Mark Raynsford <code@io7m.com> https://www.io7m.com

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted, provided that the above
  copyright notice and this permission notice appear in all copies.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
  IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-->

<schema xmlns="http://www.w3.org/2001/XMLSchema"
        targetNamespace="com.io7m.cardant:configuration:1"
        xmlns:ct="com.io7m.cardant:tls:1"
        xmlns:c="com.io7m.cardant:configuration:1">

  <import namespace="com.io7m.cardant:tls:1"/>

  <annotation>
    <documentation>
      The schema for server configuration files.
    </documentation>
  </annotation>

  <simpleType name="DatabaseKind">
    <annotation>
      <documentation>
        The kind of the target database. Currently, only PostgreSQL is supported.
      </documentation>
    </annotation>

    <restriction base="string">
      <enumeration value="POSTGRESQL">
        <annotation>
          <documentation>
            The database is PostgreSQL.
          </documentation>
        </annotation>
      </enumeration>
    </restriction>
  </simpleType>

  <element name="Idstore">
    <annotation>
      <documentation>
        Configuration for the idstore server that will be used for user identity information.
      </documentation>
    </annotation>

    <complexType>
      <attribute name="BaseURI"
                 type="anyURI"
                 use="required">
        <annotation>
          <documentation>
            The base URI of the idstore user API.
          </documentation>
        </annotation>
      </attribute>

      <attribute name="PasswordResetURI"
                 type="anyURI"
                 use="required">
        <annotation>
          <documentation>
            The password reset URI to which to redirect users who want to reset their password.
          </documentation>
        </annotation>
      </attribute>
    </complexType>
  </element>

  <simpleType name="OpenTelemetryProtocol">
    <annotation>
      <documentation>
        The protocol used to deliver OpenTelemetry data.
      </documentation>
    </annotation>

    <restriction base="string">
      <enumeration value="GRPC">
        <annotation>
          <documentation>
            The data will be sent using gRPC.
          </documentation>
        </annotation>
      </enumeration>
      <enumeration value="HTTP">
        <annotation>
          <documentation>
            The data will be sent using HTTP(s).
          </documentation>
        </annotation>
      </enumeration>
    </restriction>
  </simpleType>

  <element name="Logs">
    <annotation>
      <documentation>
        Configuration information for OpenTelemetry logs.
      </documentation>
    </annotation>

    <complexType>
      <attribute name="Endpoint"
                 use="required"
                 type="anyURI">
        <annotation>
          <documentation>
            The endpoint to which OTLP log data will be sent.
          </documentation>
        </annotation>
      </attribute>

      <attribute name="Protocol"
                 use="required"
                 type="c:OpenTelemetryProtocol">
        <annotation>
          <documentation>
            The protocol used to send log data.
          </documentation>
        </annotation>
      </attribute>
    </complexType>
  </element>

  <element name="Metrics">
    <annotation>
      <documentation>
        Configuration information for OpenTelemetry metrics.
      </documentation>
    </annotation>

    <complexType>
      <attribute name="Endpoint"
                 use="required"
                 type="anyURI">
        <annotation>
          <documentation>
            The endpoint to which OTLP metrics data will be sent.
          </documentation>
        </annotation>
      </attribute>

      <attribute name="Protocol"
                 use="required"
                 type="c:OpenTelemetryProtocol">
        <annotation>
          <documentation>
            The protocol used to send metrics data.
          </documentation>
        </annotation>
      </attribute>
    </complexType>
  </element>

  <element name="Traces">
    <annotation>
      <documentation>
        Configuration information for OpenTelemetry traces.
      </documentation>
    </annotation>

    <complexType>
      <attribute name="Endpoint"
                 use="required"
                 type="anyURI">
        <annotation>
          <documentation>
            The endpoint to which OTLP trace data will be sent.
          </documentation>
        </annotation>
      </attribute>

      <attribute name="Protocol"
                 use="required"
                 type="c:OpenTelemetryProtocol">
        <annotation>
          <documentation>
            The protocol used to send trace data.
          </documentation>
        </annotation>
      </attribute>
    </complexType>
  </element>

  <element name="OpenTelemetry">
    <annotation>
      <documentation>
        Configuration information for OpenTelemetry.
      </documentation>
    </annotation>

    <complexType>
      <sequence>
        <element ref="c:Logs"
                 minOccurs="0"
                 maxOccurs="1"/>
        <element ref="c:Metrics"
                 minOccurs="0"
                 maxOccurs="1"/>
        <element ref="c:Traces"
                 minOccurs="0"
                 maxOccurs="1"/>
      </sequence>

      <attribute name="LogicalServiceName"
                 use="required"
                 type="string">
        <annotation>
          <documentation>
            The logical name of the service as it will appear in OpenTelemetry.
          </documentation>
        </annotation>
      </attribute>
    </complexType>
  </element>

  <element name="Database">
    <annotation>
      <documentation>
        Configuration information for the database.
      </documentation>
    </annotation>

    <complexType>
      <attribute name="Kind"
                 type="c:DatabaseKind"
                 use="required"/>

      <attribute name="OwnerRoleName"
                 use="required"
                 type="string">
        <annotation>
          <documentation>
            The name of the role that owns the database. This is used for the initial database setup, and for upgrades.
          </documentation>
        </annotation>
      </attribute>

      <attribute name="OwnerRolePassword"
                 use="required"
                 type="string">
        <annotation>
          <documentation>
            The password of the role that owns the database.
          </documentation>
        </annotation>
      </attribute>

      <attribute name="WorkerRolePassword"
                 use="required"
                 type="string">
        <annotation>
          <documentation>
            The password of the role used for normal database operation. This is an unprivileged role that does not have
            the ability to perform DDL or other database-changing operations.
          </documentation>
        </annotation>
      </attribute>

      <attribute name="ReaderRolePassword"
                 use="optional"
                 type="string">
        <annotation>
          <documentation>
            The password of the role used for read-only database operation. If this attribute is not specified, the
            read-only role is not allowed to log in.
          </documentation>
        </annotation>
      </attribute>

      <attribute name="Address"
                 type="string"
                 use="required">
        <annotation>
          <documentation>
            The address of the database.
          </documentation>
        </annotation>
      </attribute>

      <attribute name="Port"
                 type="integer"
                 use="required">
        <annotation>
          <documentation>
            The port used to connect to the database.
          </documentation>
        </annotation>
      </attribute>

      <attribute name="Name"
                 type="string"
                 use="required">
        <annotation>
          <documentation>
            The name of the database.
          </documentation>
        </annotation>
      </attribute>

      <attribute name="Create"
                 type="boolean"
                 use="required">
        <annotation>
          <documentation>
            If set to true, the database and tables will be created if they do not already exist.
          </documentation>
        </annotation>
      </attribute>

      <attribute name="Upgrade"
                 type="boolean"
                 use="required">
        <annotation>
          <documentation>
            If set to true, the database and tables will be upgraded to the latest supported schema version.
          </documentation>
        </annotation>
      </attribute>

      <attribute name="Language"
                 type="string"
                 use="required">
        <annotation>
          <documentation>
            The language used for database search indexes (such as 'english'). See
            https://www.postgresql.org/docs/current/locale.html.
          </documentation>
        </annotation>
      </attribute>
    </complexType>
  </element>

  <complexType name="HTTPService">
    <sequence minOccurs="1"
              maxOccurs="1">
      <group ref="ct:TLSGroup"/>
    </sequence>

    <attribute name="ListenAddress"
               type="string"
               use="required">
      <annotation>
        <documentation>
          The address upon which this HTTP service will listen.
        </documentation>
      </annotation>
    </attribute>

    <attribute name="ListenPort"
               type="integer"
               use="required">
      <annotation>
        <documentation>
          The port upon which this HTTP service will listen.
        </documentation>
      </annotation>
    </attribute>

    <attribute name="ExternalAddress"
               type="anyURI"
               use="required">
      <annotation>
        <documentation>
          The address by which this service is accessible to the outside world. The service will typically be configured
          behind a reverse proxy to provide TLS.
        </documentation>
      </annotation>
    </attribute>

    <attribute name="SessionExpiration"
               type="duration"
               use="optional">
      <annotation>
        <documentation>
          The expiration time for sessions.
        </documentation>
      </annotation>
    </attribute>
  </complexType>

  <element name="InventoryService"
           type="c:HTTPService">
    <annotation>
      <documentation>
        Configuration for the Inventory API service.
      </documentation>
    </annotation>
  </element>

  <element name="Limits">
    <annotation>
      <documentation>
        Configuration for various limits.
      </documentation>
    </annotation>

    <complexType>
      <attribute name="MaximumFileUploadSizeOctets"
                 type="unsignedLong"
                 use="required">
        <annotation>
          <documentation>
            The maximum permitted size of uploaded files (in octets).
          </documentation>
        </annotation>
      </attribute>

      <attribute name="MaximumCommandSizeOctets"
                 type="unsignedLong"
                 use="required">
        <annotation>
          <documentation>
            The maximum permitted size of ordinary commands (in octets).
          </documentation>
        </annotation>
      </attribute>
    </complexType>
  </element>

  <element name="Maintenance">
    <annotation>
      <documentation>
        Configuration for the server's periodic maintenance tasks.
      </documentation>
    </annotation>

    <complexType>
      <attribute name="TLSReloadInterval"
                     type="duration"
                     use="optional">
        <annotation>
          <documentation>
            The interval at which TLS contexts will be reloaded. If not
            specified, TLS contexts will not be reloaded.
          </documentation>
        </annotation>
      </attribute>
    </complexType>
  </element>

  <element name="Configuration">
    <annotation>
      <documentation>
        The top-level configuration element.
      </documentation>
    </annotation>

    <complexType>
      <sequence>
        <element ref="c:InventoryService"/>
        <element ref="c:Database"/>
        <element ref="c:Idstore"/>
        <element ref="c:Limits"/>
        <element ref="c:Maintenance"/>
        <element ref="c:OpenTelemetry"
                 minOccurs="0"
                 maxOccurs="1"/>
      </sequence>
    </complexType>
  </element>

</schema>

3.9.3. TLS Schema

<?xml version="1.0" encoding="UTF-8" ?>

<!--
  Copyright © 2023 Mark Raynsford <code@io7m.com> https://www.io7m.com

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted, provided that the above
  copyright notice and this permission notice appear in all copies.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
  IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-->

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="com.io7m.cardant:tls:1"
            xmlns:nt="com.io7m.cardant:tls:1">

  <xsd:complexType name="StoreType"
                   abstract="true">
    <xsd:attribute name="Type"
                   type="xsd:string"
                   use="required"/>
    <xsd:attribute name="Provider"
                   type="xsd:string"
                   use="required"/>
    <xsd:attribute name="Password"
                   type="xsd:string"
                   use="required"/>
    <xsd:attribute name="File"
                   type="xsd:string"
                   use="required"/>
  </xsd:complexType>

  <xsd:complexType name="KeyStoreType">
    <xsd:complexContent>
      <xsd:extension base="nt:StoreType"/>
    </xsd:complexContent>
  </xsd:complexType>

  <xsd:complexType name="TrustStoreType">
    <xsd:complexContent>
      <xsd:extension base="nt:StoreType"/>
    </xsd:complexContent>
  </xsd:complexType>

  <xsd:element name="KeyStore"
               type="nt:KeyStoreType"/>

  <xsd:element name="TrustStore"
               type="nt:TrustStoreType"/>

  <xsd:complexType name="TLSType"
                   abstract="true"/>

  <xsd:complexType name="TLSDisabledType">
    <xsd:complexContent>
      <xsd:extension base="nt:TLSType"/>
    </xsd:complexContent>
  </xsd:complexType>

  <xsd:element name="TLSDisabled"
               type="nt:TLSDisabledType"/>

  <xsd:complexType name="TLSEnabledType">
    <xsd:complexContent>
      <xsd:extension base="nt:TLSType">
        <xsd:sequence>
          <xsd:element ref="nt:KeyStore"/>
          <xsd:element ref="nt:TrustStore"/>
        </xsd:sequence>
      </xsd:extension>
    </xsd:complexContent>
  </xsd:complexType>

  <xsd:element name="TLSEnabled"
               type="nt:TLSEnabledType"/>

  <xsd:group name="TLSGroup">
    <xsd:choice>
      <xsd:element ref="nt:TLSDisabled"/>
      <xsd:element ref="nt:TLSEnabled"/>
    </xsd:choice>
  </xsd:group>

</xsd:schema>
The cardant package provides a server-based application to track inventory. It stores detailed information about types of items, and can store the counts of those items present in defined locations.
This section of the documentation describes the internal cardant model.
An item is an object that is tracked by the inventory system. When the cardant package refers to items, it should more accurately be understood to be referring to classes of items. That is, the inventory system tracks sets of items of a given class within locations. An item has associated metadata, an identifier that uniquely identifies the item class, zero or more types that can constrain the metadata associated with the item, and a name.
Metadata is data associated with an item that describes that item. Items can have any number of metadata values, and inventory managers can introduce strong requirements on the presence and types of metadata on items through the application of types.
A type is a label that can be applied to an item that will constrain the metadata associated with that item. A type on an item refers to a type declaration created by the inventory manager. A type declaration is essentially a record type in the sense that it defines a set of named metadata values along with their individual scalar types.
Types are intended to ensure the integrity and quality of metadata associated with items, and allow for more precise searching. For example, inventory managers might define a voltage_regulator type that is applied to electronics components within the inventory that are voltage regulators. The voltage_regulator type might be declared to require that metadata includes numeric values input_voltage and output_voltage that describe the input and output voltages of the regular. Any attempt to update an item that has the voltage_regulator type applied without providing values for the input_voltage and output_voltage metadata will be rejected with a clear error message.
As mentioned, types are intended to facilitate more precise searching. It is possible to, for example, search for all items in the inventory that have an output_voltage metadata value, but this does not imply that all the returned items will be voltage regulators. With a well-designed and well-managed inventory, one can simply search for all items that have the type voltage_regulator (and then perhaps narrow down the search by asking for only those items that have an output_voltage value equal to 5.0).
A scalar type is a named type derived from one of the following base types:

4.2.4.2. Metadata Value Types

Name Description
Integral A type used to express integer values.
Real A type used to express real/fractional values.
Monetary A type used to express monetary values. All values include an associated currency unit.
Time A type used to express timestamp values. All values include a time zone value.
Text A type used to express plain string values.
Typically, a named scalar type will augment an existing scalar base type with further constraints such as a bound on the range of allowed values. For example, an inventory manager supervising a stock of computer keyboards might define a keyboard type with a keys field of a scalar type keyboard.keys. The keyboard.keys scalar type might be derived from the Integral scalar type with a bound of [68, 104], expressing that keyboard may have as few as 68 or as many as 104 keys.
Items can have zero or more attachments. An attachment is simply a file associated with the item according to a given relation. For example, an electronics component might have a PDF datasheet associated with it using a datasheet relation. Relations are entirely user-defined, and it is the responsibility of the inventory manager to use sensible and consistent relations.
A location is an object that tracks item counts. Locations, as a concept, are kept deliberately abstract to allow for a wide range of use-cases. A location might represent a physical storage bin in a warehouse, or it might represent a deployed computer system that has been built using items taken from the inventory. By treating locations as generic containers in this manner, the system is intended to allow for supporting different use cases such as managing retail inventory, or managing the deployment of computer parts in a laboratory. Locations are hierarchical.
Locations carry metadata in the same manner as items.
Locations can have applied types in the same manner as items.
Locations can have attachments in the same manner as items.
Locations form a hierarchy (specifically, a tree) in that any location may have any number of child locations, although a location may only have at most one parent. Locations can be reparented at any time; they are not locked into having whichever parent they had when they were created.
The hierarchical nature of locations can be used to model different kinds of inventory arrangements. For example, a manager of computer laboratories might define Laboratory A and Laboratory B locations. Within those locations, the manager might define locations Computer A1, Computer A2, Computer A3, and so on. Within the Computer A1 location, the manager might place a single motherboard item, one or more CPU items, and so on, from the inventory. This allows the manager to know what computer parts they have, and the computers in which those parts are being used in the laboratories under their supervision. Additionally, should one assembled computer be moved from one laboratory to another, the manager can simply reparent the computer location to the new laboratory.
Conversely, a manager of a retail business might define a Storage Room A location, and then within that location, define Shelf A01, Shelf A02, Shelf A03, etc. The manager can then place items onto those storage shelf locations. This allows the manager to track what stock they have, and exactly where they're keeping each kind of item.
Items are added to and removed from locations by repositing. A reposit operation atomically adds zero or more instances of an item to a location, and removes zero or more items from another location in a single step. Mathematically, a reposit operation can be modelled as a tuple (S,A,T,R,I), where S is the location to which A instances of item I will be added, and T is the location from which R instances of item I will be removed. The system automatically enforces integrity constraints such as not removing more item instances from a location than actually exist in that location.
For example, adding 10 newly purchased instances of an item I to location S can be described by the tuple (S,10,S,0,I). That is, 10 instances of I are added to S, and 0 instances of I are removed from S.
As another example, selling 10 instances of an item I that currently resides in location S can be described by the tuple (S,0,S,10,I). That is, 0 instances of I are added to S, and 10 instances of I are removed from S.
With the right arrangement of locations, reposit operations allow for a primitive form of double-entry bookkeeping.
The cardant package uses role-based access control for all operations.
Each user has a set of roles associated with it. When the user attempts to perform an operation on the server, the account's roles are checked to see if it has permission to perform the action.
A role R may be granted to a user A by user B if B has role R. Accordingly, a role R may be revoked from a user A by user B if B has role R.
A user holding the inventory.admin role effectively always has all available roles. If new roles are added in future versions of the cardant package, users holding the inventory.admin role will be automatically granted the new roles. It is recommended to limit this role to a single user, and to avoid using that user account for day-to-day operations.
The following roles are available:

4.4.3.2. Roles

Name Description
inventory.files.writer A writer of inventory files.
inventory.files.reader A reader of inventory files.
inventory.items.writer A writer of inventory items.
inventory.items.reader A reader of inventory items.
inventory.locations.writer A writer of inventory locations.
inventory.locations.reader A reader of inventory locations.
audit.reader A reader of the audit log.
inventory.admin An all-powerful administrator of inventories.
The server maintains an append-only audit log consisting of a series of audit events. An audit event has an integer id, an owner (represented by an account UUID), a timestamp, a type, and a message consisting of a set of key/value pairs.
Each operation that changes the underlying database typically results in an event being logged to the audit log.
The inventory API is the interface exposed to user clients. It exposes a Cedarbridge-based API over HTTP, using the included schema.
The inventory API is the primary means by which clients perform operations on the server.
The cardant package is extensively instrumented with OpenTelemetry in order to allow for the server to be continually monitored. The package publishes metrics, logs, and traces, all of which can be independently enabled or disabled. Most installations will only want to enable metrics or logs in production; traces are more useful when trying to diagnose performance problems, or for doing actual development on the cardant package.
The package publishes the following metrics that can be used for monitoring:

5.2.1.2. Metrics

Name Description
cardant_up A gauge that displays a constant 1 value while the server is up.
cardant_http_time A gauge that logs the time each HTTP request has taken in nanoseconds.
cardant_http_requests A counter that is incremented every time an HTTP request is handled.
cardant_http_requests_size A counter that is incremented with the size of every HTTP request.
cardant_http_responses_size A counter that is incremented with the size of every produced HTTP response.
cardant_http_responses_2xx A counter that is incremented with every HTTP response that produces a 2xx status code.
cardant_http_responses_4xx A counter that is incremented with every HTTP response that produces a 4xx status code. A 4xx status code should be understood to mean "blame the client".
cardant_http_responses_5xx A counter that is incremented with every HTTP response that produces a 5xx status code. A 5xx status code should be understood to mean "blame the server".
cardant_sessions A gauge that displays the number of currently active user sessions.
Most metrics are labelled with various attributes that allow for distinguishing between requests that occurred as part of the user service, or part of the admin service. Metrics involving the mail system typically include the destination address as an attribute.
The package may produce other metrics, however these are undocumented and should not be relied upon.
The cardant package provides a command-line interface for performing tasks such as starting the server, checking configuration files, and etc. The base cardant command is broken into a number of subcommands which are documented over the following sections.

6.1.2. Command-Line Overview

cardant: usage: cardant [command] [arguments ...]

  The cardant server command-line application.

  Use the "help" command to examine specific commands:

    $ cardant help help.

  Command-line arguments can be placed one per line into a file, and
  the file can be referenced using the @ symbol:

    $ echo help > file.txt
    $ echo help >> file.txt
    $ cardant @file.txt

  Commands:
    help          Show usage information for a command.
    initialize    Initialize the server and database.
    server        Start the server.
    shell         Start the shell.
    version       Show the application version.

  Documentation:
    https://www.io7m.com/software/cardant/
All subcommands accept a --verbose parameter that may be set to one of trace, debug, info, warn, or error. This parameter sets the lower bound for the severity of messages that will be logged. For example, at debug verbosity, only messages of severity debug and above will be logged. Setting the verbosity to trace level effectively causes everything to be logged, and will produce large volumes of debugging output.
The cardant command-line tool uses quarrel to parse command-line arguments, and therefore supports placing command-line arguments into a file, one argument per line, and then referencing that file with @. For example:

6.1.5. @ Syntax

$ cardant server --configuration server.conf

$ (cat <<EOF
--configuration
server.conf
EOF
) > args.txt

$ cardant @args.txt
All subcommands, unless otherwise specified, yield an exit code of 0 on success, and a non-zero exit code on failure.
initialize - Initialize the server
The initialize command initializes or updates the database and the initial administrator.

6.2.2.2. Parameters

Parameter Type Required Description
--configuration Path true The configuration file
--admin-id UUID true The ID of the initial administrator
--admin-username String true The initial administrator to create.
--admin-password String true The password of the initial administrator.
--admin-email String true The email address of the initial administrator.
--admin-realname String true The real name of the initial administrator.
--verbose CLPLogLevel false Set the minimum logging verbosity level.

6.2.3.1. Example

$ cardant initialize \
  --admin-id '92f83bce-3973-4db8-8aaf-d401443a9772' \
  --admin-name 'someone' \
  --configuration server.conf
server - Start the server
The server command starts the server.

6.3.2.2. Parameters

Parameter Type Required Description
--verbose CLPLogLevel false Set the minimum logging verbosity level.
--configuration Path true The configuration file

6.3.3.1. Example

$ cardant server --configuration server.conf
info: [localhost/<unresolved>:30000] Inventory API server started
shell - Start the shell
The server command starts the shell.

6.4.2.2. Parameters

Parameter Type Required Description
--verbose CLPLogLevel false Set the minimum logging verbosity level.

6.4.3.1. Example

$ cardant shell
[cardant]# version
com.io7m.cardant 0.0.1-SNAPSHOT 20af71248a7784b0e5247eab4b1ebd28de284739
version - Display the package version
The version command displays the current version of the package.

6.5.2.2. Parameters

Parameter Type Required Description
--verbose CLPLogLevel false Set the minimum logging verbosity level.

6.5.3.1. Example

cardant: usage: version

  Show the application version.

  The command does not accept any named arguments.

  The command does not accept any positional arguments.
  
  The version command produces the following output, in order:
  
    * The application ID (such as "com.io7m.quarrel")
    * The application version (such as "1.2.0")
    * The application build (such as "eacd59a2")
  
  For example, for a hypothetical application named "quarrel":
  
    $ quarrel version
    com.io7m.quarrel 1.2.0 eacd59a2
In addition to the API access the server provides, the cardant package includes an interactive command-line shell for performing tasks.
The shell is started using the shell command. The shell supports basic tab-completion and history accessed with the up and down arrows.
When running on an appropriate terminal, the shell supports tab completion for command and most command arguments. Begin typing the name of a command, or the name of a command argument, and then press tab. A set of completion options will be displayed.
When running on an appropriate terminal, the command shell stores the history of commands in memory (the history is not saved to disk, for security reasons). Press the up and down arrows to navigate to previously executed commands.
Arguments to shell commands may be quoted using the " or ' characters. This is required when calling commands that take arguments that may need to contain strings.
Typically, the ENTER key ends the current line. This will cause the shell to interpret the contents of a line as a command and execute it. Pressing ALT+ENTER inserts an actual newline character without executing the command. This can be useful if an argument to a command needs to contain a newline character:
When in this mode, the cursor can be moved around freely with the arrow keys to allow for editing lines. Pressing the ENTER key ends the multiline editing mode and executes the command.
All search commands in the cardant package provide automatic pagination. Searches are performed by beginning a search with a begin command which will yield the first page of results. Searches are then continued with next and previous commands which will return the next and previous pages of results, respectively. It is an error to try to execute a next or previous command without first having executed a begin command.
Each page of search results includes the number of the current page, and the number of pages of search results available. Attempting to seek beyond the end of the set of pages with a next command is not an error; the server will simply repeatedly return the last page in the set. Similarly, attempting to seek before the first page of results with a previous command will simply return the first page of results, repeatedly.
The shell provides different formatting options for tabular data.

7.1.7.2. Formatters

Name Description
PRETTY Provides pretty Unicode tables.
RAW Provides raw tables.
The formatter can be set using the set command.

7.1.7.4. Example

[cardant]$ set --formatter PRETTY
[cardant]$ audit-search-begin
Search results: Page 1 of 1
┌────┬──────────────────────────────────────┬────────────────┬─────────────────────────────┬─────────────────────────────────────────────────────────────────────────┐
│ ID │ Owner                                │ Type           │ Time                        │ Data                                                                    │
├────┼──────────────────────────────────────┼────────────────┼─────────────────────────────┼─────────────────────────────────────────────────────────────────────────┤
│ 1  │ c5574d46-7413-43c8-b6db-31001c382ca3 │ USER_LOGGED_IN │ 2023-12-21T11:30:11.171839Z │ {UserAgent=com.io7m.cardant.client/0.0.1-SNAPSHOT, Host=10.1.6.1:53236} │
└────┴──────────────────────────────────────┴────────────────┴─────────────────────────────┴─────────────────────────────────────────────────────────────────────────┘

[cardant]$ set --formatter RAW
[cardant]$ audit-search-begin
# Search results: Page 1 of 1
#--------------------------------
1 c5574d46-7413-43c8-b6db-31001c382ca3 USER_LOGGED_IN 2023-12-21T11:30:11.171839Z {UserAgent=com.io7m.cardant.client/0.0.1-SNAPSHOT, Host=10.1.6.1:53236}
The shell allows for setting server bookmarks. A server bookmark is essentially the information required to log in to a particular server as a particular user.
Bookmarks can be created with the bookmark-put command, listed with the bookmark-list command, and used with the bookmark-login command.

7.1.8.3. Example

[cardant]$ bookmark-put --user example --password F7B7D37C620C70BF03353148582AC97D --hostname 10.1.6.1 --name local0

[cardant]$ bookmark-list
┌─────────────────┬─────────────────────────┬───────┬───────┬──────────────────┐
│ Name            │ Host                    │ Port  │ TLS   │ User             │
├─────────────────┼─────────────────────────┼───────┼───────┼──────────────────┤
│ local0          │ 10.1.6.1                │ 30000 │ false │ example          │
└─────────────────┴─────────────────────────┴───────┴───────┴──────────────────┘
[cardant]$ bookmark-login --name local0

[cardant]$ self
User ID: c5574d46-7413-43c8-b6db-31001c382ca3
┌────────────────────────────────────────────────────────────────────────────────┐
│ Role                                                                           │
├────────────────────────────────────────────────────────────────────────────────┤
│ audit.reader                                                                   │
├────────────────────────────────────────────────────────────────────────────────┤
│ inventory.admin                                                                │
├────────────────────────────────────────────────────────────────────────────────┤
│ inventory.files.reader                                                         │
├────────────────────────────────────────────────────────────────────────────────┤
│ inventory.files.writer                                                         │
├────────────────────────────────────────────────────────────────────────────────┤
│ inventory.items.reader                                                         │
├────────────────────────────────────────────────────────────────────────────────┤
│ inventory.items.writer                                                         │
├────────────────────────────────────────────────────────────────────────────────┤
│ inventory.locations.reader                                                     │
├────────────────────────────────────────────────────────────────────────────────┤
│ inventory.locations.writer                                                     │
└────────────────────────────────────────────────────────────────────────────────┘
audit-search-begin - Begin searching for audit records.
The audit-search-begin command begins searching for audit records.

7.2.1.2.2. Parameters

Parameter Type Cardinality Default Description
--limit java.lang.Integer [1, 1] 10 The maximum number of results per page.
--time-from java.time.OffsetDateTime [1, 1] 1970-01-01T00:00Z Return audit events later than this date.
--time-to java.time.OffsetDateTime [1, 1] +101970-01-01T00:00Z Return audit events earlier than this date.
--type-equal-to java.lang.String [0, 1] Filter events by type.
--type-not-equal-to java.lang.String [0, 1] Filter events by type.
--user com.io7m.cardant.model.CAUserID [0, 1] Filter events by user.

7.2.1.3.1. Example

$ audit-search-begin
Search results: Page 1 of 1
┌────┬──────────────────────────────────────┬────────────────┬─────────────────────────────┬─────────────────────────────────────────────────────────────────────────┐
│ ID │ Owner                                │ Type           │ Time                        │ Data                                                                    │
├────┼──────────────────────────────────────┼────────────────┼─────────────────────────────┼─────────────────────────────────────────────────────────────────────────┤
│ 1  │ c5574d46-7413-43c8-b6db-31001c382ca3 │ USER_LOGGED_IN │ 2023-12-21T11:30:11.171839Z │ {UserAgent=com.io7m.cardant.client/0.0.1-SNAPSHOT, Host=10.1.6.1:53236} │
└────┴──────────────────────────────────────┴────────────────┴─────────────────────────────┴─────────────────────────────────────────────────────────────────────────┘
audit-search-next - Go to the next page of audit events.
The audit-search-next command goes to the next page of audit events.
The command can only be used after the associated begin command.
audit-search-previous - Go to the previous page of audit events.
The audit-search-previous command goes to the previous page of audit events.
The command can only be used after the associated begin command.
bookmark-list - List server bookmarks.
The bookmark-list command lists the local server bookmarks.
bookmark-login - Log in using a bookmark.
The bookmark-login command logs into a server using a bookmark.

7.2.5.2.2. Parameters

Parameter Type Cardinality Default Description
--name java.lang.String [0, 1] The name of the bookmark.
bookmark-put - Create or update a server bookmark.
The bookmark-put command creates or updates a bookmark.

7.2.6.2.2. Parameters

Parameter Type Cardinality Default Description
--hostname java.lang.String [1, 1] The hostname of the server.
--name java.lang.String [1, 1] The name of the bookmark.
--password java.lang.String [1, 1] The password for the server.
--port java.lang.Integer [1, 1] 30000 The port used for the server.
--tls java.lang.Boolean [1, 1] false Whether to use TLS to connect to the server.
--user java.lang.String [1, 1] The username for the server.
bookmark-remove - Remove a server bookmark.
The bookmark-remove command removes a bookmark.

7.2.7.2.2. Parameters

Parameter Type Cardinality Default Description
--name java.lang.String [1, 1] The name of the bookmark.
file-get - Download a file.
The file-get command downloads a file from the server.

7.2.8.2.2. Parameters

Parameter Type Cardinality Default Description
--download-to java.nio.file.Path [0, 1] If specified, the file will be downloaded to this path.
--id com.io7m.cardant.model.CAFileID [1, 1] The file ID.
file-put - Upload a file.
The file-put command creates or updates a file on the server.

7.2.9.2.2. Parameters

Parameter Type Cardinality Default Description
--content-type java.lang.String [0, 1] The content type (inferred if not specified).
--description java.lang.String [0, 1] The file description.
--file java.nio.file.Path [1, 1] The file.
--id com.io7m.cardant.model.CAFileID [1, 1] The file ID.
file-search-begin - Start searching for files.
The file-search-begin command begins searching for files on the server.

7.2.10.2.2. Parameters

Parameter Type Cardinality Default Description
--description-equal-to java.lang.String [0, 1] Filter files by description.
--description-not-equal-to java.lang.String [0, 1] Filter files by description.
--description-not-similar-to java.lang.String [0, 1] Filter files by description.
--description-similar-to java.lang.String [0, 1] Filter files by description.
--limit java.lang.Long [1, 1] 100 The maximum number of results per page.
--media-equal-to java.lang.String [0, 1] Filter files by media type.
--media-not-equal-to java.lang.String [0, 1] Filter files by media type.
--media-not-similar-to java.lang.String [0, 1] Filter files by media type.
--media-similar-to java.lang.String [0, 1] Filter files by media type.
--size-maximum java.lang.Long [1, 1] 9223372036854775807 The maximum file size.
--size-minimum java.lang.Long [1, 1] 0 The minimum file size.
file-search-next - Go to the next page of files.
The file-search-next command goes to the next page of file search results.
file-search-previous - Go to the previous page of files.
The file-search-previous command goes to the previous page of file search results.
files-recent - List the recent files.
The files-recent command lists the files recently used by shell commands such as file-put.
help - Display help for a given command.
The help command displays help for the given command.

7.2.14.3.1. Example

[cardant]$ help file-put
cardant: usage: file-put [named-arguments ...]

  Upload a file.

  Named parameters:
    --content-type
      Description       : The content type (inferred if not specified).
      Type              : String
      Cardinality       : [0, 1]; Specify at most once.
      Syntax            : <any sequence of characters>
    --description
      Description       : The file description.
      Type              : String
      Cardinality       : [0, 1]; Specify at most once.
      Syntax            : <any sequence of characters>
  * --file
      Description       : The file.
      Type              : Path
      Cardinality       : [1]; Specify exactly once.
      Syntax            : <platform-specific path syntax>
  * --id
      Description       : The file ID.
      Type              : CAFileID
      Cardinality       : [1]; Specify exactly once.
      Syntax            : [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}

  The command does not accept any positional arguments.
item-attachment-add - Add or update an attachment on an item.
The item-attachment-add command adds an attachment to an item.

7.2.15.2.2. Parameters

Parameter Type Cardinality Default Description
--file-id com.io7m.cardant.model.CAFileID [1, 1] The file ID.
--id com.io7m.cardant.model.CAItemID [1, 1] The item ID.
--relation java.lang.String [1, 1] The attachment relation.
item-attachment-remove - Remove an attachment from an item.
The item-attachment-remove command removes an attachment from an item.

7.2.16.2.2. Parameters

Parameter Type Cardinality Default Description
--file-id com.io7m.cardant.model.CAFileID [1, 1] The file ID.
--id com.io7m.cardant.model.CAItemID [1, 1] The item ID.
--relation java.lang.String [1, 1] The attachment relation.
item-create - Create an item.
The item-create command creates a new item.

7.2.17.2.2. Parameters

Parameter Type Cardinality Default Description
--id com.io7m.cardant.model.CAItemID [0, 1] The item ID.
--name java.lang.String [1, 1] The item name.
item-get - Retrieve an item.
The item-get command retrieves an item.

7.2.18.2.2. Parameters

Parameter Type Cardinality Default Description
--id com.io7m.cardant.model.CAItemID [1, 1] The item ID.
item-metadata-put - Add or update metadata on an item.
The item-metadata-put command adds or updates metadata on an item.

7.2.19.2.2. Parameters

Parameter Type Cardinality Default Description
--id com.io7m.cardant.model.CAItemID [1, 1] The item ID.
--metadata com.io7m.cardant.model.CAMetadataType [0, N] [] The metadata key.
item-metadata-remove - Remove metadata from an item.
The item-metadata-remove command removes metadata from an item.

7.2.20.2.2. Parameters

Parameter Type Cardinality Default Description
--id com.io7m.cardant.model.CAItemID [1, 1] The item ID.
--key com.io7m.lanark.core.RDottedName [1, N] [] The metadata key.
item-reposit-add - Add instances of items to locations.
The item-reposit-add command adds instances of an item to a location.

7.2.21.2.2. Parameters

Parameter Type Cardinality Default Description
--count java.lang.Long [1, 1] The number of instances of the item to add.
--item com.io7m.cardant.model.CAItemID [1, 1] The item ID.
--location com.io7m.cardant.model.CALocationID [1, 1] The location ID.
item-reposit-move - Move instances of items between locations.
The item-reposit-move command moves instances of an item between locations.

7.2.22.2.2. Parameters

Parameter Type Cardinality Default Description
--count java.lang.Long [1, 1] The number of instances of the item to remove.
--item com.io7m.cardant.model.CAItemID [1, 1] The item ID.
--location-from com.io7m.cardant.model.CALocationID [1, 1] The source location ID.
--location-to com.io7m.cardant.model.CALocationID [1, 1] The destination location ID.
item-reposit-remove - Remove instances of items from locations.
The item-reposit-remove command removes instances of an item from a location.

7.2.23.2.2. Parameters

Parameter Type Cardinality Default Description
--count java.lang.Long [1, 1] The number of instances of the item to remove.
--item com.io7m.cardant.model.CAItemID [1, 1] The item ID.
--location com.io7m.cardant.model.CALocationID [1, 1] The location ID.
item-search-begin - Start searching for items.
The item-search-begin command begins searching for items.

7.2.24.2.2. Parameters

Parameter Type Cardinality Default Description
--limit java.lang.Integer [1, 1] 100 The maximum number of results per page.
--location-match com.io7m.cardant.model.CAItemLocationMatchType [1, 1] any-location Only include items in locations matching the given expression.
--metadata-match com.io7m.cardant.model.CAMetadataElementMatchType [1, 1] anything Only include items with metadata matching the given expression.
--name-match com.io7m.cardant.model.CANameMatchType [1, 1] any-name Only include items that have names matching the given expression.
--type-match com.io7m.cardant.model.CATypeMatchType [1, 1] any-type Only include items that have types matching the given expression.
item-search-next - Go to the next page of items.
The item-search-next command goes to the next page of item search results.
item-search-previous - Go to the previous page of items.
The item-search-previous command goes to the previous page of item search results.
item-types-assign - Assign types to an item.
The item-types-assign command assigns types to an item.

7.2.27.2.2. Parameters

Parameter Type Cardinality Default Description
--id com.io7m.cardant.model.CAItemID [1, 1] The item ID.
--type com.io7m.lanark.core.RDottedName [0, N] [] The item types.
item-types-revoke - Revoke types from an item.
The item-types-revoke command revokes types from an item.

7.2.28.2.2. Parameters

Parameter Type Cardinality Default Description
--id com.io7m.cardant.model.CAItemID [1, 1] The item ID.
--type com.io7m.lanark.core.RDottedName [0, N] [] The item types.
location-attachment-add - Add or update an attachment on an location.
The location-attachment-add command adds an attachment to a location.

7.2.29.2.2. Parameters

Parameter Type Cardinality Default Description
--file-id com.io7m.cardant.model.CAFileID [1, 1] The file ID.
--id com.io7m.cardant.model.CALocationID [1, 1] The location ID.
--relation java.lang.String [1, 1] The attachment relation.
location-attachment-remove - Remove an attachment from an location.
The location-attachment-remove command removes an attachment from a location.

7.2.30.2.2. Parameters

Parameter Type Cardinality Default Description
--file-id com.io7m.cardant.model.CAFileID [1, 1] The file ID.
--id com.io7m.cardant.model.CALocationID [1, 1] The location ID.
--relation java.lang.String [1, 1] The attachment relation.
location-get - Retrieve a location.
The location-get command retrieves a location.

7.2.31.2.2. Parameters

Parameter Type Cardinality Default Description
--id com.io7m.cardant.model.CALocationID [1, 1] The location ID.
location-list - List locations.
The location-list command lists locations.
location-put - Create or update a location.
The location-put command creates or updates a location.

7.2.33.2.2. Parameters

Parameter Type Cardinality Default Description
--detach java.lang.Boolean [0, 1] Detach the location from its parent.
--id com.io7m.cardant.model.CALocationID [1, 1] 4e0a0b6c-d7ab-4ee1-a8f8-1f34dbb18044 The location ID.
--name java.lang.String [0, 1] The location name.
--parent com.io7m.cardant.model.CALocationID [0, 1] The parent location ID.
login - Log in.
The login command logs in.
logout - Log out.
The logout command logs out.
roles-assign - Assign roles to the given user.
The roles-assign command assigns roles to a user.

7.2.36.2.2. Parameters

Parameter Type Cardinality Default Description
--role com.io7m.medrina.api.MRoleName [0, N] [] The role name.
--user com.io7m.cardant.model.CAUserID [1, 1] The user ID.
roles-get - Return the set of roles held by the given user.
The roles-get command retrieves roles for a user.

7.2.37.2.2. Parameters

Parameter Type Cardinality Default Description
--user com.io7m.cardant.model.CAUserID [1, 1] The user ID.
roles-revoke - Revoke roles from the given user.
The roles-revoke command revokes roles from a user.

7.2.38.2.2. Parameters

Parameter Type Cardinality Default Description
--role com.io7m.medrina.api.MRoleName [0, N] [] The role name.
--user com.io7m.cardant.model.CAUserID [1, 1] The user ID.
self - Return details about the current user.
The self command returns the current user's ID and roles.

7.2.39.3.1. Example

[cardant]$ self
User ID: c5574d46-7413-43c8-b6db-31001c382ca3
┌────────────────────────────────────────────────────────────────────────────────┐
│ Role                                                                           │
├────────────────────────────────────────────────────────────────────────────────┤
│ audit.reader                                                                   │
├────────────────────────────────────────────────────────────────────────────────┤
│ inventory.admin                                                                │
├────────────────────────────────────────────────────────────────────────────────┤
│ inventory.files.reader                                                         │
├────────────────────────────────────────────────────────────────────────────────┤
│ inventory.files.writer                                                         │
├────────────────────────────────────────────────────────────────────────────────┤
│ inventory.items.reader                                                         │
├────────────────────────────────────────────────────────────────────────────────┤
│ inventory.items.writer                                                         │
├────────────────────────────────────────────────────────────────────────────────┤
│ inventory.locations.reader                                                     │
├────────────────────────────────────────────────────────────────────────────────┤
│ inventory.locations.writer                                                     │
└────────────────────────────────────────────────────────────────────────────────┘
set - Set shell options.
The set command sets shell options.

7.2.40.2.2. Parameters

Parameter Type Cardinality Default Description
--formatter com.io7m.cardant.shell.internal.CAShellCmdSet.Formatter [0, 1] Set the shell formatter.
--terminate-on-errors java.lang.Boolean [0, 1] Terminate execution on the first command that returns an error.
type-create - Create a type declaration
The type-create command creates a type declaration.

7.2.41.2.2. Parameters

Parameter Type Cardinality Default Description
--description java.lang.String [1, 1] The type description.
--name com.io7m.lanark.core.RDottedName [1, 1] The type name.
type-field-put - Add or update a field on a type declaration
The type-field-put command adds or updates a field on a type declaration.

7.2.42.2.2. Parameters

Parameter Type Cardinality Default Description
--field-description java.lang.String [1, 1] The field description.
--field-name com.io7m.lanark.core.RDottedName [1, 1] The field name.
--field-required java.lang.Boolean [1, 1] true Whether the field is required.
--field-type com.io7m.lanark.core.RDottedName [1, 1] The field type.
--type com.io7m.lanark.core.RDottedName [1, 1] The type name.
type-field-remove - Remove a field from a type declaration
The type-field-remove command removes a field from a type declaration.

7.2.43.2.2. Parameters

Parameter Type Cardinality Default Description
--field-name com.io7m.lanark.core.RDottedName [1, 1] The field name.
--type com.io7m.lanark.core.RDottedName [1, 1] The type name.
type-get - Retrieve a type declaration
The type-get command retrieves a type declaration.

7.2.44.2.2. Parameters

Parameter Type Cardinality Default Description
--name com.io7m.lanark.core.RDottedName [1, 1] The type name.
type-scalar-get - Retrieve a scalar type
The type-scalar-get command retrieves a scalar type.

7.2.45.2.2. Parameters

Parameter Type Cardinality Default Description
--name com.io7m.lanark.core.RDottedName [1, 1] The type name.
type-scalar-put - Create or update a scalar type.
The type-scalar-put command creates or updates a scalar type.

7.2.46.2.2. Parameters

Parameter Type Cardinality Default Description
--base-is-integral com.io7m.jranges.RangeInclusiveL [0, 1] [-9223372036854775808 9223372036854775807] Specify that the base type is integral and provide an inclusive range of values.
--base-is-monetary com.io7m.cardant.model.CAMonetaryRange [0, 1] [0 1000000000000] Specify that the base type is monetary and provide an inclusive range of values.
--base-is-real com.io7m.jranges.RangeInclusiveD [0, 1] [-1.7976931e+308 1.7976931e+308] Specify that the base type is real and provide an inclusive range of values.
--base-is-text java.lang.String [0, 1] .* Specify that the base type is text and provide a validating pattern.
--base-is-time com.io7m.cardant.model.CATimeRange [0, 1] [1970-01-01T00:00Z ...] Specify that the base type is a timestamp and provide an inclusive range of values.
--description java.lang.String [1, 1] The type description.
--name com.io7m.lanark.core.RDottedName [1, 1] The type name.
type-scalar-remove - Remove one or more scalar types
The type-scalar-remove command removes one or more scalar types.

7.2.47.2.2. Parameters

Parameter Type Cardinality Default Description
--name com.io7m.lanark.core.RDottedName [0, N] [] The type name.
type-scalar-search-begin - Start searching for scalar types
The type-scalar-search-begin command starts searching for scalar types.

7.2.48.2.2. Parameters

Parameter Type Cardinality Default Description
--limit java.lang.Integer [1, 1] 100 The maximum number of results per page.
--query java.lang.String [0, 1] The type search query.
type-scalar-search-next - Go to the next page of types.
The type-scalar-search-next command goes to the next page of types.
type-scalar-search-previous - Go to the previous page of types.
The type-scalar-search-previous command goes to the previous page of types.
type-search-begin - Start searching for types
The type-search-begin command starts searching for types.

7.2.51.2.2. Parameters

Parameter Type Cardinality Default Description
--limit java.lang.Integer [1, 1] 100 The maximum number of results per page.
--query java.lang.String [0, 1] The type search query.
type-search-next - Go to the next page of types.
The type-search-next command goes to the next page of types.
type-search-previous - Go to the previous page of types.
The type-search-previous command goes to the previous page of types.
version - Display the shell version.
The version command displays the shell version.
The Inventory API service exposes one or more versions of the Inventory protocol. The service uses the verdant protocol to advertise which versions of the Inventory protocol are available. Executing a GET request to the root endpoint will yield a verdant message showing which versions of the Inventory protocol are available. The Inventory protocol is assigned the protocol identifier 8ee23158-f8db-317a-a58b-45bd9d702040.
As an example, the following shows that the Inventory protocol version 1 is available at /inventory/1/0/:

8.1.1.3. Verdant Example

$ curl https://cardant.example.com:30000/ | hexdump
0000:0000  00 00 00 01 00 00 00 01  8e e2 31 58 f8 db 31 7a  |..........1X..1z|
0000:0010  a5 8b 45 bd 9d 70 20 40  00 00 00 01 00 00 00 00  |..E..p @........|
0000:0020  00 00 00 0f 2f 69 6e 76  65 6e 74 6f 72 79 2f 31  |..../inventory/1|
0000:0030  2f 30 2f                                          |/0/|
The Inventory API service exposes a health check endpoint at /health. The endpoint returns a 200 status code and the string OK if the server's most recent internal health checks succeeded. The server returns a 500 status code and string not equal to OK if the server's most recent internal health checks failed. In both cases, the string is returned directly as a text/plain UTF-8 value.
The version 1 Inventory protocol uses cedarbridge encoded messages over HTTP(s).
Send an CAI1CommandLogin command to /inventory/1/0/login. If the login succeeds, a cookie named CARDANT_INVENTORY_SESSION will be set. This cookie must be included with all subsequent requests.
After logging in successfully, send commands of type CAI1Command* to /inventory/1/0/command. Failed commands will yield a value of type CAI1ResponseError, whilst successful results will yield values of type CAI1Response*.

8.1.4.1. Schemas

;
; Copyright © 2023 Mark Raynsford <code@io7m.com> https://www.io7m.com
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; copyright notice and this permission notice appear in all copies.
;
; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
; SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
; IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
;

[language cedarbridge 1 0]

[package com.io7m.cardant.protocol.inventory.cb]

[import com.io7m.cedarbridge cb]
[import com.io7m.cedarbridge.time ct]

[variant CAI1ComparisonExact
  [parameter T]
  [case Anything]
  [case IsEqualTo    [field value T]]
  [case IsNotEqualTo [field value T]]
]

[variant CAI1ComparisonFuzzy
  [parameter T]
  [case Anything]
  [case IsEqualTo      [field value T]]
  [case IsNotEqualTo   [field value T]]
  [case IsSimilarTo    [field value T]]
  [case IsNotSimilarTo [field value T]]
]

[variant CAI1ComparisonSet
  [parameter T]
  [case Anything]
  [case IsEqualTo     [field value [cb:List T]]]
  [case IsNotEqualTo  [field value [cb:List T]]]
  [case IsSubsetOf    [field value [cb:List T]]]
  [case IsSupersetOf  [field value [cb:List T]]]
  [case IsOverlapping [field value [cb:List T]]]
]

[documentation CAI1Id "An identifier value."]
[variant CAI1Id
  [case CAI1FileID     [field id cb:UUID]]
  [case CAI1ItemID     [field id cb:UUID]]
  [case CAI1LocationID [field id cb:UUID]]
  [case CAI1UserID     [field id cb:UUID]]
]

[documentation CAI1File "A file."]
[variant CAI1File
  [case CAI1FileWithoutData
    [documentation id "The file ID."]
    [field id cb:UUID]
    [documentation description "The file description."]
    [field description cb:String]
    [documentation mediaType "The media type."]
    [field mediaType cb:String]
    [documentation size "The file size."]
    [field size cb:IntegerUnsigned64]
    [documentation hashAlgorithm "The hash algorithm name."]
    [field hashAlgorithm cb:String]
    [documentation hashValue "The hash value."]
    [field hashValue cb:String]]
  [case CAI1FileWithData
    [documentation id "The file ID."]
    [field id cb:UUID]
    [documentation description "The file description."]
    [field description cb:String]
    [documentation mediaType "The media type."]
    [field mediaType cb:String]
    [documentation size "The file size."]
    [field size cb:IntegerUnsigned64]
    [documentation hashAlgorithm "The hash algorithm name."]
    [field hashAlgorithm cb:String]
    [documentation hashValue "The hash value."]
    [field hashValue cb:String]
    [documentation data "The file data."]
    [field data cb:ByteArray]]
]

[documentation CAI1Metadata "A metadata value."]
[variant CAI1Metadata
  [case Integral
    [documentation key "The metadata value key."]
    [field key cb:String]
    [documentation value "The metadata value."]
    [field value cb:IntegerSigned64]
  ]
  [case Text
    [documentation key "The metadata value key."]
    [field key cb:String]
    [documentation value "The metadata value."]
    [field value cb:String]
  ]
  [case Time
    [documentation key "The metadata value key."]
    [field key cb:String]
    [documentation value "The metadata value."]
    [field value ct:OffsetDateTime]
  ]
  [case Monetary
    [documentation key "The metadata value key."]
    [field key cb:String]
    [documentation value "The metadata value."]
    [field value cb:String]
    [documentation currency "The metadata currency unit."]
    [field currency cb:String]
  ]
  [case Real
    [documentation key "The metadata value key."]
    [field key cb:String]
    [documentation value "The metadata value."]
    [field value cb:Float64]
  ]
]

[documentation CAI1AttachmentKey "The values that uniquely identify an attachment on an object."]
[record CAI1AttachmentKey
  [documentation id "The file ID."]
  [field id cb:UUID]
  [documentation relation "The relation."]
  [field relation cb:String]
]

[documentation CAI1Attachment "An item attachment."]
[record CAI1Attachment
  [documentation file "The file ID."]
  [field file CAI1File]
  [documentation relation "The relation."]
  [field relation cb:String]
]

[documentation CAI1Item "An item."]
[record CAI1Item
  [documentation id "The item ID."]
  [field id cb:UUID]
  [documentation name "The item name."]
  [field name cb:String]
  [documentation countTotal "The total item count."]
  [field countTotal cb:IntegerUnsigned64]
  [documentation countHere "The item count in this location."]
  [field countHere cb:IntegerUnsigned64]
  [documentation metadata "The item metadata."]
  [field metadata [cb:Map cb:String CAI1Metadata]]
  [documentation attachments "The item attachments."]
  [field attachments [cb:Map CAI1AttachmentKey CAI1Attachment]]
  [documentation types "The item types."]
  [field types [cb:List cb:String]]
]

[documentation CAI1ListLocationBehaviour "The behaviour requested for listing items in locations."]
[variant CAI1ListLocationBehaviour
  [documentation CAI1ListLocationExact "Only list items within the exact given location."]
  [case CAI1ListLocationExact
    [field locationId cb:UUID]]
  [documentation CAI1ListLocationWithDescendants "List items within the given location and all descendants of the given location."]
  [case CAI1ListLocationWithDescendants
    [field locationId cb:UUID]]
  [documentation CAI1ListLocationsAll "List items within all locations."]
  [case CAI1ListLocationsAll]
]

[documentation CAI1ItemLocation "An assertion that there are count instances of item at location."]
[record CAI1ItemLocation
  [documentation itemId "The item."]
  [field itemId cb:UUID]
  [documentation locationId "The location ID."]
  [field locationId cb:UUID]
  [documentation count "The count."]
  [field count cb:IntegerUnsigned64]
]

[documentation CAI1ItemLocations "A set of assertions indicating items in locations."]
[record CAI1ItemLocations
  [documentation locations "The item locations."]
  [field locations [cb:Map cb:UUID [cb:Map cb:UUID CAI1ItemLocation]]]
]

[documentation CAI1ItemReposit "The type of item reposit operations."]
[variant CAI1ItemReposit
  [documentation CAI1ItemRepositAdd "An operation that adds a set of items to a storage location."]
  [case CAI1ItemRepositAdd
    [documentation itemId "The item."]
    [field itemId cb:UUID]
    [documentation locationId "The location ID."]
    [field locationId cb:UUID]
    [documentation count "The count."]
    [field count cb:IntegerUnsigned64]]
  [documentation CAI1ItemRepositMove "An operation that moves a set of items from one storage location to another."]
  [case CAI1ItemRepositMove
    [documentation itemId "The item."]
    [field itemId cb:UUID]
    [documentation locationFrom "The from location."]
    [field locationFrom cb:UUID]
    [documentation locationTo "The to location."]
    [field locationTo cb:UUID]
    [documentation count "The count."]
    [field count cb:IntegerUnsigned64]]
  [documentation CAI1ItemRepositRemove "An operation that removes a set of items from a storage location."]
  [case CAI1ItemRepositRemove
    [documentation itemId "The item."]
    [field itemId cb:UUID]
    [documentation locationId "The location ID."]
    [field locationId cb:UUID]
    [documentation count "The count."]
    [field count cb:IntegerUnsigned64]]
]

[documentation CAI1LocationSummary "A summary of a location."]
[record CAI1LocationSummary
  [field id cb:UUID]
  [field parent [cb:Option cb:UUID]]
  [field name cb:String]
]

[documentation CAI1Location "A location."]
[record CAI1Location
  [documentation locationId "The location ID."]
  [field locationId cb:UUID]
  [documentation parent "The location parent."]
  [field parent [cb:Option cb:UUID]]
  [documentation name "The location name."]
  [field name cb:String]
  [documentation metadata "The item metadata."]
  [field metadata [cb:Map cb:String CAI1Metadata]]
  [documentation attachments "The item attachments."]
  [field attachments [cb:Map CAI1AttachmentKey CAI1Attachment]]
  [documentation types "The item types."]
  [field types [cb:List cb:String]]
]

[documentation CAI1ItemColumn "The item column by which to order results."]
[variant CAI1ItemColumn
  [documentation CAI1ById "Order by item ID."]
  [case CAI1ById]
  [documentation CAI1ByName "Order by item name."]
  [case CAI1ByName]
]

[documentation CAI1ItemColumnOrdering "The item column by which to order results."]
[record CAI1ItemColumnOrdering
  [documentation column "The item column."]
  [field column CAI1ItemColumn]
  [documentation ascending "Whether results should be in ascending order."]
  [field ascending cb:Boolean]
]

[documentation CAI1NameMatch "An expression against which item names are matched."]
[variant CAI1NameMatch
  [case CAI1NameMatchAny]
  [case CAI1NameMatchExact  [field text cb:String]]
  [case CAI1NameMatchSearch [field query cb:String]]
]

[documentation CAI1TypeMatch "An expression against which item types are matched."]
[variant CAI1TypeMatch
  [case CAI1TypeMatchAny]
  [case CAI1TypeMatchAnyOf [field types [cb:List cb:String]]]
  [case CAI1TypeMatchAllOf [field types [cb:List cb:String]]]
]

[documentation CAI1MetadataValueMatch "An expression against which item metadata values are matched."]
[variant CAI1MetadataValueMatch
  [case Anything]
  [case IntegralWithinRange
    [field lower cb:IntegerSigned64]
    [field upper cb:IntegerSigned64]
  ]
  [case RealWithinRange
    [field lower cb:Float64]
    [field upper cb:Float64]
  ]
  [case TimeWithinRange
    [field lower ct:OffsetDateTime]
    [field upper ct:OffsetDateTime]
  ]
  [case MonetaryWithinRange
    [field lower cb:String]
    [field upper cb:String]
  ]
  [case MonetaryWithCurrency
    [field currency cb:String]
  ]
  [case TextExact
    [field exact cb:String]
  ]
  [case TextSearch
    [field search cb:String]
  ]
]

[documentation CAI1MetadataNameMatch "An expression against which item metadata names are matched."]
[variant CAI1MetadataNameMatch
  [case Anything]
  [case Exact
    [field name cb:String]
  ]
  [case Search
    [field search cb:String]
  ]
]

[documentation CAI1MetadataElementMatch "An expression against which item metadata is matched."]
[variant CAI1MetadataElementMatch
  [case And
    [field e0 CAI1MetadataElementMatch]
    [field e1 CAI1MetadataElementMatch]
  ]
  [case Or
    [field e0 CAI1MetadataElementMatch]
    [field e1 CAI1MetadataElementMatch]
  ]
  [case Specific
    [field name CAI1MetadataNameMatch]
    [field value CAI1MetadataValueMatch]
  ]
]

[documentation CAI1ItemSearchParameters "The item search parameters."]
[record CAI1ItemSearchParameters
  [documentation location "The item location search behaviour."]
  [field location CAI1ListLocationBehaviour]
  [documentation nameMatch "The item name match expression."]
  [field nameMatch CAI1NameMatch]
  [documentation typeMatch "The item type match expression."]
  [field typeMatch CAI1TypeMatch]
  [documentation metaMatch "The item metadata match expression."]
  [field metaMatch CAI1MetadataElementMatch]
  [documentation order "The item result ordering."]
  [field order CAI1ItemColumnOrdering]
  [documentation limit "The item limit."]
  [field limit cb:IntegerUnsigned32]
]

[documentation CAI1FileColumn "The item column by which to order results."]
[variant CAI1FileColumn
  [documentation CAI1ById "Order by file ID."]
  [case CAI1ById]
  [documentation CAI1ByDescription "Order by file description."]
  [case CAI1ByDescription]
]

[documentation CAI1FileColumnOrdering "The file column by which to order results."]
[record CAI1FileColumnOrdering
  [documentation column "The file column."]
  [field column CAI1FileColumn]
  [documentation ascending "Whether results should be in ascending order."]
  [field ascending cb:Boolean]
]

[documentation CAI1SizeRange "A size range."]
[record CAI1SizeRange
  [documentation sizeMinimum "The lower bound (inclusive)."]
  [field sizeMinimum cb:IntegerUnsigned64]
  [documentation sizeMaximum "The upper bound (inclusive)."]
  [field sizeMaximum cb:IntegerUnsigned64]
]

[documentation CAI1TimeRange "An inclusive time range."]
[record CAI1TimeRange
  [field timeLower ct:OffsetDateTime]
  [field timeUpper ct:OffsetDateTime]
]

[documentation CAI1FileSearchParameters "The item search parameters."]
[record CAI1FileSearchParameters
  [documentation search "The file description search query."]
  [field search [CAI1ComparisonFuzzy cb:String]]
  [documentation mediaType "The file media type search query."]
  [field mediaType [CAI1ComparisonFuzzy cb:String]]
  [documentation sizeRange "The file size range query."]
  [field sizeRange CAI1SizeRange]
  [documentation order "The file result ordering."]
  [field order CAI1FileColumnOrdering]
  [documentation limit "The file limit."]
  [field limit cb:IntegerUnsigned32]
]

[documentation CAI1TypeScalarSearchParameters "The scalar type search parameters."]
[record CAI1TypeScalarSearchParameters
  [documentation search "The type description search query."]
  [field search [cb:Option cb:String]]
  [documentation limit "The limit."]
  [field limit cb:IntegerUnsigned32]
]

[documentation CAI1Page "A page of search results."]
[record CAI1Page
  [parameter T]
  [field items [cb:List T]]
  [field pageIndex cb:IntegerUnsigned32]
  [field pageCount cb:IntegerUnsigned32]
  [field pageFirstOffset cb:IntegerUnsigned64]
]

[documentation CAI1ItemSummary "A summary of an item."]
[record CAI1ItemSummary
  [field id cb:UUID]
  [field name cb:String]
]

[documentation CAI1TypeScalar "A scalar type."]
[variant CAI1TypeScalar
  [case Integral
    [documentation name "The type name."]
    [field name cb:String]
    [documentation description "The type description."]
    [field description cb:String]
    [field rangeLower cb:IntegerSigned64]
    [field rangeUpper cb:IntegerSigned64]
  ]
  [case Text
    [documentation name "The type name."]
    [field name cb:String]
    [documentation description "The type description."]
    [field description cb:String]
    [field pattern cb:String]
  ]
  [case Time
    [documentation name "The type name."]
    [field name cb:String]
    [documentation description "The type description."]
    [field description cb:String]
    [field rangeLower ct:OffsetDateTime]
    [field rangeUpper ct:OffsetDateTime]
  ]
  [case Monetary
    [documentation name "The type name."]
    [field name cb:String]
    [documentation description "The type description."]
    [field description cb:String]
    [field rangeLower cb:String]
    [field rangeUpper cb:String]
  ]
  [case Real
    [documentation name "The type name."]
    [field name cb:String]
    [documentation description "The type description."]
    [field description cb:String]
    [field rangeLower cb:Float64]
    [field rangeUpper cb:Float64]
  ]
]

[documentation CAI1TypeField "A field within a type declaration."]
[record CAI1TypeField
  [documentation name "The type field name."]
  [field name cb:String]
  [documentation description "The type field description."]
  [field description cb:String]
  [documentation type "The type of the field."]
  [field type CAI1TypeScalar]
  [documentation required "Whether or not the field is required to be present."]
  [field required cb:Boolean]
]

[documentation CAI1TypeDeclaration "A type declaration."]
[record CAI1TypeDeclaration
  [documentation name "The type declaration name."]
  [field name cb:String]
  [documentation description "The type declaration description."]
  [field description cb:String]
  [documentation fields "Whether or not the field is required to be present."]
  [field fields [cb:Map cb:String CAI1TypeField]]
]

[documentation CAI1TypeDeclarationSummary "A type declaration summary."]
[record CAI1TypeDeclarationSummary
  [documentation name "The type declaration name."]
  [field name cb:String]
  [documentation description "The type declaration description."]
  [field description cb:String]
]

[documentation CAI1TypeDeclarationSearchParameters "The declared type search parameters."]
[record CAI1TypeDeclarationSearchParameters
  [documentation search "The type description search query."]
  [field search [cb:Option cb:String]]
  [documentation limit "The limit."]
  [field limit cb:IntegerUnsigned32]
]

[documentation CAI1AuditSearchParameters "The audit search parameters."]
[record CAI1AuditSearchParameters
  [documentation owner "Limit to events with the given owner."]
  [field owner [cb:Option cb:UUID]]
  [documentation type "Limit to events with the given type."]
  [field type [CAI1ComparisonExact cb:String]]
  [documentation timeRange "Limit to events in the given time range."]
  [field timeRange CAI1TimeRange]
  [documentation pageSize "The page size."]
  [field pageSize cb:IntegerUnsigned32]
]

[documentation CAI1AuditEvent "An audit event."]
[record CAI1AuditEvent
  [documentation id "The event ID."]
  [field id cb:IntegerUnsigned64]
  [documentation time "The event time."]
  [field time ct:OffsetDateTime]
  [documentation owner "The event owner."]
  [field owner cb:UUID]
  [documentation type "The event type."]
  [field type cb:String]
  [documentation data "The event data."]
  [field data [cb:Map cb:String cb:String]]
]

;
; Commands.
;

[documentation CAI1CommandLogin "A request to log in."]
[record CAI1CommandLogin
  [documentation userName "The username."]
  [field userName cb:String]
  [documentation password "The password."]
  [field password cb:String]
  [documentation metadata "Extra metadata included with the login request."]
  [field metadata [cb:Map cb:String cb:String]]
]

[documentation CAI1CommandFilePut "Add or update files."]
[record CAI1CommandFilePut
  [documentation file "The file."]
  [field file CAI1File]
]

[documentation CAI1CommandFileRemove "Remove files."]
[record CAI1CommandFileRemove
  [documentation id "The file ID."]
  [field id cb:UUID]
]

[documentation CAI1CommandItemAttachmentAdd "Add item attachments."]
[record CAI1CommandItemAttachmentAdd
  [documentation itemId "The item ID."]
  [field itemId cb:UUID]
  [documentation fileId "The file ID."]
  [field fileId cb:UUID]
  [documentation relation "The attachment relation."]
  [field relation cb:String]
]

[documentation CAI1CommandItemAttachmentRemove "Remove item attachments."]
[record CAI1CommandItemAttachmentRemove
  [documentation itemId "The item ID."]
  [field itemId cb:UUID]
  [documentation fileId "The file ID."]
  [field fileId cb:UUID]
  [documentation relation "The attachment relation."]
  [field relation cb:String]
]

[documentation CAI1CommandItemCreate "Create a new item."]
[record CAI1CommandItemCreate
  [documentation itemId "The item ID."]
  [field itemId cb:UUID]
  [documentation name "The item name."]
  [field name cb:String]
]

[documentation CAI1CommandItemGet "Retrieve an item."]
[record CAI1CommandItemGet
  [documentation itemId "The item ID."]
  [field itemId cb:UUID]
]

[documentation CAI1CommandFileGet "Retrieve a file."]
[record CAI1CommandFileGet
  [documentation fileId "The file ID."]
  [field fileId cb:UUID]
]

[documentation CAI1CommandItemLocationsList "List all the locations an item is in, and how many times the item is in them."]
[record CAI1CommandItemLocationsList
  [documentation itemId "The item ID."]
  [field itemId cb:UUID]
]

[documentation CAI1CommandItemMetadataPut "Add or update metadata values in an item."]
[record CAI1CommandItemMetadataPut
  [documentation itemId "The item ID."]
  [field itemId cb:UUID]
  [documentation metadatas "The item metadatas."]
  [field metadatas [cb:List CAI1Metadata]]
]

[documentation CAI1CommandItemMetadataRemove "Remove metadata values from an item."]
[record CAI1CommandItemMetadataRemove
  [documentation itemId "The item ID."]
  [field itemId cb:UUID]
  [documentation metadatas "The item metadatas."]
  [field metadatas [cb:List cb:String]]
]

[documentation CAI1CommandItemReposit "Reposit an item."]
[record CAI1CommandItemReposit
  [documentation reposit "The item reposition."]
  [field reposit CAI1ItemReposit]
]

[documentation CAI1CommandItemsRemove "Remove items."]
[record CAI1CommandItemsRemove
  [documentation items "The items."]
  [field items [cb:List cb:UUID]]
]

[documentation CAI1CommandItemSetName "Update the names of items."]
[record CAI1CommandItemSetName
  [documentation item "The item ID."]
  [field item cb:UUID]
  [documentation name "The item name."]
  [field name cb:String]
]

[documentation CAI1CommandLocationGet "Retrieve a location."]
[record CAI1CommandLocationGet
  [documentation location "The location ID."]
  [field location cb:UUID]
]

[documentation CAI1CommandLocationList "List locations."]
[record CAI1CommandLocationList]

[documentation CAI1CommandLocationPut "Create or update a location."]
[record CAI1CommandLocationPut
  [documentation location "The location."]
  [field location CAI1Location]
]

[documentation CAI1CommandItemSearchBegin "Start searching for items."]
[record CAI1CommandItemSearchBegin
  [documentation parameters "The search parameters."]
  [field parameters CAI1ItemSearchParameters]
]

[documentation CAI1CommandItemSearchNext "Return the next page of search results."]
[record CAI1CommandItemSearchNext]

[documentation CAI1CommandItemSearchPrevious "Return the next page of search results."]
[record CAI1CommandItemSearchPrevious]

[documentation CAI1CommandFileSearchBegin "Start searching for items."]
[record CAI1CommandFileSearchBegin
  [documentation parameters "The search parameters."]
  [field parameters CAI1FileSearchParameters]
]

[documentation CAI1CommandFileSearchNext "Return the next page of search results."]
[record CAI1CommandFileSearchNext]

[documentation CAI1CommandFileSearchPrevious "Return the next page of search results."]
[record CAI1CommandFileSearchPrevious]

[documentation CAI1CommandRolesAssign "Assign a set of roles to a user."]
[record CAI1CommandRolesAssign
  [documentation user "The target user."]
  [field user cb:UUID]
  [documentation roles "The assigned roles."]
  [field roles [cb:List cb:String]]
]

[documentation CAI1CommandRolesRevoke "Revoke a set of roles from a user."]
[record CAI1CommandRolesRevoke
  [documentation user "The target user."]
  [field user cb:UUID]
  [documentation roles "The roles."]
  [field roles [cb:List cb:String]]
]

[documentation CAI1CommandRolesRevoke "Get the roles for a user."]
[record CAI1CommandRolesGet
  [documentation user "The target user."]
  [field user cb:UUID]
]

[documentation CAI1CommandTypeScalarPut "Create or update scalar types."]
[record CAI1CommandTypeScalarPut
  [documentation types "The types."]
  [field types [cb:List CAI1TypeScalar]]
]

[documentation CAI1CommandTypeScalarGet "Get a scalar type."]
[record CAI1CommandTypeScalarGet
  [documentation name "The type name."]
  [field name cb:String]
]

[documentation CAI1CommandTypeScalarRemove "Remove scalar types."]
[record CAI1CommandTypeScalarRemove
  [documentation types "The type names."]
  [field types [cb:List cb:String]]
]

[documentation CAI1CommandTypeScalarSearchBegin "Search for scalar types."]
[record CAI1CommandTypeScalarSearchBegin
  [documentation parameters "The parameters."]
  [field parameters CAI1TypeScalarSearchParameters]
]

[documentation CAI1CommandTypeScalarSearchNext "Search for scalar types."]
[record CAI1CommandTypeScalarSearchNext]

[documentation CAI1CommandTypeScalarSearchPrevious "Search for scalar types."]
[record CAI1CommandTypeScalarSearchPrevious]

[documentation CAI1CommandTypeDeclarationPut "Create or update declared types."]
[record CAI1CommandTypeDeclarationPut
  [documentation types "The types."]
  [field types [cb:List CAI1TypeDeclaration]]
]

[documentation CAI1CommandTypeDeclarationGet "Get a declared type."]
[record CAI1CommandTypeDeclarationGet
  [documentation name "The type name."]
  [field name cb:String]
]

[documentation CAI1CommandTypeDeclarationRemove "Remove declared types."]
[record CAI1CommandTypeDeclarationRemove
  [documentation types "The type names."]
  [field types [cb:List cb:String]]
]

[documentation CAI1CommandTypeDeclarationSearchBegin "Search for declared types."]
[record CAI1CommandTypeDeclarationSearchBegin
  [documentation parameters "The parameters."]
  [field parameters CAI1TypeDeclarationSearchParameters]
]

[documentation CAI1CommandTypeDeclarationSearchNext "Search for declared types."]
[record CAI1CommandTypeDeclarationSearchNext]

[documentation CAI1CommandTypeDeclarationSearchPrevious "Search for declared types."]
[record CAI1CommandTypeDeclarationSearchPrevious]

[documentation CAI1CommandItemTypesAssign "Assign a set of types to an item."]
[record CAI1CommandItemTypesAssign
  [documentation item "The target item."]
  [field item cb:UUID]
  [documentation types "The assigned types."]
  [field types [cb:List cb:String]]
]

[documentation CAI1CommandItemTypesRevoke "Revoke a set of types from an item."]
[record CAI1CommandItemTypesRevoke
  [documentation item "The target item."]
  [field item cb:UUID]
  [documentation types "The removed types."]
  [field types [cb:List cb:String]]
]

[documentation CAI1CommandLocationMetadataPut "Add or update metadata values in a location."]
[record CAI1CommandLocationMetadataPut
  [documentation locationId "The location ID."]
  [field locationId cb:UUID]
  [documentation metadatas "The location metadatas."]
  [field metadatas [cb:List CAI1Metadata]]
]

[documentation CAI1CommandLocationMetadataRemove "Remove metadata values from a location."]
[record CAI1CommandLocationMetadataRemove
  [documentation locationId "The location ID."]
  [field locationId cb:UUID]
  [documentation metadatas "The location metadatas."]
  [field metadatas [cb:List cb:String]]
]

[documentation CAI1CommandLocationTypesAssign "Assign a set of types to a location."]
[record CAI1CommandLocationTypesAssign
  [documentation location "The target location."]
  [field location cb:UUID]
  [documentation types "The assigned types."]
  [field types [cb:List cb:String]]
]

[documentation CAI1CommandLocationTypesRevoke "Revoke a set of types from a location."]
[record CAI1CommandLocationTypesRevoke
  [documentation location "The target location."]
  [field location cb:UUID]
  [documentation types "The removed types."]
  [field types [cb:List cb:String]]
]

[documentation CAI1CommandLocationAttachmentAdd "Add location attachments."]
[record CAI1CommandLocationAttachmentAdd
  [documentation locationId "The location ID."]
  [field locationId cb:UUID]
  [documentation fileId "The file ID."]
  [field fileId cb:UUID]
  [documentation relation "The attachment relation."]
  [field relation cb:String]
]

[documentation CAI1CommandLocationAttachmentRemove "Remove location attachments."]
[record CAI1CommandLocationAttachmentRemove
  [documentation locationId "The location ID."]
  [field locationId cb:UUID]
  [documentation fileId "The file ID."]
  [field fileId cb:UUID]
  [documentation relation "The attachment relation."]
  [field relation cb:String]
]

[documentation CAI1CommandAuditSearchBegin "Start searching audit events."]
[record CAI1CommandAuditSearchBegin
  [documentation parameters "The search parameters."]
  [field parameters CAI1AuditSearchParameters]
]

[documentation CAI1CommandAuditSearchNext "Continue searching audit events."]
[record CAI1CommandAuditSearchNext]

[documentation CAI1CommandAuditSearchPrevious "Continue searching audit events."]
[record CAI1CommandAuditSearchPrevious]

;
; Responses.
;

[documentation CAI1ResponseBlame "A blame assignment."]
[variant CAI1ResponseBlame
  [documentation BlameClient "The client sent a bad response."]
  [case BlameClient]
  [documentation BlameServer "Something went wrong on the server."]
  [case BlameServer]
]

[documentation CAI1ResponseError "An error response."]
[record CAI1ResponseError
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation errorCode "The error code."]
  [field errorCode cb:String]
  [documentation message "The error message."]
  [field message cb:String]
  [documentation attributes "The error attributes."]
  [field attributes [cb:Map cb:String cb:String]]
  [documentation remediatingAction "The remediating action, if any."]
  [field remediatingAction [cb:Option cb:String]]
  [documentation blame "The blame assignment."]
  [field blame CAI1ResponseBlame]
]

[documentation CAI1ResponseLogin "A response to CAI1CommandLogin."]
[record CAI1ResponseLogin
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation userId "The ID of the user that logged in."]
  [field userId cb:UUID]
]

[documentation CAI1ResponseFilePut "A response to CAI1CommandFilePut."]
[record CAI1ResponseFilePut
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation file "The file."]
  [field file CAI1File]
]

[documentation CAI1ResponseFileRemove "A response to CAI1CommandFileRemove."]
[record CAI1ResponseFileRemove
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation id "The file ID."]
  [field id cb:UUID]
]

[documentation CAI1ResponseItemAttachmentAdd "A response to CAI1CommandItemAttachmentAdd."]
[record CAI1ResponseItemAttachmentAdd
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation item "The item."]
  [field item CAI1Item]
]

[documentation CAI1ResponseItemAttachmentRemove "A response to CAI1CommandItemAttachmentRemove."]
[record CAI1ResponseItemAttachmentRemove
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation item "The item."]
  [field item CAI1Item]
]

[documentation CAI1ResponseItemCreate "A response to CAI1CommandItemCreate."]
[record CAI1ResponseItemCreate
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation item "The item."]
  [field item CAI1Item]
]

[documentation CAI1ResponseItemGet "A response to CAI1CommandItemGet."]
[record CAI1ResponseItemGet
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation item "The item."]
  [field item CAI1Item]
]

[documentation CAI1ResponseFileGet "A response to CAI1CommandFileGet."]
[record CAI1ResponseFileGet
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation file "The file."]
  [field file CAI1File]
]

[documentation CAI1ResponseItemLocationsList "A response to CAI1CommandItemLocationsList."]
[record CAI1ResponseItemLocationsList
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation itemLocations "The item locations."]
  [field itemLocations CAI1ItemLocations]
]

[documentation CAI1ResponseItemMetadataPut "A response to CAI1CommandItemMetadataPut."]
[record CAI1ResponseItemMetadataPut
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation item "The item."]
  [field item CAI1Item]
]

[documentation CAI1ResponseItemMetadataRemove "A response to CAI1CommandItemMetadataRemove."]
[record CAI1ResponseItemMetadataRemove
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation item "The item."]
  [field item CAI1Item]
]

[documentation CAI1ResponseItemReposit "A response to CAI1CommandItemReposit."]
[record CAI1ResponseItemReposit
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation item "The item."]
  [field item CAI1Item]
]

[documentation CAI1ResponseItemsRemove "A response to CAI1CommandItemsRemove."]
[record CAI1ResponseItemsRemove
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation items "The items."]
  [field items [cb:List cb:UUID]]
]

[documentation CAI1ResponseItemSetName "A response to CAI1CommandItemSetName."]
[record CAI1ResponseItemSetName
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation item "The item."]
  [field item CAI1Item]
]

[documentation CAI1ResponseLocationGet "A response to CAI1CommandLocationGet."]
[record CAI1ResponseLocationGet
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation location "The location."]
  [field location CAI1Location]
]

[documentation CAI1ResponseLocationList "A response to CAI1CommandLocationList."]
[record CAI1ResponseLocationList
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation locations "The locations."]
  [field locations [cb:Map cb:UUID CAI1LocationSummary]]
]

[documentation CAI1ResponseLocationPut "A response to CAI1CommandLocationPut."]
[record CAI1ResponseLocationPut
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation location "The location."]
  [field location CAI1Location]
]

[documentation CAI1ResponseItemSearch "A response to CAI1CommandItemSearch."]
[record CAI1ResponseItemSearch
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation results "The results."]
  [field results [CAI1Page CAI1ItemSummary]]
]

[documentation CAI1ResponseFileSearch "A response to CAI1CommandFileSearch."]
[record CAI1ResponseFileSearch
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation results "The results."]
  [field results [CAI1Page CAI1File]]
]

[documentation CAI1ResponseRolesAssign "A response to CAI1ResponseRolesAssign."]
[record CAI1ResponseRolesAssign
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
]

[documentation CAI1ResponseRolesRevoke "A response to CAI1ResponseRolesRevoke."]
[record CAI1ResponseRolesRevoke
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
]

[documentation CAI1ResponseRolesGet "A response to CAI1ResponseRolesGet."]
[record CAI1ResponseRolesGet
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation roles "The roles."]
  [field roles [cb:List cb:String]]
]

[documentation CAI1ResponseTypeScalarPut "A response to CAI1CommandTypeScalarPut."]
[record CAI1ResponseTypeScalarPut
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation types "The scalar types."]
  [field types [cb:List CAI1TypeScalar]]
]

[documentation CAI1ResponseTypeScalarGet "A response to CAI1CommandTypeScalarGet."]
[record CAI1ResponseTypeScalarGet
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation type "The scalar type."]
  [field type CAI1TypeScalar]
]

[documentation CAI1ResponseTypeScalarRemove "A response to CAI1CommandTypeScalarRemove."]
[record CAI1ResponseTypeScalarRemove
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
]

[documentation CAI1ResponseTypeScalarSearch "A response to CAI1CommandTypeScalarSearch*."]
[record CAI1ResponseTypeScalarSearch
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation results "The results."]
  [field results [CAI1Page CAI1TypeScalar]]
]

[documentation CAI1ResponseTypeDeclarationPut "A response to CAI1CommandTypeDeclarationPut."]
[record CAI1ResponseTypeDeclarationPut
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation types "The scalar types."]
  [field types [cb:List CAI1TypeDeclaration]]
]

[documentation CAI1ResponseTypeDeclarationGet "A response to CAI1CommandTypeDeclarationGet."]
[record CAI1ResponseTypeDeclarationGet
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation type "The scalar type."]
  [field type CAI1TypeDeclaration]
]

[documentation CAI1ResponseTypeDeclarationRemove "A response to CAI1CommandTypeDeclarationRemove."]
[record CAI1ResponseTypeDeclarationRemove
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
]

[documentation CAI1ResponseTypeDeclarationSearch "A response to CAI1CommandTypeDeclarationSearch*."]
[record CAI1ResponseTypeDeclarationSearch
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation results "The results."]
  [field results [CAI1Page CAI1TypeDeclarationSummary]]
]

[documentation CAI1ResponseItemTypesAssign "A response to CAI1CommandItemTypesAssign."]
[record CAI1ResponseItemTypesAssign
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation item "The item."]
  [field item CAI1Item]
]

[documentation CAI1ResponseItemTypesRevoke "A response to CAI1CommandItemTypesRevoke."]
[record CAI1ResponseItemTypesRevoke
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation item "The item."]
  [field item CAI1Item]
]

[documentation CAI1ResponseLocationMetadataPut "A response to CAI1CommandLocationMetadataPut."]
[record CAI1ResponseLocationMetadataPut
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation location "The location."]
  [field location CAI1Location]
]

[documentation CAI1ResponseLocationMetadataRemove "A response to CAI1CommandLocationMetadataRemove."]
[record CAI1ResponseLocationMetadataRemove
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation location "The location."]
  [field location CAI1Location]
]

[documentation CAI1ResponseLocationTypesAssign "A response to CAI1CommandLocationTypesAssign."]
[record CAI1ResponseLocationTypesAssign
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation location "The location."]
  [field location CAI1Location]
]

[documentation CAI1ResponseLocationTypesRevoke "A response to CAI1CommandLocationTypesRevoke."]
[record CAI1ResponseLocationTypesRevoke
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation location "The location."]
  [field location CAI1Location]
]

[documentation CAI1ResponseLocationAttachmentAdd "A response to CAI1CommandLocationAttachmentAdd."]
[record CAI1ResponseLocationAttachmentAdd
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation location "The location."]
  [field location CAI1Location]
]

[documentation CAI1ResponseLocationAttachmentRemove "A response to CAI1CommandLocationAttachmentRemove."]
[record CAI1ResponseLocationAttachmentRemove
  [documentation requestId "The ID of the request that yielded this response."]
  [field requestId cb:UUID]
  [documentation location "The location."]
  [field location CAI1Location]
]

[documentation CAI1ResponseAuditSearch "A response to CAI1CommandAuditSearchBegin."]
[record CAI1ResponseAuditSearch
  [documentation messageId "The message ID"]
  [field messageId cb:UUID]
  [documentation results "The results"]
  [field results [CAI1Page CAI1AuditEvent]]
]

;
; Events.
;

[documentation CAI1EventUpdated "Data on the server was updated."]
[record CAI1EventUpdated
  [documentation updated "The list of objects that were updated."]
  [field updated [cb:List CAI1Id]]
  [documentation removed "The list of objects that were removed."]
  [field removed [cb:List CAI1Id]]]

;
; Protocol.
;

[documentation CAI "The Inventory protocol."]
[protocol CAI
  [version 1
    [types-added
      CAI1CommandAuditSearchBegin
      CAI1CommandAuditSearchNext
      CAI1CommandAuditSearchPrevious
      CAI1CommandFileGet
      CAI1CommandFilePut
      CAI1CommandFileRemove
      CAI1CommandFileSearchBegin
      CAI1CommandFileSearchNext
      CAI1CommandFileSearchPrevious
      CAI1CommandItemAttachmentAdd
      CAI1CommandItemAttachmentRemove
      CAI1CommandItemCreate
      CAI1CommandItemGet
      CAI1CommandItemLocationsList
      CAI1CommandItemMetadataPut
      CAI1CommandItemMetadataRemove
      CAI1CommandItemReposit
      CAI1CommandItemSearchBegin
      CAI1CommandItemSearchNext
      CAI1CommandItemSearchPrevious
      CAI1CommandItemSetName
      CAI1CommandItemTypesAssign
      CAI1CommandItemTypesRevoke
      CAI1CommandItemsRemove
      CAI1CommandLocationAttachmentAdd
      CAI1CommandLocationAttachmentRemove
      CAI1CommandLocationGet
      CAI1CommandLocationList
      CAI1CommandLocationMetadataPut
      CAI1CommandLocationMetadataRemove
      CAI1CommandLocationPut
      CAI1CommandLocationTypesAssign
      CAI1CommandLocationTypesRevoke
      CAI1CommandLogin
      CAI1CommandRolesAssign
      CAI1CommandRolesGet
      CAI1CommandRolesRevoke
      CAI1CommandTypeDeclarationGet
      CAI1CommandTypeDeclarationPut
      CAI1CommandTypeDeclarationRemove
      CAI1CommandTypeDeclarationSearchBegin
      CAI1CommandTypeDeclarationSearchNext
      CAI1CommandTypeDeclarationSearchPrevious
      CAI1CommandTypeScalarGet
      CAI1CommandTypeScalarPut
      CAI1CommandTypeScalarRemove
      CAI1CommandTypeScalarSearchBegin
      CAI1CommandTypeScalarSearchNext
      CAI1CommandTypeScalarSearchPrevious
      CAI1EventUpdated
      CAI1ResponseAuditSearch
      CAI1ResponseError
      CAI1ResponseFileGet
      CAI1ResponseFilePut
      CAI1ResponseFileRemove
      CAI1ResponseFileSearch
      CAI1ResponseItemAttachmentAdd
      CAI1ResponseItemAttachmentRemove
      CAI1ResponseItemCreate
      CAI1ResponseItemGet
      CAI1ResponseItemLocationsList
      CAI1ResponseItemMetadataPut
      CAI1ResponseItemMetadataRemove
      CAI1ResponseItemReposit
      CAI1ResponseItemSearch
      CAI1ResponseItemSetName
      CAI1ResponseItemTypesAssign
      CAI1ResponseItemTypesRevoke
      CAI1ResponseItemsRemove
      CAI1ResponseLocationAttachmentAdd
      CAI1ResponseLocationAttachmentRemove
      CAI1ResponseLocationGet
      CAI1ResponseLocationList
      CAI1ResponseLocationMetadataPut
      CAI1ResponseLocationMetadataRemove
      CAI1ResponseLocationPut
      CAI1ResponseLocationTypesAssign
      CAI1ResponseLocationTypesRevoke
      CAI1ResponseLogin
      CAI1ResponseRolesAssign
      CAI1ResponseRolesGet
      CAI1ResponseRolesRevoke
      CAI1ResponseTypeDeclarationGet
      CAI1ResponseTypeDeclarationPut
      CAI1ResponseTypeDeclarationRemove
      CAI1ResponseTypeDeclarationSearch
      CAI1ResponseTypeScalarGet
      CAI1ResponseTypeScalarPut
      CAI1ResponseTypeScalarRemove
      CAI1ResponseTypeScalarSearch
    ]
  ]
]
This section of the manual attempts to describe the security properties of the cardant server.
All command execution in the cardant server passes through a single code path that captures OpenTelemetry traces and publishes events. Administrators should write alerting rules in their metrics system of choice to watch for instances of the traces, metrics, and events described here; they almost certainly indicate malicious behaviour.
At the time of writing, events are published as part of a containing trace, and therefore the server must be configured to publish traces in order for an external monitoring system to be able to observe the events. The reason for this is that the OpenTelemetry Events API specification is not yet stable and so traces are currently the only way to publish events. It is expected that a stable version of the Events API will be released soon, and then the cardant server will switch to using that API directly (and this will require only log telemetry to be configured as opposed to full trace collection).
If an operation attempts to violate the server's built-in security policy, an exception will be raised with an cardant.errorCode attribute set to error-security-policy-denied.
An attacker with access to the APIs could send specially crafted messages designed to exhaust server resources during parsing/validation of the messages.
All APIs exposed by the idstore server are defined using the Cedarbridge protocol. Cedarbridge-based protocols have the following properties:

9.3.1.2.2. Cedarbridge Properties

  • Parsing and validation complexity is linear in the size of the parsed message. It is not possible to specify a message that will result in an exponentially complex parse.
  • Cedarbridge protocols are very strongly-typed, operate under a closed-world assumption, and are immune to any kind of reflection-based deserialization vulnerabilities. Deserialization of messages cannot under any circumstances result in the deserialization of arbitrary objects.
  • Cedarbridge protocols have a structure that is known ahead of time by both peers. Neither side gets to decide the structure of messages during communication. This is a common source of vulnerabilities in other protocols, where message types are self-describing on the wire and therefore a hostile client is able to transmit the description of a message that will cause the server to do an unbounded amount of work to parse and/or validate the message.
  • Parsing code generated by the Cedarbridge compiler does not preallocate any structures. It is not possible, therefore, for a specially-crafted message to result in the server performing a huge allocation based on an attacker-provided size value. If a message claims to provide a string that is 4294967295 octets long, the parser will attempt to consume 4294967295 octets one at a time. This requires an attacker to actually provide 4294967295 octets over the network, and the attacker will run into a hard request size limit before this completes.
io7m | single-page | multi-page | epub | Cardant User Manual 0.0.1