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.
| Name | Type | Description |
|---|---|---|
hostId | HostId | |
volumes | array of DesiredVolume | |
instances | array of DesiredInstance | |
checkpoints | array of DesiredCheckpoint | |
exports | array of DesiredExport |
HostId
an identifier
string, matching ^[A-Za-z0-9][A-Za-z0-9_-]{0,62}$
DesiredVolume
| Name | Type | Description |
|---|---|---|
volumeId | VolumeId | |
appId | AppId | |
sizeBytes | integer, at least 0 | |
desiredState | DesiredPresence | |
initialContents optional | InitialContents | null | Absent for a volume that starts empty. |
DesiredInstance
| Name | Type | Description |
|---|---|---|
appId | AppId | |
deploymentId | DeploymentId | A running instance is replaced when this changes, and only then: a new layer or config under the same deploymentId is not picked up. |
volumeId | VolumeId | One of this document's volumes, mounted in the guest as the app's data directory. |
desiredState | DesiredInstanceState | |
layers | array of DesiredLayer, 1 to 8 items | The root filesystem, bottom layer first. At least one; at most MAX_LAYERS. |
config | AppConfig | |
hostnames | array of AppHostname | What the HTTP proxy routes to this app's httpPort. Empty for an app nothing outside needs to reach by name. |
activation optional | ActivationPolicy | null | What 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 optional | IdleTimeoutMs | null | How 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
| Name | Type | Description |
|---|---|---|
checkpointId | CheckpointId | |
volumeId | VolumeId | |
desiredState | DesiredPresence |
DesiredExport
| Name | Type | Description |
|---|---|---|
exportId | ExportId | |
appId | AppId | |
volumeId | VolumeId | |
objectKey | ObjectKey | |
desiredState | DesiredPresence | |
environment optional | TenantEnvironment | 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.
| Name | Type | Description |
|---|---|---|
digest | Sha256Digest | |
objectKey | ObjectKey | Where the object lives in the store. |
destinationPath | GuestPath | The 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.
| Value | Description |
|---|---|
"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.
| Name | Type | Description |
|---|---|---|
kind | "filesystem" | |
digest | Sha256Digest | |
objectKey | ObjectKey | Where 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.
| Name | Type | Description |
|---|---|---|
kind | "executable" | |
digest | Sha256Digest | |
objectKey | ObjectKey | Where the object lives in the store. |
destinationPath | ExecutablePath |
AppConfig
| Name | Type | Description |
|---|---|---|
httpPort | HttpPort | The guest port the HTTP proxy sends this app's hostnames to, and the one the health check probes. |
command | Command | |
resources | InstanceResources | |
healthCheck | HealthCheck | |
restartPolicy | RestartPolicy | |
ports optional | array of InstancePort | What this app answers on besides httpPort, if anything. |
AppHostname
| Name | Type | Description |
|---|---|---|
hostname | Hostname | |
kind | AppHostnameKind |
ActivationPolicy
| Name | Type | Description |
|---|---|---|
sleepWhen | SleepPolicy |
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.
| Name | Type | Description |
|---|---|---|
program | GuestPath | |
args | TenantArguments | |
workingDirectory | GuestPath | |
environment | TenantEnvironment |
InstanceResources
| Name | Type | Description |
|---|---|---|
vcpuCount | integer, at least 0 | |
memoryMib | integer, 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.
| Name | Type | Description |
|---|---|---|
kind | "http" | |
path | string | |
intervalMs | integer, at least 0 | |
timeoutMs | integer, at least 0 | |
gracePeriodMs | integer, at least 0 | |
healthyThreshold | integer, at least 0 | |
unhealthyThreshold | integer, at least 0 |
tcp
A connection to httpPort is accepted. Only that — for a port that does not speak HTTP.
| Name | Type | Description |
|---|---|---|
kind | "tcp" | |
intervalMs | integer, at least 0 | |
timeoutMs | integer, at least 0 | |
gracePeriodMs | integer, at least 0 | |
healthyThreshold | integer, at least 0 | |
unhealthyThreshold | integer, 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.
| Name | Type | Description |
|---|---|---|
kind | "boot-completed" |
RestartPolicy
| Name | Type | Description |
|---|---|---|
maxRestarts | integer, at least 0 | |
initialBackoffMs | integer, at least 0 | |
maxBackoffMs | integer, at least 0 | |
backoffFactor | number | |
resetAfterMs | integer, 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.
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
| Name | Type | Description |
|---|---|---|
kind | "never" |
traffic-idle
| Name | Type | Description |
|---|---|---|
kind | "traffic-idle" | |
timeoutMs | IdleTimeoutMs |
max-lifetime
| Name | Type | Description |
|---|---|---|
kind | "max-lifetime" | |
ttlMs | MaxLifetimeMs |
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