Module Mirage

Release v4.1.1

What is MirageOS?

MirageOS is a library operating system that can build standalone unikernels on various platforms. More precisely, the architecture can be divided into:

It is possible to write high-level MirageOS applications, such as HTTPS, email or CalDAV servers which can be deployed on very heterogenous and embedded platforms by changing only a few compilation parameters. The supported platforms range from minimal virtual machines running on cloud providers, or processes running inside Docker containers configured with a tight security profile. In general, these platform do not have a full POSIX environment; MirageOS does not try to emulate POSIX and focuses on providing a small, well-defined, typed interface with the system components. The nearest equivalent to the MirageOS approach is the WASI (wasi.dev) set of interfaces for WebAssembly.

Is everything really written in OCaml?

While most of the code is written in OCaml, a typed, high-level language with many good safety properties, there are pieces of MirageOS which are still written in C. These bits can be separated in three categories:

MirageOS as a cross-compilator

The MirageOS compiler is basically a cross-compiler, where the host and target toolchain are identical, but with different flags for the C bindings: for instance, it is necessary to pass -freestanding to all C bindings to not use POSIX headers. The MirageOS compiler also uses a custom linker: eg. not only it needs a custom OCaml's runtime libasmrun.a, but it also needs to run a different linker to generate specialised executable images.

Historically, the OCaml ecosystem always had partial support for cross-compilation: for instance, the ocaml-cross way of doing it is to duplicate all existing opam pacakges by adding a -windows suffix to their names and dependencies; this allows normal packages and windows packages can be co-installed in the same opam switch.

MirageOS 3.x

MirageOS 3.x solves this by duplicating only the packages defining C bindings. It relies on every MirageOS backend registering a set of CFLAGS with pkg-config. Then every bindings uses pkg-config to configure their CFLAGS and ocamlfind to register link-time predicates, e.g. additional link time options like the name of the C archives. Finally, the final link step is done by querying ocamlfind (using the custom registered predicates) to link the list of dependencies' objects files with the result of OCam compiler's --output-obj option.

MirageOS 4.x

MirageOS 4 solves this by relying on dune's built-in support for cross-compilation. This is done by gathering all the sources of the dependencies locally with opam-monorepo, and by creating a `dune-workspace` file describing the C flags to use in each cross-compilation "context". Once this is set-up, only one dune build can cross-compile the unikernel target with all its local sources.

MirageOS eDSL

The rest of the document describes Functoria, the embedded domain-specific language to be used in config.ml files, to described how the typed libraries have to be assembled.

Combinators

type 'a typ = 'a Functoria.Type.t

The type for values representing module types.

val typ : 'a -> 'a typ

type t is a value representing the module type t.

val (@->) : 'a typ -> 'b typ -> ( 'a -> 'b ) typ

Construct a functor type from a type and an existing functor type. This corresponds to prepending a parameter to the list of functor parameters. For example:

kv_ro @-> ip @-> kv_ro

This describes a functor type that accepts two arguments -- a kv_ro and an ip device -- and returns a kv_ro.

type 'a impl = 'a Functoria.Impl.t

The type for values representing module implementations.

val ($) : ( 'a -> 'b ) impl -> 'a impl -> 'b impl

m $ a applies the functor m to the module a.

type abstract_impl = Functoria.Impl.abstract

Same as impl but with hidden type.

val dep : 'a impl -> abstract_impl

dep t is the (build-time) dependency towards t.

Keys

type 'a key = 'a Functoria.Key.key

The type for command-line parameters.

type abstract_key = Functoria.Key.t

The type for abstract keys.

type context = Functoria.Key.context

The type for keys' parsing context. See Key.context.

type 'a value = 'a Functoria.Key.value

The type for values parsed from the command-line. See Key.value.

val key : 'a key -> Functoria.Key.t

key k is an untyped representation of k.

val if_impl : bool value -> 'a impl -> 'a impl -> 'a impl

if_impl v impl1 impl2 is impl1 if v is resolved to true and impl2 otherwise.

val match_impl : 'b value -> default:'a impl -> ('b * 'a impl) list -> 'a impl

match_impl v cases ~default chooses the implementation amongst cases by matching the v's value. default is chosen if no value matches.

Package dependencies

For specifying opam package dependencies, the type package is used. It consists of the opam package name, the ocamlfind names, and optional lower and upper bounds. The version constraints are merged with other modules.

type package = Functoria.Package.t

The type for opam packages.

Installation scope of a package.

val package : ?scope:scope -> ?build:bool -> ?sublibs:string list -> ?libs:string list -> ?min:string -> ?max:string -> ?pin:string -> ?pin_version:string -> string -> package

Application Builder

Values of type impl are tied to concrete module implementation with the device and foreign construct. Module implementations of type job can then be registered into an application builder. The builder is in charge if parsing the command-line arguments and of generating code for the final application. See Functoria.Lib for details.

type info = Functoria.Info.t

The type for build information.

val foreign : ?packages:package list -> ?packages_v:package list value -> ?keys:abstract_key list -> ?deps:abstract_impl list -> string -> 'a typ -> 'a impl

Alias for main, where ?extra_deps has been renamed to ?deps.

val main : ?packages:package list -> ?packages_v:package list value -> ?keys:abstract_key list -> ?extra_deps:abstract_impl list -> string -> 'a typ -> 'a impl

foreign name typ is the functor name, having the module type typ. The connect code will call <name>.start.

  • If packages or packages_v is set, then the given packages are installed before compiling the current application.
  • If keys is set, use the given keys to parse at configure and runtime the command-line arguments before calling <name>.connect.
  • If extra_deps is set, the given list of abstract implementations is added as data-dependencies: they will be initialized before calling <name>.connect.

Devices

type 'a device = ( 'a, abstract_impl ) Functoria.Device.t
val of_device : 'a device -> 'a impl

of_device t is the implementation device t.

val impl : ?packages:package list -> ?packages_v:package list Functoria.Key.value -> ?install:( Functoria.Info.t -> Functoria.Install.t ) -> ?install_v:( Functoria.Info.t -> Functoria.Install.t Functoria.Key.value ) -> ?keys:Functoria.Key.t list -> ?extra_deps:abstract_impl list -> ?connect:( info -> string -> string list -> string ) -> ?dune:( info -> Functoria.Dune.stanza list ) -> ?configure:( info -> unit Functoria.Action.t ) -> ?files:( info -> Fpath.t list ) -> string -> 'a typ -> 'a impl

impl ... is of_device @@ Device.v ...

Jobs

type job
module Key : module type of struct include Mirage_key end

Configuration keys.

val abstract : 'a impl -> abstract_impl

General mirage devices

type tracing

The type for tracing.

val tracing : tracing typ

Implementation of the tracing type.

val mprof_trace : size:int -> unit -> tracing impl

Use mirage-profile to trace the unikernel. On Unix, this creates and mmaps a file called "trace.ctf". On Xen, it shares the trace buffer with dom0.

  • parameter size:

    size of the ring buffer to use.

type qubesdb
val qubesdb : qubesdb typ

For the Qubes target, the Qubes database from which to look up * dynamic runtime configuration information.

val default_qubesdb : qubesdb impl

A default qubes database, guessed from the usual valid configurations.

Time

type time

Abstract type for timers.

val time : time typ

Implementations of the Mirage_types.TIME signature.

val default_time : time impl

The default timer implementation.

Clocks

type pclock

Abstract type for POSIX clocks.

val pclock : pclock typ

Implementations of the Mirage_clock.PCLOCK signature.

val default_posix_clock : pclock impl

The default mirage-clock PCLOCK implementation.

type mclock

Abstract type for monotonic clocks

val mclock : mclock typ

Implementations of the Mirage_clock.MCLOCK signature.

val default_monotonic_clock : mclock impl

The default mirage-clock MCLOCK implementation.

Log reporters

type reporter

The type for log reporters.

val reporter : reporter typ

Implementation of the log reporter type.

val default_reporter : ?clock:pclock impl -> ?ring_size:int -> ?level:Logs.level -> unit -> reporter impl

default_reporter ?clock ?level () is the log reporter that prints log messages to the console, timestampted with clock. If not provided, the default clock is default_posix_clock. level is the default log threshold. It is Logs.Info if not specified.

val no_reporter : reporter impl

no_reporter disable log reporting.

Random

type random

Abstract type for random sources.

val random : random typ

Implementations of the Mirage_types.RANDOM signature.

val stdlib_random : random impl

Passthrough to the OCaml Random generator.

val nocrypto_random : random impl

Passthrough to the Fortuna PRNG implemented in nocrypto.

val default_random : random impl

Default PRNG device to be used in unikernels. It uses getrandom/getentropy on Unix, and a Fortuna PRNG on other targets.

val rng : ?time:time impl -> ?mclock:mclock impl -> unit -> random impl

rng is the device Mirage_crypto_rng.Make.

Consoles

type console

Abstract type for consoles.

val console : console typ

Implementations of the Mirage_types.CONSOLE signature.

val default_console : console impl

Default console implementation.

val custom_console : string -> console impl

Custom console implementation.

Block devices

type block

Abstract type for raw block device configurations.

val block : block typ

Implementations of the Mirage_types.BLOCK signature.

val block_of_file : string -> block impl

Use the given file as a raw block device.

val block_of_xenstore_id : string -> block impl

Use the given XenStore ID (ex: /dev/xvdi1 or 51760) as a raw block device.

val ramdisk : string -> block impl

Use a ramdisk with the given name.

val generic_block : ?group:string -> ?key:[ `XenstoreId | `BlockFile | `Ramdisk ] value -> string -> block impl

Static key/value stores

type kv_ro

Abstract type for read-only key/value store.

val kv_ro : kv_ro typ

Implementations of the Mirage_types.KV_RO signature.

val crunch : string -> kv_ro impl

Crunch a directory.

val archive : block impl -> kv_ro impl
val archive_of_files : ?dir:string -> unit -> kv_ro impl
val direct_kv_ro : string -> kv_ro impl

Direct access to the underlying filesystem as a key/value store. For Xen backends, this is equivalent to crunch.

val generic_kv_ro : ?group:string -> ?key:[ `Archive | `Crunch | `Direct | `Fat ] value -> string -> kv_ro impl

Generic key/value that will choose dynamically between fat, archive and crunch. To use a filesystem implementation, try kv_ro_of_fs.

If no key is provided, it uses Key.kv_ro to create a new one.

val docteur : ?mode:[ `Fast | `Light ] -> ?disk:string Key.key -> ?analyze:bool Key.key -> ?branch:string -> ?extra_deps:string list -> string -> kv_ro impl

docteur ?mode ?disk ?analyze remote is a read-only, key-value store device. Data is stored on that device using the Git PACK file format, version 2. This format has very good compression factors for many similar files of relatively small size. For instance, 14Gb of HTML files can be compressed into a disk image of 240Mb.

Unlike crunch, docteur produces an external image which means that less memory is used to keep and get files. The image can be produced from many sources:

  • A local Git repository (like file://path/to/the/git/repository/)
  • A simple directory (like file://path/to/a/simple/directory/)
  • A remote Git repository (via SSH, HTTP(S) or TCP/IP as what git clone expects)

If you use a Git repository, you can choose a specific branch with the ?branch argument (like refs/heads/main). Otherwise, this argument is ignored.

If you use a simple directory, it can be a relative from your unikernel project (relativize://directory) or an absolute path (file://home/user/directory).

If a required file is produced by a dune rule, you must notice it via the extra_deps argument.

For a Solo5 target, users must attach the image as a block device:

$ solo5-hvt --block:<name>=<path-to-the-image> -- unikernel.{hvt,...}

For the Unix target, the program open the image at the beginning of the process. An integrity check of the image can be done via the analyze value (defaults to true).

It's possible to use the file-system into 2 modes:

  • `Light: any access requires that we reconstruct the path to the requested file. That means that we will need to extract a few additional objects before the extraction of the requested one. `Light does not cache anything in memory but it can be slower if the requested file is deep in the directory structure.
  • `Fast: reconstructs and cache the layout of the directory structure when the unikernel starts: it might increase boot-time and bigger memory requirements. However, `Fast allows the device to decode only the requested object so it is faster than the `Light mode.
type kv_rw

Abstract type for read-write key/value store.

val kv_rw : kv_rw typ

Implementations of the Mirage_types.KV_RW signature.

val direct_kv_rw : string -> kv_rw impl

Direct access to the underlying filesystem as a key/value store. Only available on Unix backends.

val kv_rw_mem : ?clock:pclock impl -> unit -> kv_rw impl

An in-memory key-value store using mirage-kv-mem.

val chamelon : program_block_size:int key -> ?pclock:pclock impl -> block impl -> kv_rw impl

chamelon ~program_block_size returns a kv_rw filesystem which is an implementation of littlefs in OCaml. The chamelon device expects a block-device:

let program_block_size =
  let doc = Key.Arg.info [ "program-block-size" ] in
  Key.(create "program_block_size" Arg.(opt int 16 doc))

let block = block_of_file "db"
let fs = chamelon ~program_block_size block

For Solo5 targets, you finally can launch the unikernel with:

$ solo5-hvt --block:db=db.img unikernel.hvt

The block-device must be well-formed and formatted by the chamelon tool:

$ dd if=/dev/zero if=db.img bs=1M count=1
$ chamelon format 512 db.img

Filesystem

type fs

Abstract type for filesystems.

val fs : fs typ

Implementations of the Mirage_types.FS signature.

val fat : block impl -> fs impl

Consider a raw block device as a FAT filesystem.

val fat_of_files : ?dir:string -> ?regexp:string -> unit -> fs impl

fat_files dir ?dir ?regexp () collects all the files matching the shell pattern regexp in the directory dir into a FAT image. By default, dir is the current working directory and regexp is *

val kv_ro_of_fs : fs impl -> kv_ro impl

Consider a filesystem implementation as a read-only key/value store.

Network interfaces

type network

Abstract type for network configurations.

val network : network typ

Implementations of the Mirage_types.NETWORK signature.

val default_network : network impl

default_network is a dynamic network implementation * which attempts to do something reasonable based on the target.

val netif : ?group:string -> string -> network impl

A custom network interface. Exposes a Key.interface key.

Ethernet configuration

type ethernet
val ethernet : ethernet typ

Implementations of the Mirage_types.ETHERNET signature.

val etif : network impl -> ethernet impl

ARP configuration

type arpv4
val arpv4 : arpv4 typ

Implementation of the Mirage_types.ARPV4 signature.

val arp : ?time:time impl -> ethernet impl -> arpv4 impl

ARP implementation provided by the arp library

IP configuration

Implementations of the Mirage_types.IP signature.

type v4
type v6
type v4v6
type 'a ip

Abstract type for IP configurations.

type ipv4 = v4 ip
type ipv6 = v6 ip
type ipv4v6 = v4v6 ip
val ipv4 : ipv4 typ

The Mirage_types.IPV4 module signature.

val ipv6 : ipv6 typ

The Mirage_types.IPV6 module signature.

val ipv4v6 : ipv4v6 typ

The Mirage_types.IP module signature with ipaddr = Ipaddr.t.

type ipv4_config = {
network : Ipaddr.V4.Prefix.t;
gateway : Ipaddr.V4.t option;
}

Types for manual IPv4 configuration.

type ipv6_config = {
network : Ipaddr.V6.Prefix.t;
gateway : Ipaddr.V6.t option;
}

Types for manual IPv6 configuration.

val create_ipv4 : ?group:string -> ?config:ipv4_config -> ?no_init:bool Key.key -> ?random:random impl -> ?clock:mclock impl -> ethernet impl -> arpv4 impl -> ipv4 impl

Use an IPv4 address Exposes the keys Key.V4.network and Key.V4.gateway. If provided, the values of these keys will override those supplied in the ipv4 configuration record, if that has been provided.

val ipv4_qubes : ?random:random impl -> ?clock:mclock impl -> qubesdb impl -> ethernet impl -> arpv4 impl -> ipv4 impl

Use a given initialized QubesDB to look up and configure the appropriate * IPv4 interface.

val create_ipv6 : ?random:random impl -> ?time:time impl -> ?clock:mclock impl -> ?group:string -> ?config:ipv6_config -> ?no_init:bool Key.key -> network impl -> ethernet impl -> ipv6 impl

Use an IPv6 address. Exposes the keys Key.V6.network, Key.V6.gateway.

val create_ipv4v6 : ?group:string -> ipv4 impl -> ipv6 impl -> ipv4v6 impl

UDP configuration

type 'a udp
type udpv4 = v4 udp
type udpv6 = v6 udp
type udpv4v6 = v4v6 udp
val udp : 'a udp typ

Implementation of the Mirage_types.UDP signature.

val udpv4 : udpv4 typ
val udpv6 : udpv6 typ
val udpv4v6 : udpv4v6 typ
val direct_udp : ?random:random impl -> 'a ip impl -> 'a udp impl
val socket_udpv4 : ?group:string -> Ipaddr.V4.t option -> udpv4 impl
val socket_udpv6 : ?group:string -> Ipaddr.V6.t option -> udpv6 impl
val socket_udpv4v6 : ?group:string -> Ipaddr.V4.t option -> Ipaddr.V6.t option -> udpv4v6 impl

TCP configuration

type 'a tcp
type tcpv4 = v4 tcp
type tcpv6 = v6 tcp
type tcpv4v6 = v4v6 tcp
val tcp : 'a tcp typ

Implementation of the Mirage_types.TCP signature.

val tcpv4 : tcpv4 typ
val tcpv6 : tcpv6 typ
val tcpv4v6 : tcpv4v6 typ
val direct_tcp : ?mclock:mclock impl -> ?time:time impl -> ?random:random impl -> 'a ip impl -> 'a tcp impl
val socket_tcpv4 : ?group:string -> Ipaddr.V4.t option -> tcpv4 impl
val socket_tcpv6 : ?group:string -> Ipaddr.V6.t option -> tcpv6 impl
val socket_tcpv4v6 : ?group:string -> Ipaddr.V4.t option -> Ipaddr.V6.t option -> tcpv4v6 impl

Network stack configuration

type stackv4

IPv4

val stackv4 : stackv4 typ

Implementation of the Mirage_types.STACKV4 signature.

val direct_stackv4 : ?mclock:mclock impl -> ?time:time impl -> ?random:random impl -> network impl -> ethernet impl -> arpv4 impl -> ipv4 impl -> stackv4 impl

Direct network stack with given ip.

val socket_stackv4 : ?group:string -> unit -> stackv4 impl

Network stack with sockets.

val qubes_ipv4_stack : ?qubesdb:qubesdb impl -> ?arp:( ethernet impl -> arpv4 impl ) -> network impl -> stackv4 impl

Build a stackv4 by looking up configuration information via QubesDB, * building an ipv4, then building a stack on top of that.

val dhcp_ipv4_stack : ?random:random impl -> ?clock:mclock impl -> ?time:time impl -> ?arp:( ethernet impl -> arpv4 impl ) -> network impl -> stackv4 impl

Build a stackv4 by obtaining a DHCP lease, using the lease to * build an ipv4, then building a stack on top of that.

val static_ipv4_stack : ?group:string -> ?config:ipv4_config -> ?arp:( ethernet impl -> arpv4 impl ) -> network impl -> stackv4 impl

Build a stackv4 by checking the Key.V4.network, and Key.V4.gateway keys * for ipv4 configuration information, filling in unspecified information from ?config, * then building a stack on top of that.

val generic_stackv4 : ?group:string -> ?config:ipv4_config -> ?dhcp_key:bool value -> ?net_key:[ `Direct | `Socket ] option value -> network impl -> stackv4 impl

Generic stack using a dhcp and a net keys: Key.net and Key.dhcp.

If a key is not provided, it uses Key.net or Key.dhcp (with the group argument) to create it.

IPv6

type stackv6
val stackv6 : stackv6 typ

Implementation of the Mirage_stack.V6 signature.

val direct_stackv6 : ?mclock:mclock impl -> ?random:random impl -> ?time:time impl -> network impl -> ethernet impl -> ipv6 impl -> stackv6 impl

Direct network stack with given ip.

val socket_stackv6 : ?group:string -> unit -> stackv6 impl

Network stack with sockets.

val static_ipv6_stack : ?group:string -> ?config:ipv6_config -> network impl -> stackv6 impl

Build a stackv6 by checking the Key.V6.network, and Key.V6.gateway keys for ipv6 configuration information, filling in unspecified information from ?config, then building a stack on top of that.

val generic_stackv6 : ?group:string -> ?config:ipv6_config -> ?net_key:[ `Direct | `Socket ] option value -> network impl -> stackv6 impl

Generic stack using a net keys: Key.net.

If a key is not provided, it uses Key.net (with the group argument) to create it.

Dual IPv4 and IPv6

type stackv4v6
val stackv4v6 : stackv4v6 typ

Implementation of the Mirage_stack.V4V6 signature.

val direct_stackv4v6 : ?mclock:mclock impl -> ?random:random impl -> ?time:time impl -> ipv4_only:bool Key.key -> ipv6_only:bool Key.key -> network impl -> ethernet impl -> arpv4 impl -> ipv4 impl -> ipv6 impl -> stackv4v6 impl

Direct network stack with given ip.

val socket_stackv4v6 : ?group:string -> unit -> stackv4v6 impl

Network stack with sockets.

val static_ipv4v6_stack : ?group:string -> ?ipv6_config:ipv6_config -> ?ipv4_config:ipv4_config -> ?arp:( ethernet impl -> arpv4 impl ) -> network impl -> stackv4v6 impl

Build a stackv4v6 by checking the Key.V6.network, and Key.V6.gateway keys for IPv4 and IPv6 configuration information, filling in unspecified information from ?config, then building a stack on top of that.

val generic_stackv4v6 : ?group:string -> ?ipv6_config:ipv6_config -> ?ipv4_config:ipv4_config -> ?dhcp_key:bool value -> ?net_key:[ `Direct | `Socket ] option value -> network impl -> stackv4v6 impl

Generic stack using a net keys: Key.net.

If a key is not provided, it uses Key.net (with the group argument) to create it.

val tcpv4v6_of_stackv4v6 : stackv4v6 impl -> tcpv4v6 impl

tcpv4v6 stackv4v6 is an helper to extract the TCP/IP stack regardless the UDP/IP stack expected by some devices such as protocols.

Resolver configuration

type resolver
val resolver : resolver typ
val resolver_dns : ?ns:Ipaddr.t -> ?ns_port:int -> ?time:time impl -> ?mclock:mclock impl -> ?pclock:pclock impl -> ?random:random impl -> stackv4v6 impl -> resolver impl
val resolver_unix_system : resolver impl

DNS client

A DNS client is a module which implements:

type dns_client
val dns_client : dns_client typ
val generic_dns_client : ?timeout:int64 option key -> ?nameservers:string list key -> ?random:random impl -> ?time:time impl -> ?mclock:mclock impl -> ?pclock:pclock impl -> stackv4v6 impl -> dns_client impl

generic_dns_client stackv4v6 creates a new DNS value which is able to resolve domain-name from nameservers. It requires a network stack to communicate with these nameservers.

Happy-eyeballs

Happy-eyeballs is an implementation of RFC 8305 which specifies how to connect to a remote host using either IP protocol version 4 or IP protocol version 6 from a stackv4v6 network implementation.

The given device is able to resolve a remote host via a dns_client device and both must share the same stackv4v6 implementation.

type happy_eyeballs
val happy_eyeballs : happy_eyeballs typ
val generic_happy_eyeballs : ?aaaa_timeout:int64 option key -> ?v6_connect_timeout:int64 option key -> ?connect_timeout:int64 option key -> ?resolve_timeout:int64 option key -> ?resolve_retries:int64 option key -> ?timer_interval:int64 option key -> ?time:time impl -> ?mclock:mclock impl -> stackv4v6 impl -> dns_client impl -> happy_eyeballs impl

generic_happy_eyeballs stackv4v6 dns_client creates a new happy-eyeballs value which is able to resolve and connect to a remote host and allocate finally a connected flow from the given network implementation stackv4v6.

This device has several optional arguments of keys for timeouts specified in nanoseconds.

Syslog configuration

Syslog exfiltrates log messages (generated by libraries using the logs library) via a network connection. The log level of the log sources is controlled via the Mirage_key.logs key. The functionality is provided by the logs-syslog package.

type syslog_config = {
hostname : string;
server : Ipaddr.t option;
port : int option;
truncate : int option;
}
val syslog_config : ?port:int -> ?truncate:int -> ?server:Ipaddr.t -> string -> syslog_config

Helper for constructing a syslog_config.

type syslog

The type for syslog

val syslog : syslog typ

Implementation of the syslog type.

val syslog_udp : ?config:syslog_config -> ?console:console impl -> ?clock:pclock impl -> stackv4v6 impl -> syslog impl

Emit log messages via UDP to the configured host.

val syslog_tcp : ?config:syslog_config -> ?console:console impl -> ?clock:pclock impl -> stackv4v6 impl -> syslog impl

Emit log messages via TCP to the configured host.

val syslog_tls : ?config:syslog_config -> ?keyname:string -> ?console:console impl -> ?clock:pclock impl -> stackv4v6 impl -> kv_ro impl -> syslog impl

Emit log messages via TLS to the configured host, using the credentials (private key, certificate, trust anchor) provided in the KV_RO using the keyname.

Entropy

val nocrypto : job impl

Device that initializes the entropy.

Conduit configuration

type conduit
val conduit : conduit typ
val conduit_direct : ?tls:bool -> ?random:random impl -> stackv4v6 impl -> conduit impl

HTTP configuration

type http
val http : http typ
val http_server : conduit impl -> http impl
val cohttp_server : conduit impl -> http impl

cohttp_server starts a Cohttp server.

val httpaf_server : conduit impl -> http impl

httpaf_server starts a http/af server.

type http_client
val http_client : http_client typ
val cohttp_client : ?pclock:pclock impl -> resolver impl -> conduit impl -> http_client impl

cohttp_server starts a Cohttp server.

Argv configuration

type argv = Functoria.argv
val argv : argv typ
val default_argv : argv impl

default_argv is a dynamic argv implementation * which attempts to do something reasonable based on the target.

val no_argv : argv impl

no_argv Disable command line parsing and set argv to |""|.

Git client configuration

Users can connect to a remote Git repository in many ways:

The devices defined below define these in composable ways. The git_client impl returned from them can be passed to Git or Irmin in order to be able to fetch and push from/into a Git repository.

The user is able to restrict or enlarge protocol possibilities needed for its application. For instance, the user is able to restrict only the SSH connection to communicate with a Git repository or the user can handle TCP/IP and SSH as possible protocols to communicate with a peer.

For instance, a device which is able to communicate via TCP/IP and SSH can be implemented like:

let git_client =
  let dns = happy_eyeballs stackv4v6 in
  let ssh = git_ssh ~key (tcpv4v6_of_stackv4v6 stackv4v6) dns in
  let tcp = git_tcp (tcpv4v6_of_stackv4v6 stackv4v6) dns in
  merge_git_clients ssh tcp
type git_client

The type for devices that implement the Git protocol.

val git_client : git_client typ
val merge_git_clients : git_client impl -> git_client impl -> git_client impl

merge_git_clients a b is a device that can connect to remote Git repositories using either the device a or the device b.

val git_happy_eyeballs : stackv4v6 impl -> dns_client impl -> happy_eyeballs impl -> git_client impl

git_tcp tcpv4v6 dns is a device able to connect to a remote Git repository using TCP/IP.

val git_ssh : ?authenticator:string option key -> key:string option key -> ?mclock:mclock impl -> ?time:time impl -> tcpv4v6 impl -> git_client impl -> git_client impl

git_ssh ?authenticator ~key tcpv4v6 dns is a device able to connect to a remote Git repository using an SSH connection with the given private key. The identity of the remote Git repository can be verified using authenticator.

The format of the private key is: <type>:<seed or b64 encoded>. <type> can be rsa or ed25519 and, if the type is RSA, we expect the seed of the private key. Otherwise (if the type is Ed25519), we expect the b64-encoded private key.

The format of the authenticator is SHA256:<b64-encoded-public-key>, the output of:

$ ssh-keygen -lf <(ssh-keyscan -t rsa|ed25519 remote 2>/dev/null)
val git_http : ?authenticator:string option key -> ?headers:(string * string) list key -> ?time:time impl -> ?pclock:pclock impl -> tcpv4v6 impl -> git_client impl -> git_client impl

git_http ?authenticator ?headers tcpv4v6 dns is a device able to connect to a remote Git repository via an HTTP(S) connection, using the provided HTTP headers. The identity of the remote Git repository can be verified using authenticator.

The format of it is:

  • none no authentication
  • key(:<hash>)?:<b64-encoded fingerprint> to authenticate via the key fingerprint
  • cert(:<hash>)?:<b64-encoded fingerprint> to authenticate via the cert fingerprint
  • trust-anchor(:<der-encoded cert>)+ to authenticate via a list of certificates - By default, we use X.509 trust anchors extracted from Mozilla's NSS

Other devices

val job : job typ

job is the combinator for representing main tasks.

val noop : job impl

noop is a job that does nothing, has no dependency and returns ()

val keys : argv impl -> job impl

keys argv is a job that loads argv.

val info : info typ

info is the combinator to generate info values to use at runtime.

val app_info : info impl

app_info exports all the information available at configure time into a runtime Mirage.Info.t value.

val app_info_with_opam_deps : (string * string) list -> info impl

app_info exports all the information available at configure time into a runtime Mirage.Info.t value.

Application registering

val register : ?argv:argv impl -> ?tracing:tracing impl -> ?reporter:reporter impl -> ?keys:Key.t list -> ?packages:Functoria.package list -> ?src:[ `Auto | `None | `Some of string ] -> string -> job impl list -> unit

register name jobs registers the application named by name which will executes the given jobs.

  • parameter packages

    The opam packages needed by this module.

  • parameter keys

    The keys related to this module.

  • parameter tracing

    Enable tracing.

  • parameter reporter

    Configure logging. The default log reporter is default_reporter. To disable logging, use no_reporter.

  • parameter argv

    Configure command-line argument parsing. The default parser is default_argv. To disable command-line parsing, use no_argv.

module Type = Functoria.Type
module Impl = Functoria.Impl
module Info = Functoria.Info
module Dune = Functoria.Dune
module Action = Functoria.Action
module FS : sig ... end
module Project : sig ... end
module Tool : sig ... end