desired.json

Every field of the document a host converges on, from its JSON Schema.

The document a host watches at the path paths.desired_state_file names, and converges on with every change to it: which apps run here, on which volumes, reachable how. Deploy an app is the guide to it; this is the file field by field. A document the host cannot read is refused whole, and reported.json says so.

An editor completes and checks a document that names the schema:

{
  "$schema": "https://raw.githubusercontent.com/ilbertt/nibrunner/main/crates/protocol/schema/desired-state.schema.json",
  "hostId": "host-1"
}

Schema: desired-state.schema.json

HostDesiredState

What one host should be running. The daemon watches this document at the path its paths.desired_state_file names and converges on every change to it.

NameTypeDescription
hostIdHostId
volumesarray of DesiredVolume
instancesarray of DesiredInstance
checkpointsarray of DesiredCheckpoint
exportsarray of DesiredExport

HostId

an identifier

string, matching ^[A-Za-z0-9][A-Za-z0-9_-]{0,62}$

DesiredVolume

NameTypeDescription
volumeIdVolumeId
appIdAppId
sizeBytesinteger, at least 0
desiredStateDesiredPresence
initialContents optionalInitialContents | nullAbsent for a volume that starts empty.

DesiredInstance

NameTypeDescription
appIdAppId
deploymentIdDeploymentIdA running instance is replaced when this changes, and only then: a new layer or config under the same deploymentId is not picked up.
volumeIdVolumeIdOne of this document's volumes, mounted in the guest as the app's data directory.
desiredStateDesiredInstanceState
layersarray of DesiredLayer, 1 to 8 itemsThe root filesystem, bottom layer first. At least one; at most MAX_LAYERS.
configAppConfig
hostnamesarray of AppHostnameWhat the HTTP proxy routes to this app's httpPort. Empty for an app nothing outside needs to reach by name.
activation optionalActivationPolicy | nullWhat puts this instance to sleep and what tells the host it is ready. A sleepWhen other than never is refused on anything but an on-request instance.
idleTimeoutMs optionalIdleTimeoutMs | nullHow long an on-request instance stays up after its last request before it sleeps: the older spelling of activation.sleepWhen, refused beside it. 300000 when neither is named.

DesiredCheckpoint

NameTypeDescription
checkpointIdCheckpointId
volumeIdVolumeId
desiredStateDesiredPresence

DesiredExport

NameTypeDescription
exportIdExportId
appIdAppId
volumeIdVolumeId
objectKeyObjectKey
desiredStateDesiredPresence
environment optionalTenantEnvironment | null

VolumeId

an identifier

string, matching ^[A-Za-z0-9][A-Za-z0-9_-]{0,62}$

AppId

an identifier

string, matching ^[A-Za-z0-9][A-Za-z0-9_-]{0,62}$

DesiredPresence

One of "present", "absent".

InitialContents

What a volume holds before its app has written a byte: an archive in the store — a tar, gzipped or not, or a zip — unpacked under destinationPath in the app's root as the volume is formatted. That happens once, so this is read once: a volume already formatted is its app's, and a change here does nothing to it.

NameTypeDescription
digestSha256Digest
objectKeyObjectKeyWhere the object lives in the store.
destinationPathGuestPathThe directory the archive's entries land under, as the app sees it. Made if no layer holds it, and given — with everything unpacked into it — to the uid the program runs as.

DeploymentId

an identifier

string, matching ^[A-Za-z0-9][A-Za-z0-9_-]{0,62}$

DesiredInstanceState

The whole of an instance's activation policy.

ValueDescription
"running"Keeps the microVM up.
"on-request"Brings the microVM up for the first deploy and for every request that finds it asleep, and lets it sleep again once it has been quiet for idleTimeoutMs.
"stopped"Takes the microVM down and leaves the app reachable enough to say so.

DesiredLayer

One read-only layer of the root filesystem an instance boots into. Layers stack in the order the document lists them, first at the bottom, and the app's volume is stacked writable over all of them. The kind says what the object is, and so what the host does with it.

One of the following, told apart by kind:

filesystem

A squashfs or ext4 image, attached as it was uploaded.

NameTypeDescription
kind"filesystem"
digestSha256Digest
objectKeyObjectKeyWhere the object lives in the store.

executable

One program, packed into an image at destinationPath and run the way this host has always run one: by the guest's own init, with the app's arguments and environment.

NameTypeDescription
kind"executable"
digestSha256Digest
objectKeyObjectKeyWhere the object lives in the store.
destinationPathExecutablePath

AppConfig

NameTypeDescription
httpPortHttpPortThe guest port the HTTP proxy sends this app's hostnames to, and the one the health check probes.
commandCommand
resourcesInstanceResources
healthCheckHealthCheck
restartPolicyRestartPolicy
ports optionalarray of InstancePortWhat this app answers on besides httpPort, if anything.

AppHostname

NameTypeDescription
hostnameHostname
kindAppHostnameKind

ActivationPolicy

NameTypeDescription
sleepWhenSleepPolicy

IdleTimeoutMs

integer, 60000 to 86400000

CheckpointId

an identifier

string, matching ^[A-Za-z0-9][A-Za-z0-9_-]{0,62}$

ExportId

an identifier

string, matching ^[A-Za-z0-9][A-Za-z0-9_-]{0,62}$

ObjectKey

between 1 and 1024 characters

string, 1 to 1024 characters

TenantEnvironment

map of TenantValue, keys matching ^[A-Za-z_][A-Za-z0-9_]*$

Sha256Digest

a lowercase hex sha-256

string, matching ^[0-9a-f]{64}$

GuestPath

a guest path

string, at most 4096 characters, matching ^/$|^(/(?!\.\.?(/|$))[^/\\"'\x00-\x1f]+)+$

ExecutablePath

an absolute path to a file other than /sbin/init

string, at most 4096 characters, matching ^/$|^(/(?!\.\.?(/|$))[^/\\"'\x00-\x1f]+)+$

HttpPort

integer, 1 to 65535

Command

What the guest runs once its root is stacked, as uid 65534: program with args, in workingDirectory, with environment. The working directory is made if it is not there, given to that uid, and is where the program's persistent state lives, because everything under the root persists on the volume and nowhere else is the program's to write.

NameTypeDescription
programGuestPath
argsTenantArguments
workingDirectoryGuestPath
environmentTenantEnvironment

InstanceResources

NameTypeDescription
vcpuCountinteger, at least 0
memoryMibinteger, at least 0

HealthCheck

What tells this host an instance is well — and so what a wake waits for before it hands the caller on, and what the instance's liveness is read from after that. Named in every document: a default would be a kind nobody chose, and the one that looks obvious lies. A TCP connect is answered by the guest kernel's accept queue whether or not the process behind it will ever read the request, so a program that has stopped answering passes it for as long as it lives.

One of the following, told apart by kind:

http

path, requested on httpPort, answers 2xx.

NameTypeDescription
kind"http"
pathstring
intervalMsinteger, at least 0
timeoutMsinteger, at least 0
gracePeriodMsinteger, at least 0
healthyThresholdinteger, at least 0
unhealthyThresholdinteger, at least 0

tcp

A connection to httpPort is accepted. Only that — for a port that does not speak HTTP.

NameTypeDescription
kind"tcp"
intervalMsinteger, at least 0
timeoutMsinteger, at least 0
gracePeriodMsinteger, at least 0
healthyThresholdinteger, at least 0
unhealthyThresholdinteger, at least 0

boot-completed

The microVM is up and httpPort has accepted a connection once: the tenant is listening. That is all it is asked, and only until it answers — a guest this host did not build answers no path it was not told about — so its liveness after that is the microVM being up.

NameTypeDescription
kind"boot-completed"

RestartPolicy

NameTypeDescription
maxRestartsinteger, at least 0
initialBackoffMsinteger, at least 0
maxBackoffMsinteger, at least 0
backoffFactornumber
resetAfterMsinteger, at least 0

InstancePort

A port a guest answers on beside its HTTP one, carried to it unread.

It carries whatever arrives, tcp or udp: a port is a port, the relay in front of a host forwards both for every one in its range, and a guest that listens on only one of them answers the other with a port-unreachable the way any host would.

NameTypeDescription
namePortName
guestPortGuestPort

Hostname

a hostname

string, at most 253 characters, matching ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+$

AppHostnameKind

One of "platform", "custom".

SleepPolicy

What puts a running microVM back to sleep. Only an on-request instance may carry one that fires: nothing else on this host would wake it again, and the next reconcile pass would bring it straight back up.

One of the following, told apart by kind:

never

NameTypeDescription
kind"never"

traffic-idle

NameTypeDescription
kind"traffic-idle"
timeoutMsIdleTimeoutMs

max-lifetime

NameTypeDescription
kind"max-lifetime"
ttlMsMaxLifetimeMs

TenantValue

Handed to the app as is, except that ${NAME} and $NAME are filled in for NAME in NIBRUN_HOSTNAME, NIBRUN_HTTP_PORT; any other $NIBRUN_ reference is refused.

string, at most 32768 characters

TenantArguments

The arguments the binary is started with.

array of string, at most 4096 characters, at most 64 items

PortName

lowercase letters, digits and dashes, starting with a letter

string, matching ^[a-z][a-z0-9-]{0,15}$

GuestPort

integer, 1 to 65535

MaxLifetimeMs

integer, 60000 to 604800000

On this page