Contract reference
This page is the reference of each contract type: what the contract is for, how to use it, the providers that this repository has for it, and the options of its request and its result. The Contracts (RFC 189) page tells what a contract is and how contracts relate to hosting resources.
How to read a section
Each section has the same parts:
- The description of the contract type, and the file that defines it.
- The providers in this repository, with the name that selects each provider.
- An example of a consumer.
- The options. A consumer sets the
requestoptions. A provider sets theresultoptions, and the consumer reads them.
The common pattern
All contract types have the same option tree, config.contracts.<type>.
A consumer declares a request below want, and reads the answer below
results. The path below want is free: most consumers use
want.<consumer>.<instance>. The value at the end of the path has the
request. The results tree has the same paths, and each value has the request
options and the result options together.
{ config, ... }:
{
contracts.smtp.want.myapp.default.request.sender = "myapp@example.org";
services.myapp.mail = {
inherit (config.contracts.smtp.results.myapp.default) host port username;
};
}
A provider declares an option of the type mkProviderType, and registers the
option below providers.<name>. The fulfill function calculates the result
from the request. The default of the option is
providerRequests.<name>, the requests that the provider must answer, so a
provider that answers no request does nothing.
{
config,
lib,
options,
...
}:
{
options.services.myrelay.smtp = lib.mkOption {
default = config.contracts.smtp.providerRequests.myrelay;
type = config.contracts.smtp.mkProviderType {
fulfill = request: {
host = "relay.example.org";
port = 587;
username = request.sender;
passwordFile = "/run/secrets/relay-password";
};
};
};
config.contracts.smtp.providers.myrelay.module = options.services.myrelay.smtp;
}
One provider answers each request. Select the provider with one of these options:
contracts.<type>.defaultProviderName = "<name>";selects the provider for all requests of the type, by its name.contracts.<type>.defaultProvider = config.contracts.<type>.providers.<name>;does the same with a reference to the provider.contracts.<type>.instances.<consumer>.<instance> = config.contracts.<type>.providers.<name>;selects the provider for one request.contracts.<type>.instanceProviderNames.<consumer>.<instance> = "<name>";selects the provider for one request, by its name.
If a consumer reads a result and no provider is selected, the evaluation fails.
A result that is a path, for example passwordFile, is the path of a file on
the target machine. The file does not exist at evaluation time, and its
contents do not go into the Nix store.
In a Fediversity deployment, a component usually requests a hosting resource, and the resource declares the contract requests and selects the providers. Use a contract directly when you write a resource, a provider, or a NixOS module of a component.
credentials
Contract for delivering a secret to a systemd unit as a systemd
credential. A consumer names the unit, the credential id that unit loads
it under, and where the value comes from; a provider makes that id
resolvable and returns the LoadCredential= entry to attach.
Unlike fileSecrets (a file at a path) and envSecrets (an environment
file bound to a unit), nothing is rendered onto the node: the value exists
only inside the unit's credentials directory, for as long as the unit
runs. That also rules out EnvironmentFile=, which systemd loads before
it materializes %d -- a consumer that needs the value in its
environment has to read it out of the credentials directory itself.
A request the provider cannot resolve is NOT a failed start. systemd
reads the source until EOF and takes whatever arrived as the value, so a
provider that refuses or errors hands the unit an EMPTY credential and
the unit starts normally. Restart= therefore buys a consumer nothing on
its own: it has to reject an empty value itself, and can then rely on the
restart to converge over the window in which the value exists nowhere yet
-- a fresh deployment whose generating node has not published it.
Defined in nix/contracts/lib/contracts/definitions/credentials.nix.
Use credentials when a systemd unit reads a secret as a systemd credential.
The value is only in the credentials directory of the unit, and is not in a
file that other units can read.
These modules register a provider. The name of a provider is the value
for defaultProviderName.
-
openbao: the optionservices.systemd-creds-openbao.contracts.credentialsinnix/contracts/nixos/modules/services/security/creds-openbao.nix.credentialsprovider instances. Each request becomes oneservices.systemd-creds-openbao.rulesentry, letting the requesting unit alone load the secret atpathunder the credential id it asked for. Requesting anything at all switches the daemon on. -
hardcoded-credential(only for tests): the optiontesting.hardcoded-credentialinnix/contracts/nixos/modules/testing/hardcoded-credential.nix.Hardcoded systemd credentials. These should only be used in tests.
They let a test exercise a unit's
LoadCredential=wiring without standing up the secret store a real provider reads from. The value is installed at runtime as a0400 rootfile the credential is loaded from, rather than handed over as a store path, so what the unit sees matches what a real provider delivers.
Give the name of the unit with its suffix. Add the loadCredential result to
the unit:
{ config, ... }:
{
contracts.credentials.want.audit.ingest.request = {
unit = "fediversity-audit-shipper.service";
id = "auth-header";
path = "vars/secret/audit-ingest/authHeader";
};
systemd.services.fediversity-audit-shipper.serviceConfig.LoadCredential = [
config.contracts.credentials.results.audit.ingest.loadCredential
];
}
The unit reads the value from $CREDENTIALS_DIRECTORY/auth-header.
- A request is for one unit. Declare a second request for a second unit.
- When the provider cannot find the value, the credential is empty and the unit starts. The service must refuse an empty value.
- A credential cannot be an
EnvironmentFile. UseenvSecretsfor environment variables.
Options of credentials
credentials.request.base64Decode
Whether the field value is base64-encoded and must be decoded before being served.
Type: boolean
Default:
true
Declared by:
credentials.request.field
Field within the secret at path holding the value.
Type: string
Default:
"content"
Declared by:
credentials.request.id
Credential id the unit loads the value under, i.e. the <id> of
LoadCredential=<id>:<source> and the name of the file the unit finds
under $CREDENTIALS_DIRECTORY.
Type: string
Declared by:
credentials.request.kv
Whether path addresses a versioned key-value entry rather than a
leased or engine-issued secret. Set false for an endpoint that mints
or rotates its own value – e.g. a database secrets engine’s
database/static-creds/<role>, whose current password is at field
password and is not base64-wrapped.
Type: boolean
Default:
true
Declared by:
credentials.request.path
Provider-specific read path of the secret holding the value (e.g. a KV v2 read path for an OpenBao-backed provider). A provider that carries the value itself (a test stub) ignores it, as it does the other addressing options below.
Type: string
Default:
""
Declared by:
credentials.request.transient
Whether unit is created at run time by systemd-run --unit= rather
than declared, as restic-run-<name>.service is
(nix/contracts/nixos/modules/services/backup/restic.nix).
Such a unit can be given nothing on disk, and a provider must not try:
systemd-run refuses a name that is already loaded or carries a
fragment file. A fragment fails the first invocation; a
<unit>.wants/ symlink is worse, pinning the unit object past its own
exit so that --collect never reaps it and only the SECOND invocation
fails. Either way the name is lost, which is the name the request
exists to fix – the runner names its unit precisely so the serving
rule can name one unit rather than every run-*.service.
A provider may therefore serve a transient unit but may hang no dependency on it, so whatever a provider does for an ordinary consumer to be reachable when it starts, a transient one goes without.
Type: boolean
Default:
false
Declared by:
credentials.request.unit
Systemd unit, with its suffix (authelia-main.service), that loads
the credential. A provider may serve the value to that unit alone, so
a second unit reading the same secret is a second request.
Type: string
Declared by:
credentials.result.loadCredential
The whole LoadCredential= entry, <id>:<source>, to add to the
requesting unit’s serviceConfig.LoadCredential.
Type: string
Declared by:
credentials.result.source
The <source> of LoadCredential=<id>:<source>: the file or socket
systemd reads the value from. Useful on its own where a consumer
builds the entry itself, or maps several ids to one source.
Type: string
Declared by:
dns
Contract for authoritative-DNS coordinates, where a consumer requests publication under a domain and a provider returns the coordinates needed to reach the service holding that zone: the octoDNS provider name, its host/port (and scheme), and an optional path to an API credential. The credential itself is never carried -- only a path, so it stays out of the Nix store.
Defined in core/resources/dns/contract.nix.
Use dns to find the authoritative DNS service that publishes the records of
a domain. The result has only coordinates, so the consumer evaluates the
contract on its own node, and no shared evaluation is necessary.
These modules register a provider. The name of a provider is the value
for defaultProviderName.
-
bind: the optionfediversity.dns-self-hosted.dnsincore/resources/dns/self-hosted.nix.Self-hosted-node-backed provider of the local
dnscontract. -
external: the optionfediversity.dns-external.dnsincore/resources/dns/external.nix.External-service-backed provider of the local
dnscontract.
lib.contracts does not have this contract type. A module that uses it must
import the definition:
{ config, ... }:
let
dns = config.contracts.dns.results.windmill.default;
in
{
imports = [ ../../../resources/dns/contract.nix ];
contracts.dns.want.windmill.default.request.domain = "example.org";
environment.etc."octodns/provider.json".text = builtins.toJSON {
inherit (dns)
provider
host
port
scheme
;
};
}
In a deployment, the dns resource declares the request, and
core/setups/wiring/resource-routing.nix selects the provider with
defaultProviderName.
apiTokenFile is the path of a file with the credential, and not the
credential. The empty string means that there is no file: the deployer gets
the credential by a different method.
Options of dns
dns.request.domain
The domain (zone) the consumer needs records published under.
Type: string
Example:
"example.tld"
Declared by:
dns.result.apiTokenFile
Path to a file containing the credential authenticating against the service, read on the deployer at apply time. Never the credential value: contents must not reach the Nix store. Empty means no file is supplied and the credential is resolved another way (for the self-hosted bind node, the out-of-band TSIG key file).
Type: string
Default:
""
Example:
"/run/secrets/powerdns-api-key"
Declared by:
dns.result.domain
The domain the provider settled on (the request’s, unless the provider overrides it).
Type: string
Declared by:
dns.result.host
Host the service’s management API (or, for bind, its RFC2136/AXFR listener) answers on.
Type: string
Example:
"pdns.example.org"
Declared by:
dns.result.port
Port that management endpoint listens on.
Type: signed integer
Example:
8081
Declared by:
dns.result.provider
Name of the octoDNS provider that speaks to this service, as keyed in the octodns-zone effect’s provider catalog (bind, powerdns, …).
Type: string
Example:
"powerdns"
Declared by:
dns.result.scheme
URL scheme the management API is reached over (http or https). Meaningless for providers whose transport is not HTTP (bind), which leave it at the default.
Type: string
Default:
"http"
Example:
"https"
Declared by:
envSecrets
Contract for delivering a set of secret values to a service as an
environment file. A consumer requests one or more environment variables,
each sourced from a secret at a given path, and a provider renders them
into an EnvironmentFile attached to the consumer's systemd unit at
runtime, reloading the unit when a value rotates.
Unlike fileSecrets (a single file at a path), envSecrets expresses a
whole set of variables bound to one unit, which is what services taking an
EnvironmentFile (Mastodon, PeerTube, ...) need.
Defined in nix/contracts/lib/contracts/definitions/env-secrets.nix.
Use envSecrets when a systemd unit reads secrets from environment variables.
The provider writes an environment file and adds it to the unit as
EnvironmentFile, so the consumer usually does not read the result.
These modules register a provider. The name of a provider is the value
for defaultProviderName.
-
openbao-agent: the optionservices.secrets-openbao.envSecretsinnix/contracts/nixos/modules/services/security/secrets-openbao.nix.envSecretsprovider instances. Each request delivers a set of environment variables to itsunitvia an agent-renderedEnvironmentFile, reloading the unit on rotation.Each
optionalvariable is rendered into anEnvironmentFileof its own, attached to the same unit but outside its startup barrier, so a secret nobody ever publishes cannot withhold the required variables beside it, nor the other optional ones.result.environmentFilenames the required file: it is the one whose arrival the unit is ordered behind.
Give the name of the unit without the .service suffix. Each variable names
the location of its value at the provider:
{
contracts.envSecrets.want.myapp.default.request = {
unit = "myapp";
variables.API_TOKEN.path = "kv/data/myapp/secret/api/token";
};
}
If the consumer writes the unit itself, it can read the path of the file from
config.contracts.envSecrets.results.myapp.default.environmentFile. The file
exists only at run time.
Options of envSecrets
envSecrets.request.unit
Name of the systemd unit (without the .service suffix) the rendered
environment file is attached to. The provider wires the unit to wait
for the file and reload when any value rotates.
Type: string
Declared by:
envSecrets.request.variables
Environment variables to deliver, keyed by variable name. Each maps to
a secret at the given path, reading the named field, optionally
base64-decoding it.
A variable is required unless it says optional; see that option for
what a provider owes the two kinds.
Type: attribute set of (submodule)
Declared by:
envSecrets.request.variables.<name>.base64Decode
Whether the field value is base64-encoded and must be decoded before being written into the environment file.
Type: boolean
Default:
true
Declared by:
envSecrets.request.variables.<name>.field
Field within the secret at path holding the value.
Type: string
Default:
"content"
Declared by:
envSecrets.request.variables.<name>.optional
Whether this variable may be absent: the secret at path need
never be published, and the unit starts and runs without it.
A provider must deliver an optional variable such that its absence can neither delay nor block the required ones. Delivery mechanisms that resolve a whole set at once – an agent rendering one file from several reads, say – have to carry the optional variables separately, or one unpublished secret withholds every variable beside it.
Only the requester knows which of its variables it can run without, so this is stated here rather than inferred from whether a value happens to exist.
Type: boolean
Default:
false
Declared by:
envSecrets.request.variables.<name>.path
Provider-specific path to the secret holding this variable’s value (e.g. a KV v2 read path for an OpenBao-backed provider).
Type: string
Declared by:
envSecrets.result.environmentFile
Path to the rendered environment file. This path will exist after deploying to a target host; it is not available through the nix store.
Type: string
Declared by:
fileBackup
Contract for file backup where a directory containing regular files is to be backed up.
Defined in nix/contracts/lib/contracts/definitions/file-backup.nix.
Use fileBackup to make a backup of directories.
These modules register a provider. The name of a provider is the value
for defaultProviderName.
-
restic: the optionservices.restic.contracts.fileBackupinnix/contracts/nixos/modules/services/backup/restic.nix.Instances of the fileBackup contract fulfilled by restic.
-
hardcoded-file-backup(only for tests): the optiontesting.hardcoded-file-backupinnix/contracts/nixos/modules/testing/hardcoded-file-backup.nix.Hardcoded file backup provider for testing. Uses tar to create timestamped snapshots in a local directory.
The request names the directories and the user that reads them. The settings of the repository are options of the provider, at the same path as the request:
{ config, ... }:
{
contracts.fileBackup = {
want.netbox.uploads.request = {
user = "netbox";
sourceDirectories = [ "/var/lib/netbox/media" ];
};
defaultProvider = config.contracts.fileBackup.providers.restic;
};
services.restic.contracts.fileBackup.netbox.uploads = {
repository = "s3:https://s3.example.org/netbox-backups";
passwordFile = "/run/secrets/restic-password";
};
}
The results are two scripts:
scripthas the commandsbackup,snapshots,restore <snapshot>andexec.upgradeScripthas the commandsbackup <version>andrestore <version>, for a backup before an upgrade of the application. It runs asroot. Therestorecommand refuses a snapshot of a newer version unlessRESTORE_IGNORE_VERSION=1is set.
sourceDirectories must have one directory or more.
Options of fileBackup
fileBackup.request.excludePatterns
File patterns to exclude.
Type: list of string
Default:
[ ]
Declared by:
fileBackup.request.hooks
Hooks to run around the backup.
Type: submodule
Default:
{ }
Declared by:
fileBackup.request.hooks.afterBackup
Hooks to run after backup.
Type: list of absolute path
Default:
[ ]
Declared by:
fileBackup.request.hooks.beforeBackup
Hooks to run before backup.
Type: list of absolute path
Default:
[ ]
Declared by:
fileBackup.request.sourceDirectories
Directories to back up.
Type: non-empty (list of string)
Example:
[
"/var/lib/vaultwarden"
]
Declared by:
fileBackup.request.user
Unix user doing the backups.
Type: string
Example:
"vaultwarden"
Declared by:
fileBackup.request.version
The version of the format of the files, for example the version of
the application that writes them. The provider records it with each
snapshot, and script restore <snap> refuses a snapshot with a newer
version. null records no version.
Type: null or string
Default:
null
Example:
"9.20.0"
Declared by:
fileBackup.result.script
Script package exposing verbs:
<script> backup - take a backup
<script> snapshots - list snapshots, one id per line
<script> restore <snap> - restore named snapshot. It refuses
a snapshot whose version is newer than request.version, except
when RESTORE_IGNORE_VERSION=1 is set.
<script> exec <args...> - passthrough to underlying tool
Type: package
Declared by:
fileBackup.result.upgradeScript
Path to a script, to run as root, for the backup before an upgrade:
backup <version>takes a snapshot ofsourceDirectoriesas an upgrade backup of the files of version<version>. The provider keeps the 2 newest upgrade backups, and the scheduled backups do not remove them.restore <version>puts the newest upgrade backup of version<version>back. It deletes the files that the snapshot does not have, but it keeps the files that matchexcludePatterns.
Type: absolute path
Declared by:
fileSecrets
Contract for secrets handling where a consumer requests a secret and a provider provides it at runtime at a given file path.
Defined in nix/contracts/lib/contracts/definitions/file-secrets.nix.
Use fileSecrets when a service reads a secret from a file.
These modules register a provider. The name of a provider is the value
for defaultProviderName.
-
agenix: the optionfediversity.agenix.fileSecretsincore/resources/secrets/agenix.nix.Age-backed
fileSecretsprovider: decrypts.agefiles at activation via agenix. -
openbao-agent: the optionservices.secrets-openbao.fileSecretsinnix/contracts/nixos/modules/services/security/secrets-openbao.nix.fileSecretsprovider instances. Each request composes the configuredlinesinto a single environment-style secret file rendered by the agent, returning its path. The file is delivered tounitas anEnvironmentFileso this module handles ordering and reload; consumers that read the file directly point atresult.path. -
vars: the optionservices.varsinnix/contracts/nixos/modules/services/security/vars.nix.Declarative file generation provider (vars).
Reads generateFiles contract requests and generates files at boot using the configured varsBackend for storage.
-
hardcoded-secret(only for tests): the optiontesting.hardcoded-secretinnix/contracts/nixos/modules/testing/hardcoded-secret.nix.Hardcoded file secrets. These should only be used in tests.
They aim to replace the usage of pkgs.writeText in NixOS VM tests as those make the file world readable while this module set runtime permissions on the file. This makes the tests more accurate, ensuring the permissions set by the contract consumer are correct.
The request tells who can read the file. The result is the path of the file:
{ config, ... }:
{
contracts.fileSecrets.want.forgejo.database-password.request = {
owner = "forgejo";
group = "forgejo";
mode = "0400";
};
services.forgejo.database.passwordFile =
config.contracts.fileSecrets.results.forgejo.database-password.path;
}
The secrets resource selects a provider for each of its requests with
instances, and sets no defaultProvider. Thus other consumers on the node
can select their own provider.
Options of fileSecrets
fileSecrets.request.group
Linux group that must own the secret file.
Type: string
Declared by:
fileSecrets.request.mode
Mode the secret file must have.
Type: string
Default:
"0400"
Declared by:
fileSecrets.request.owner
Linux user that must own the secret file.
Type: string
Declared by:
fileSecrets.result.path
Path to the file containing the secret generated out of band.
This path will exist after deploying to a target host, it is not available through the nix store.
Type: string
Declared by:
generateFiles
Contract for declarative file generation where a consumer provides a script that produces named files and a provider handles generation, storage, and provisioning with the requested permissions.
Defined in nix/contracts/lib/contracts/definitions/generate-files.nix.
Use generateFiles when a machine must generate files one time and keep them,
for example a random key.
These modules register a provider. The name of a provider is the value
for defaultProviderName.
-
vars: the optionservices.varsinnix/contracts/nixos/modules/services/security/vars.nix.Declarative file generation provider (vars).
Reads generateFiles contract requests and generates files at boot using the configured varsBackend for storage.
The path of a request has one level, want.<instance>. The script writes each
declared file to $out/<file>:
{ config, pkgs, ... }:
{
contracts.generateFiles.want.netbox.request = {
files.secretKeyFile = {
owner = "netbox";
group = "netbox";
};
runtimeInputs = [ pkgs.openssl ];
script = ''
openssl rand -hex 64 > "$out"/secretKeyFile
'';
};
services.netbox.secretKeyFile =
config.contracts.generateFiles.results.netbox.files.secretKeyFile.path;
}
- A request can name other requests in
dependencies. The script reads their files from$in/<dependency>/<file>. - The provider does not run the script again when all files exist. When only
some of the files exist, the provider refuses to run. Set
independent = truewhen each file is independent of the others: the provider then runs the script, and keeps only the files that did not exist. - An item of
runtimeInputscan be a string, which is an attribute path in the package set of the provider. Use this when the request must be plain data.
Options of generateFiles
generateFiles.request.dependencies
Names of other generateFiles instances whose outputs
are made available under $in/<name>/ when the script runs.
Type: list of string
Default:
[ ]
Declared by:
generateFiles.request.files
Files to generate. Keys are file names the script must produce under $out/.
Type: attribute set of (submodule)
Declared by:
generateFiles.request.files.<name>.group
Unix group that must own the generated file.
Type: string
Default:
"root"
Declared by:
generateFiles.request.files.<name>.mode
File permissions as an octal string.
Type: string
Default:
"0400"
Declared by:
generateFiles.request.files.<name>.owner
Unix user that must own the generated file.
Type: string
Default:
"root"
Declared by:
generateFiles.request.files.<name>.secret
Whether the file contains sensitive data.
Type: boolean
Default:
true
Declared by:
generateFiles.request.independent
Whether the requested files are independent of one another.
By default an instance is ATOMIC: the provider refuses to run when some of its files exist and others do not, because a generator whose outputs are correlated (the halves of a keypair, a secret and its digest) cannot regenerate one without invalidating the rest. That refusal is also what makes adding a file to an already-deployed instance a hard failure with no recovery short of hand-placing the missing file on every node.
Set this when each file stands on its own – one independent random password per consuming app, say. The provider then still runs the whole script, but keeps only the files that were missing and discards regenerated copies of the ones already present, so an instance can grow a file without rotating its siblings. Generators need no change: they always produce their full set.
Type: boolean
Default:
false
Declared by:
generateFiles.request.runtimeInputs
Packages available in PATH during script execution.
A string is the attribute path of a package in the pkgs of the
provider, such as "openssl" or "python3Packages.pip".
A request that is data, such as one in the plan of a node, uses
strings.
Type: list of (string or package)
Default:
[ ]
Declared by:
generateFiles.request.script
Shell script that generates the requested files.
The script must write each declared file to $out/<name>.
Dependency outputs (from dependencies) are available under $in/<dep>/.
Type: string
Declared by:
generateFiles.result.files
Generated files with their provisioned paths.
Type: attribute set of (submodule)
Declared by:
generateFiles.result.files.<name>.path
Path to the generated file on the target system.
Type: string
Declared by:
ldap
Contract for LDAP integration where a consumer requests access to a directory group and a provider supplies connection details.
Defined in nix/contracts/lib/contracts/definitions/ldap.nix.
Use ldap when an application reads its users from a directory.
These modules register a provider. The name of a provider is the value
for defaultProviderName.
-
lldap: the optionfediversity.lldapincore/resources/ldap/lldap-provider.nix.The lldap-backed provider of the nixpkgs
ldapcontract on this node: its contract instances (ldap) plus the deployment parameters shared across all requests. -
openldap(only for tests): the optiontesting.openldapinnix/contracts/nixos/modules/testing/openldap.nix.openldap reference provider for the
ldapcontract, for testing. Runs openldap with a minimal directory containing requested groups.
The request names the group whose members can use the application. The provider makes the group:
{ config, ... }:
let
ldap = config.contracts.ldap.results.forgejo.default;
in
{
contracts.ldap.want.forgejo.default.request.group = "forgejo-users";
services.myapp.ldap = {
inherit (ldap) url bindDN userBaseDN;
bindPasswordFile = ldap.bindPasswordFile;
userFilter = "(memberOf=cn=${ldap.group},${ldap.groupBaseDN})";
caFile = ldap.caFile;
};
}
caFile is null when the server has no TLS, or when its certificate is from
a public certificate authority.
The directory is usually on a different node. The ldap resource then
evaluates the contract one time with crossNodeWant, and gives the result to
the node of the consumer. See
contracts across nodes.
Options of ldap
ldap.request.group
LDAP group that the service requires. The provider is expected to create this group.
Type: string
Example:
"myapp-users"
Declared by:
ldap.result.baseDN
Base distinguished name for LDAP queries.
Type: string
Example:
"dc=example,dc=com"
Declared by:
ldap.result.bindDN
DN used to bind (authenticate) to the LDAP server.
Type: string
Declared by:
ldap.result.bindPasswordFile
Path to a file containing the bind password.
Type: string
Declared by:
ldap.result.caFile
Path to a PEM file with the CA certificate that signs the server certificate.
null when url is a plaintext ldap:// URL,
or when the system trust store already trusts the server certificate.
Type: null or string
Default:
null
Declared by:
ldap.result.groupBaseDN
Base DN under which group entries reside.
Type: string
Example:
"ou=groups,dc=example,dc=com"
Declared by:
ldap.result.host
Hostname of the LDAP server.
Type: string
Declared by:
ldap.result.port
Port of the LDAP server.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Declared by:
ldap.result.url
URL of the LDAP server, with the scheme.
A consumer connects to this URL.
With ldaps://, the consumer verifies the server certificate against caFile.
Type: string
Example:
"ldaps://ldap.example.com:6360"
Declared by:
ldap.result.userBaseDN
Base DN under which user entries reside.
Type: string
Example:
"ou=people,dc=example,dc=com"
Declared by:
netbox
Contract for NetBox IPAM coordinates, where a consumer requests allocation context (the cluster its virtual machines are recorded under and the prefix pool its addresses come from) and a provider returns the coordinates needed to reach a NetBox instance: its URL and an optional path to an API token. The token itself is never carried -- only a path, so credentials stay out of the Nix store.
Defined in core/resources/netbox/contract.nix.
Use netbox to find the NetBox instance that allocates the addresses of the
nodes, and to tell where the allocation comes from. The result has only
coordinates, so the consumer evaluates the contract on its own node.
These modules register a provider. The name of a provider is the value
for defaultProviderName.
-
external: the optionfediversity.netbox-external.netboxincore/resources/netbox/external.nix.External-instance-backed provider of the local
netboxcontract. -
netbox: the optionfediversity.netbox-self-hosted.netboxincore/resources/netbox/self-hosted.nix.Self-hosted-node-backed provider of the local
netboxcontract.
lib.contracts does not have this contract type. A module that uses it must
import the definition:
{ config, ... }:
let
netbox = config.contracts.netbox.results.windmill.default;
in
{
imports = [ ../../../resources/netbox/contract.nix ];
contracts.netbox.want.windmill.default.request = {
clusterName = "incus";
prefixSelector = {
kind = "tag";
value = "fediversity";
};
allocateIpv4 = true;
};
environment.etc."netbox/coordinates.json".text = builtins.toJSON {
inherit (netbox) serverUrl clusterName tenant;
};
}
- The result repeats the options of the request, so a reader of the result has the full context of the allocation.
- An empty
serverUrlmeans that the deployer finds the URL at apply time. apiTokenFileis the path of a file with the token, and not the token. The empty string means that there is no file.
Options of netbox
netbox.request.allocateIpv4
Whether the consumer allocates a v4 address for a public node. Off, the consumer allocates only v6, and registers only the v4 addresses its nodes pin.
Type: boolean
Default:
false
Declared by:
netbox.request.clusterName
Name of the NetBox cluster the per-node virtual machines are recorded under. Must already exist at the provider (the deploy reads it, it does not create it). One cluster per deployment, unless a tenant tells two deployments apart within a shared one.
Type: string
Example:
"incus"
Declared by:
netbox.request.prefixSelector
How the consumer discovers the prefix(es) to allocate from, without naming a subnet.
Type: submodule
Declared by:
netbox.request.prefixSelector.kind
How to select the prefix pool: cidr, id, or a NetBox prefix filter name (tag, status, site_id, …).
Type: string
Example:
"tag"
Declared by:
netbox.request.prefixSelector.value
The selector’s value: a CIDR, a comma-separated list of prefix ids, or a filter value.
Type: string
Example:
"fediversity"
Declared by:
netbox.request.rangeSelector
How the consumer discovers an IP range to allocate from instead of the whole prefix, for a family the range covers. An empty kind selects no range.
Type: submodule
Default:
{
kind = "";
value = "";
}
Declared by:
netbox.request.rangeSelector.kind
How to select the prefix pool: cidr, id, or a NetBox prefix filter name (tag, status, site_id, …).
Type: string
Example:
"tag"
Declared by:
netbox.request.rangeSelector.value
The selector’s value: a CIDR, a comma-separated list of prefix ids, or a filter value.
Type: string
Example:
"fediversity"
Declared by:
netbox.request.tenant
Name of the NetBox tenant the per-node virtual machines are recorded under. NetBox keys a virtual machine’s uniqueness on cluster, tenant and name together, so this is what lets two deployments share one cluster and still name a node openbao. Empty (the default) records them untenanted, which needs a cluster of this deployment’s own. Must already exist at the provider (the deploy reads it, it does not create it).
Type: string
Default:
""
Example:
"fediversity-dev"
Declared by:
netbox.result.allocateIpv4
Whether v4 addresses are allocated (the request’s).
Type: boolean
Default:
false
Declared by:
netbox.result.apiTokenFile
Path to a file containing the NetBox API token, read on the deployer at apply time. Never the token value: contents must not reach the Nix store. Empty means no file is supplied and the token is resolved another way (for the self-hosted node, from OpenBao KV).
Type: string
Default:
""
Example:
"/run/secrets/netbox-api-token"
Declared by:
netbox.result.clusterName
The cluster name the provider settled on (the request’s, unless the provider overrides it).
Type: string
Declared by:
netbox.result.prefixSelector
The prefix selector the provider settled on (the request’s, unless the provider overrides it).
Type: submodule
Declared by:
netbox.result.prefixSelector.kind
How to select the prefix pool: cidr, id, or a NetBox prefix filter name (tag, status, site_id, …).
Type: string
Example:
"tag"
Declared by:
netbox.result.prefixSelector.value
The selector’s value: a CIDR, a comma-separated list of prefix ids, or a filter value.
Type: string
Example:
"fediversity"
Declared by:
netbox.result.rangeSelector
The range selector the provider settled on (the request’s, unless the provider overrides it). An empty kind means the whole prefix is the pool.
Type: submodule
Default:
{
kind = "";
value = "";
}
Declared by:
netbox.result.rangeSelector.kind
How to select the prefix pool: cidr, id, or a NetBox prefix filter name (tag, status, site_id, …).
Type: string
Example:
"tag"
Declared by:
netbox.result.rangeSelector.value
The selector’s value: a CIDR, a comma-separated list of prefix ids, or a filter value.
Type: string
Example:
"fediversity"
Declared by:
netbox.result.serverUrl
Base URL of the NetBox instance to allocate against. Empty when the provider expects the URL to be discovered at apply time (the self-hosted node’s own published TF state).
Type: string
Example:
"https://netbox.example.org"
Declared by:
netbox.result.tenant
The tenant the provider settled on (the request’s, unless the provider overrides it). Empty means the virtual machines are recorded untenanted.
Type: string
Default:
""
Declared by:
oidc
Contract for OIDC where a consumer requests OpenID Connect integration and a provider supplies the OpenID Connect endpoint and client credentials.
Defined in nix/contracts/lib/contracts/definitions/oidc.nix.
Use oidc when an application lets its users log in through an OpenID Connect
provider.
These modules register a provider. The name of a provider is the value
for defaultProviderName.
-
authelia-oidc: the optionfediversity.autheliaincore/resources/oidc/authelia-oidc-provider.nix.The authelia-backed provider of the nixpkgs
oidc(OIDC) contract on this node: its contract instances (oidc) plus the deployment parameters shared across all OIDC clients. -
dex(only for tests): the optiontesting.dexinnix/contracts/nixos/modules/testing/dex.nix.Dex reference provider for the
oidccontract, for testing. Runs Dex with static client configuration.
By convention, the path of a request starts with the client ID:
{ config, ... }:
let
oidc = config.contracts.oidc.results.grafana.default;
in
{
contracts.oidc.want.grafana.default.request = {
clientID = "grafana";
redirectURI = "https://grafana.example.org/login/generic_oauth";
scopes = [
"openid"
"profile"
"email"
"groups"
];
};
services.grafana.settings."auth.generic_oauth" = {
enabled = true;
client_id = oidc.clientID;
client_secret = "$__file{${oidc.clientSecretFile}}";
auth_url = oidc.authorizationEndpoint;
token_url = oidc.tokenEndpoint;
api_url = oidc.userinfoEndpoint;
scopes = oidc.scopes;
};
}
Settings of a client that the request does not have, for example the groups that can log in, are options of the provider at the same path as the request.
Options of oidc
oidc.request.clientID
Client ID to register with the OIDC provider.
Type: string
Declared by:
oidc.request.redirectURI
Callback URI the provider should redirect to after authentication.
Type: string
Example:
"https://myapp.example.com/oauth2/callback"
Declared by:
oidc.request.redirectURIs
More callback URIs the provider accepts, in addition to
redirectURI. Use them when the client is served under more than
one name, or when it also has a local callback. The provider
registers redirectURI and these URIs.
Type: list of string
Default:
[ ]
Example:
[
"http://localhost:8250/oidc/callback"
]
Declared by:
oidc.request.scopes
OIDC scopes that the provider lets the client request. The client requests the same scopes, so that the provider and the client agree.
Type: list of string
Default:
[
"openid"
"profile"
"email"
]
Example:
[
"openid"
"profile"
"email"
"groups"
]
Declared by:
oidc.result.authorizationEndpoint
OIDC authorization endpoint URL. Provided for consumers that cannot perform discovery from the issuer and need the endpoint spelled out.
Type: string
Example:
"https://auth.example.com/api/oidc/authorization"
Declared by:
oidc.result.clientSecretFile
Path to a file containing the OIDC client secret.
Type: string
Declared by:
oidc.result.issuer
OIDC issuer URL (the provider’s discovery endpoint base).
Type: string
Example:
"https://auth.example.com"
Declared by:
oidc.result.tokenEndpoint
OIDC token endpoint URL. Provided for consumers that cannot perform discovery from the issuer and need the endpoint spelled out.
Type: string
Example:
"https://auth.example.com/api/oidc/token"
Declared by:
oidc.result.userinfoEndpoint
OIDC userinfo endpoint URL. Provided for consumers that cannot perform discovery from the issuer and need the endpoint spelled out.
Type: string
Example:
"https://auth.example.com/api/oidc/userinfo"
Declared by:
postgresql
Contract for PostgreSQL database access where a consumer requests a database (and optionally a role name) and a provider supplies the connection coordinates plus a credential.
Defined in nix/contracts/lib/contracts/definitions/postgresql.nix.
Use postgresql when an application needs a PostgreSQL database.
These modules register a provider. The name of a provider is the value
for defaultProviderName.
-
postgresql: the optionfediversity.postgresqlincore/resources/postgresql/postgresql-provider.nix.The postgres-backed provider of the
postgresqlcontract on this node: its contract instances (postgresql) plus the deployment parameters shared across all requests.
{ config, ... }:
let
db = config.contracts.postgresql.results.forgejo.default;
in
{
contracts.postgresql.want.forgejo.default.request = {
database = "forgejo";
username = "forgejo";
};
services.forgejo.database = {
type = "postgres";
inherit (db) host port;
name = db.database;
user = db.username;
passwordFile = db.passwordFile;
};
}
urlFile is the path of a file with the full connection URL, with the
password. Use it for an application that reads one URL, for example with
LoadCredential.
The fulfill function of the provider does not read the state of the server.
Thus a consumer on a different node can get the result from a shared
evaluation. See contracts across nodes.
Options of postgresql
postgresql.request.database
PostgreSQL database the service requires. The provider is expected to create this database.
Type: string matching the pattern [A-Za-z0-9._@-]+
Example:
"myapp"
Declared by:
postgresql.request.username
PostgreSQL role the service connects as. Defaults to the consumer
name. The provider owns whether this role is a static, generate-once
account or a short-lived one minted on demand; either way it resolves
to the credential delivered through passwordFile/urlFile.
Type: string matching the pattern [A-Za-z0-9._@-]+
Example:
"myapp"
Declared by:
postgresql.result.host
Hostname of the PostgreSQL server.
Type: string matching the pattern [A-Za-z0-9._:-]+
Declared by:
postgresql.result.passwordFile
Path to a file containing the password for username. Consumers that
configure discrete connection fields (mastodon, peertube) read this;
consumers that want a single DSN read urlFile instead.
Type: string
Declared by:
postgresql.result.port
Port of the PostgreSQL server.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Declared by:
postgresql.result.sslMode
libpq sslmode consumers should use when connecting. PostgreSQL is
internal-only and reached over TLS, so the provider serves a cert from
the group’s ssl provider and consumers verify against it.
Type: string
Default:
"require"
Example:
"require"
Declared by:
postgresql.result.urlFile
Path to a file containing the full connection DSN
(postgres://user:pass@host:port/db). Consumers that take a single
connection string (windmill, the panel’s DATABASE_URL) load this via
LoadCredential; it carries the same credential as passwordFile,
just in URL form.
Type: string
Declared by:
redis
Contract for a Redis-compatible cache/key-value store where a consumer requests an isolated namespace and a provider supplies the connection coordinates plus the credential for that namespace's store.
Mirrors s3 (the capability is named after the abstract service, not the
implementation): the in-repo provider gives each namespace its OWN valkey
instance (valkey@<namespace>, its own process + port on the group's
valkey node), whose default-user password is rotated by OpenBao's
valkey-database-secrets engine. Isolation is physical (one process per
namespace) rather than an ACL key-scope, so the consumer authenticates
with a password only -- there is no per-namespace ACL username. The
contract names only the capability (redis), so any compatible backend
could fulfil it.
Defined in nix/contracts/lib/contracts/definitions/redis.nix.
Use redis when an application needs a key-value store with the Redis
protocol.
These modules register a provider. The name of a provider is the value
for defaultProviderName.
-
external: the optionfediversity.redisincore/resources/redis/external-provider.nix.The
externalprovider of the nixpkgsrediscontract on this node: its contract instances (redis) plus the deployment parameters shared across all requests -- the shared valkey node's coordinates and the openbao-agent password render directory. -
valkey(only for tests): the optiontesting.valkeyinnix/contracts/nixos/modules/testing/valkey.nix.Reference
valkeyprovider of therediscontract for testing. Runs one valkey instance per requested namespace on localhost (each on its own namespace-derived port, password on the instance'sdefaultuser) and materializes a static password file per namespace -- standing in for the per-namespacevalkey@<ns>instances + OpenBao-rotated password that the realexternalprovider uses. Isolation is physical (one instance per namespace), so there is no ACL key-scope.
Each namespace is a separate Valkey process with its own port and password, so a consumer cannot read the data of a different namespace:
{ config, ... }:
let
redis = config.contracts.redis.results.mastodon.default;
in
{
contracts.redis.want.mastodon.default.request.namespace = "mastodon";
services.mastodon.redis = {
createLocally = false;
inherit (redis) host port;
passwordFile = redis.passwordFile;
enableUnixSocket = false;
};
}
When tls is true, connect with TLS, and trust caFile if it is not null.
The store is usually on a different node. The redis resource then evaluates
the contract one time with crossNodeWant. See
contracts across nodes.
Options of redis
redis.request.namespace
The consumer’s logical cache identity. The provider gives this
namespace its own isolated store (the in-repo provider: its own
valkey@<namespace> instance). The in-repo provider also uses it as
the name of the OpenBao static role that rotates the password of that
instance. In a tenant deployment, the deployment namespace follows it.
Type: string
Example:
"mastodon"
Declared by:
redis.result.caFile
Path to the CA bundle that signs the server certificate, when tls
is true. Null means the system trust store.
Type: null or string
Default:
null
Declared by:
redis.result.host
Hostname of the Redis-compatible server. A consumer writes this value with no quotes into its configuration, so it cannot hold a newline or a quote.
Type: string matching the pattern [A-Za-z0-9._:-]+
Declared by:
redis.result.passwordFile
Path to a file containing the password. Rendered out of band by a
secret provider (the openbao-agent renders the OpenBao-rotated
password here). Must not live under /nix/store – the peertube
module asserts this, and the value rotates.
Type: string
Declared by:
redis.result.port
Port of the Redis-compatible server. The in-repo provider gives each namespace its own instance on its own port, so this is per-namespace.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Declared by:
redis.result.tls
Whether the server accepts only TLS on port. When true, the consumer
connects with TLS (a rediss:// URL) and verifies the server
certificate against caFile. When false, the consumer connects in
plain text (a redis:// URL).
Type: boolean
Default:
false
Declared by:
redis.result.username
ACL username the consumer authenticates as, if the provider scopes
access by ACL user. Empty (the default) means password-only auth on
the server’s built-in default user – the in-repo provider isolates
by per-namespace instance, not by ACL, so it leaves this empty. A
consumer writes this value with no quotes into its configuration, so
it cannot hold a newline or a quote.
Type: string matching the pattern [A-Za-z0-9._@-]*
Default:
""
Example:
"mastodon"
Declared by:
s3
Contract for a bucket on S3-compatible object storage. The consumer
requests a bucket by its logical name. The provider gives the physical
bucket name, the address of the S3 API, the region and, if it serves
them, the domains of the bucket on the virtual-host API and on the
website endpoint. The access keys are not part of this contract: a
secrets contract, for example credentials or envSecrets, gives them.
Defined in nix/contracts/lib/contracts/definitions/s3.nix.
Use s3 when an application needs a bucket in an object store with the S3
API.
These modules register a provider. The name of a provider is the value
for defaultProviderName.
-
garage(only for tests): the optiontesting.garageinnix/contracts/nixos/modules/testing/garage.nix.Garage reference provider for the
s3contract, for testing. Runs a single-node Garage on localhost and provisions requested buckets.
In a deployment, the garage resource (core/resources/garage/default.nix)
registers no provider. It calculates the result of each bucket and sets
contracts.s3.instances directly.
{ config, ... }:
let
s3 = config.contracts.s3.results.myapp-media.default;
in
{
contracts.s3.want.myapp-media.default.request.bucket = "myapp-media";
services.myapp.storage = {
inherit (s3) endpoint region bucket;
};
}
- The
bucketof the request is a logical name. Thebucketof the result is the name in the object store, which can have a prefix. Read the name from the result. - The access keys are not part of this contract. Get them from a contract for
secrets, for example
credentialsorenvSecrets.
Options of s3
s3.request.bucket
The logical name of the bucket.
Type: string
Example:
"mastodon"
Declared by:
s3.result.apiDomain
The virtual-host domain of the bucket on the S3 API, or null if
the provider does not serve virtual-host requests.
Type: null or string
Default:
null
Example:
"group-mastodon.s3.example.org"
Declared by:
s3.result.bucket
The physical name of the bucket. It can differ from the requested name, for example by a tenant prefix.
Type: string
Example:
"group-mastodon"
Declared by:
s3.result.endpoint
The URL of the S3 API, with the scheme and the port.
Type: string
Example:
"http://s3.example.org:3900"
Declared by:
s3.result.port
The port of the S3 API, the same port as in endpoint.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Example:
3900
Declared by:
s3.result.publicEndpoint
The URL of the S3 API that a client outside the network of the
provider can reach, with the scheme, or null if the provider does
not serve the S3 API publicly. The API takes path-style requests at
this URL.
Type: null or string
Default:
null
Example:
"https://s3.example.org"
Declared by:
s3.result.region
The region of the bucket.
Type: string
Default:
"us-east-1"
Declared by:
s3.result.webDomain
The domain of the bucket on the website endpoint, or null if the
provider has no website endpoint.
Type: null or string
Default:
null
Example:
"group-mastodon.web.example.org"
Declared by:
s3.result.webUrl
The URL of the bucket on the website endpoint, or null if the
provider has no website endpoint.
Type: null or string
Default:
null
Example:
"https://group-mastodon.web.example.org"
Declared by:
smtp
Contract for SMTP email sending where a consumer requests mail relay configuration and a provider supplies the connection details.
Defined in nix/contracts/lib/contracts/definitions/smtp.nix.
Use smtp when an application sends mail through a relay.
These modules register a provider. The name of a provider is the value
for defaultProviderName.
-
external-relay: the optionfediversity.smtp-relay.smtpincore/resources/smtp/external-relay.nix.External-relay-backed provider of the nixpkgs
smtpcontract. -
maddy: the optionfediversity.smtpincore/resources/smtp/maddy-provider.nix.The maddy-backed provider of the nixpkgs
smtpcontract on this node: its contract instances (smtp) plus the deployment parameters shared across all consumers. -
opensmtpd(only for tests): the optiontesting.opensmtpdinnix/contracts/nixos/modules/testing/opensmtpd.nix.opensmtpd reference provider for the
smtpcontract, for testing. Runs a local opensmtpd instance that accepts mail on localhost.
The path of the request starts with the name of the application. The maddy
provider makes one account for each name, and the user name of the account is
the sender address:
{ config, ... }:
let
smtp = config.contracts.smtp.results.authelia.default;
in
{
contracts.smtp.want.authelia.default.request.sender = "auth@example.org";
services.authelia.instances.main.settings.notifier.smtp = {
address = "submission://${smtp.host}:${toString smtp.port}";
inherit (smtp) username;
sender = smtp.sender;
};
}
passwordFile is the path of a file on the target machine. Give it to the
service as a file or as a credentials request.
Options of smtp
smtp.request.sender
Email address used as the sender (From header).
Type: string matching the pattern [A-Za-z0-9._%+@-]+
Example:
"noreply@example.com"
Declared by:
smtp.result.host
Hostname of the SMTP server.
Type: string matching the pattern [A-Za-z0-9._:-]+
Declared by:
smtp.result.passwordFile
Path to a file containing the SMTP password.
Type: string
Declared by:
smtp.result.port
Port of the SMTP server.
Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)
Declared by:
smtp.result.username
Username for SMTP authentication.
Type: string matching the pattern [A-Za-z0-9._%+=/@-]*
Declared by:
ssl
Contract for SSL certificate provisioning where a consumer requests a certificate and a provider generates it.
Defined in nix/contracts/lib/contracts/definitions/ssl.nix.
Use ssl to get a TLS certificate and its key for a domain.
These modules register a provider. The name of a provider is the value
for defaultProviderName.
-
acme: the optionfediversity.acme.sslincore/resources/ssl/acme.nix.ACME-backed SSL provider mapping
security.acmecert paths. -
openbao-pki: the optionfediversity.openbao-pki.sslincore/resources/ssl/openbao-pki.nix.OpenBao-PKI-backed SSL provider issuing runtime certs from a private CA.
-
self-signed: the optionfediversity.self-signed.sslincore/resources/ssl/self-signed.nix.Self-signed SSL certificate provider returning store paths.
-
self-signed-ssl(only for tests): the optiontesting.self-signed-sslinnix/contracts/nixos/modules/testing/self-signed-ssl.nix.Self-signed SSL certificate provider for testing.
By convention, the path of a request ends with the domain:
{ config, ... }:
let
domain = "ldap.example.org";
tls = config.contracts.ssl.results.fediversity.${domain};
in
{
contracts.ssl.want.fediversity.${domain}.request = {
inherit domain;
keyMode = "0600";
restartServices = [ "lldap.service" ];
};
systemd.services.lldap = {
after = [ tls.systemdService ];
wants = [ tls.systemdService ];
};
services.lldap.settings = {
ldaps_options.cert_file = tls.cert;
ldaps_options.key_file = tls.key;
};
}
The provider restarts the units in restartServices when it renews the
certificate. When keyOwner, keyGroup or keyMode is the empty string, the
provider uses its default.
Options of ssl
ssl.request.domain
Domain name for which the certificate is requested.
Type: string
Example:
"example.com"
Declared by:
ssl.request.keyGroup
Group the provider should give the private key file. Empty leaves the
provider default. See keyOwner.
Type: string
Default:
""
Example:
"postgres"
Declared by:
ssl.request.keyMode
Octal mode the provider should give the private key file. Empty leaves
the provider default (a private key must never be world-readable, so a
sane provider default is at most 0640). Set 0600 for a service
that owns the key exclusively (e.g. PostgreSQL, which rejects a key
with any group or world access).
Type: string
Default:
""
Example:
"0600"
Declared by:
ssl.request.keyOwner
Owner (user) the provider should give the private key file. Empty leaves the provider default (root). A consumer whose terminating service reads the key as a non-root user must set this (e.g. PostgreSQL, which rejects a key it cannot own): the provider chowns the key to this owner. Providers that cannot honour ownership (e.g. a store-baked key) ignore it.
Type: string
Default:
""
Example:
"postgres"
Declared by:
ssl.request.restartServices
Systemd units the provider should converge after (re)issuing the
certificate. A runtime-cert provider (e.g. openbao-pki) uses this
to heal consumers that raced the first issue at boot (a service that
burned its start limit while the cert file was still missing) and to
reload long-running consumers onto a renewed leaf. Providers whose
certs are eval-time store paths (self-signed) or that manage reloads
themselves (acme) ignore it.
Type: list of string
Default:
[ ]
Example:
[
"nginx.service"
]
Declared by:
ssl.result.cert
Path to the certificate file.
Type: string
Declared by:
ssl.result.key
Path to the private key file.
Type: string
Declared by:
ssl.result.systemdService
Systemd oneshot service that generates the certificate.
Ends with the .service suffix.
Type: string
Example:
"cert-generator.service"
Declared by:
streamingBackup
Contract for streaming backup where what to back up comes from a stream. Well suited for backing up databases or tar archives.
Defined in nix/contracts/lib/contracts/definitions/streaming-backup.nix.
Use streamingBackup to make a backup of data that a command writes as a
stream, for example a database dump.
These modules register a provider. The name of a provider is the value
for defaultProviderName.
-
restic: the optionservices.restic.contracts.streamingBackupinnix/contracts/nixos/modules/services/backup/restic.nix.Instances of the streamingBackup contract fulfilled by restic.
-
hardcoded-streaming-backup(only for tests): the optiontesting.hardcoded-streaming-backupinnix/contracts/nixos/modules/testing/hardcoded-streaming-backup.nix.Hardcoded streaming backup provider for testing. Stores streaming output in timestamped files and replays on restore. The name of a file is
snapshot-<nanoseconds>.<kind>[@<version>], with the kindscheduledorupgrade.
backupCmd writes the data to its standard output. restoreCmd reads the data
from its standard input:
{ config, pkgs, ... }:
{
contracts.streamingBackup = {
want.myapp.postgres.request = {
user = "postgres";
backupName = "myapp.sql";
backupCmd = "${pkgs.postgresql}/bin/pg_dump myapp";
restoreCmd = "${pkgs.postgresql}/bin/psql myapp";
};
defaultProvider = config.contracts.streamingBackup.providers.restic;
};
}
As for fileBackup, the settings of the repository are options
of the provider, at services.restic.contracts.streamingBackup.myapp.postgres.
The results are:
backupService, the name of the systemd unit that makes the backup.restoreScript, with the commandsrestore latest,snapshotsanddrill latest. Thedrillcommand needs adrillCmdin the request.upgradeScript, with the commandsbackup <version>andrestore <version>.
Options of streamingBackup
streamingBackup.request.backupCmd
A bash command that produces the backup data on stdout.
Type: string
Declared by:
streamingBackup.request.backupName
Name of the backup in the repository.
Type: string
Example:
"postgresql.sql"
Declared by:
streamingBackup.request.drillCmd
A bash command that reads the backup data on stdin and checks that
it restores, into a scratch target. It does not change the data of
the service. null gives no drill.
Type: null or string
Default:
null
Declared by:
streamingBackup.request.restoreCmd
A bash command that reads the backup data on stdin and restores it.
Type: string
Declared by:
streamingBackup.request.user
Unix user doing the backups.
Type: string
Example:
"vaultwarden"
Declared by:
streamingBackup.request.version
The version of the format of the backup data, for example the
version of the application that writes it. The provider records it
with each snapshot, and restoreScript refuses a snapshot with a
newer version. null records no version.
Type: null or string
Default:
null
Example:
"4.3.0"
Declared by:
streamingBackup.result.backupService
Name of the systemd service that performs the backup.
Type: string
Declared by:
streamingBackup.result.restoreScript
Path to a script that can restore from backup.
Supports restore latest and snapshots subcommands, and
drill latest when request.drillCmd is set.
restore latest gives the newest snapshot to restoreCmd, and
drill latest gives it to drillCmd, both as request.user.
Both refuse a snapshot whose version is newer than
request.version, except when RESTORE_IGNORE_VERSION=1 is set.
Type: absolute path
Declared by:
streamingBackup.result.upgradeScript
Path to a script, to run as root, for the backup before an upgrade:
backup <version>runsbackupCmdand keeps its output as an upgrade backup of the data of version<version>. The provider keeps the 2 newest upgrade backups, and the scheduled backups do not remove them.restore <version>gives the newest upgrade backup of version<version>torestoreCmd.
Type: absolute path
Declared by:
varsBackend
Contract for vars storage backends. A backend determines where and how generated files are stored and retrieved, returning the final file paths.
Defined in nix/contracts/lib/contracts/definitions/vars-backend.nix.
varsBackend is the interface between the vars provider of
generateFiles and the storage of the generated files.
Application code does not declare a request: the vars module is the only
consumer, and declares want.vars.<instance> for each generateFiles request.
These modules register a provider. The name of a provider is the value
for defaultProviderName.
-
on-machine: the optionservices.vars-on-machineinnix/contracts/nixos/modules/services/security/vars-on-machine.nix.On-machine storage backend for vars. Stores generated files directly on the filesystem.
-
openbao: the optionservices.vars-openbaoinnix/contracts/nixos/modules/services/security/vars-openbao.nix.OpenBao storage backend for vars. Secrets are generated locally and uploaded to an OpenBao KV v2 mount. At boot, a shared
openbao agentfetches them into a tmpfs directory.
To use this contract, select the backend:
{
contracts.varsBackend.defaultProviderName = "on-machine";
}
To write a backend, give the final path of each file in the request:
fulfill' =
{ name, request, ... }:
{
files = lib.mapAttrs (fileName: file: {
path = "/var/lib/mybackend/${name}/${fileName}";
}) request.files;
};
Options of varsBackend
varsBackend.request.files
Files to be stored by the backend, with their metadata. Keys are file names matching those declared in the generateFiles request.
Type: attribute set of (submodule)
Declared by:
varsBackend.request.files.<name>.group
Unix group that must own the file.
Type: string
Default:
"root"
Declared by:
varsBackend.request.files.<name>.mode
File permissions as an octal string.
Type: string
Default:
"0400"
Declared by:
varsBackend.request.files.<name>.owner
Unix user that must own the file.
Type: string
Default:
"root"
Declared by:
varsBackend.request.files.<name>.secret
Whether the file contains sensitive data.
Type: boolean
Default:
true
Declared by:
varsBackend.result.files
Final provisioned paths per file.
Type: attribute set of (submodule)
Declared by:
varsBackend.result.files.<name>.path
Path to the provisioned file on the target system.
Type: string
Declared by: