Configuration overview 

    Basil runs from a TOML startup config that names three core inputs: a catalog (what keys exist and where), a policy (who may do what), and a sealed bundle (the credential that lets the broker reach the backend). Get these right and the rest of operations is routine. Basil validates them before serving, so mistakes surface at startup, not under traffic.

    The subcommands 

    The broker is one binary, basil, with these config/daemon subcommands (plus config init for first-run scaffolding):

    CommandPurpose
    basil config initFirst-run scaffolding: a starter catalog + least-privilege policy + config.
    basil agentRun the broker daemon.
    basil config checkPre-flight: validate catalog + policy, enforce capability requirements, and read-only probe the backend for declared keys.
    basil config explainOffline policy dry-run: "would this be allowed, and why?" See Policy explain.
    basil bundleCreate, update, verify, review, and promote sealed credential bundles. See Unlock & the sealed bundle.
    basil doctorPreflight environment diagnostics before the daemon starts. See Doctor.

    Startup config 

    Put daemon startup settings in a TOML file and start with basil agent --config /etc/basil/agent.toml. For deploy-time relocation and tests, only the config path and the catalog/policy/bundle/socket/vault address may be overridden by CLI or environment.

    catalog = "/etc/basil/catalog.json"
    policy = "/etc/basil/policy.json"
    bundle = "/var/lib/basil/bundle.sealed"
    socket = "/run/basil/agent.sock"
    socket-mode = "0660"
    socket-group = "basil-clients"
    vault-addr = "https://vault.example:8200"
    transit-mount = "transit"
    capability-policy = "strict"   # strict | degraded | off
    svid-ttl-secs = 300
    max-encrypt-size = 1048576
    max-payload-size = 1048576
    grace-versions = 1
    retention-sweep-secs = 3600
    # retain-versions = 30
    # audit-log = "/var/log/basil/audit.jsonl"
    # no-reconcile = false
    
    [unlock]
    age-yubikey = true
    # unlock-passphrase-file = "/run/basil/unlock-passphrase"
    # unlock-passphrase-no-wipe = true
    # bip39-phrase-file = "/run/secrets/basil-recovery.txt"
    # strict-bundle-perms = true
    
    [broker-identity]
    # id = "basil://prod/us-east-1/agent-a"
    # response-signing-key-id = "broker.response_signing.2026q3"
    
    [invocation]
    enable = false
    # audience = ["basil://prod/us-east-1/agent-a"]
    # request-encryption-key-id = "broker.request_encryption.2026q3"
    # max-ttl-secs = 60
    # clock-skew-secs = 30
    # replay-cache-capacity = 4096
    Config keyWhat it does
    catalogPath to the catalog JSON. Required unless supplied by override.
    policyPath to the policy JSON. Required unless supplied by override.
    bundlePath to the 0600 sealed bundle. Required unless supplied by override.
    socketUnix socket to listen on. Defaults to Basil's built-in socket path.
    socket-modeOctal Unix socket mode. Default 0600; use "0660" with socket-group for authorized local service users.
    socket-groupOptional group name or numeric gid applied to the socket before serving.
    vault-addrDefault backend address when a credential pins none. Default http://127.0.0.1:8200.
    transit-mountTransit engine mount path. Default transit.
    jwt-auth-mountJWT auth method mount for SpiffeSigner backend login. Default jwt.
    jwt-roleVault/OpenBao JWT role for SpiffeSigner. Required when any backend uses that credential type.
    jwt-audienceJWT-SVID audience for SpiffeSigner login. Default openbao.
    db-keystore-cipherDefault cipher for db-keystore backends when the sealed credential omits it. Requires a keystore-capable build.
    onepassword-provider-uriDefault 1Password provider URI when the sealed OnePassword credential leaves it empty. 1Password is in the default build.
    onepassword-projectDefault 1Password project. 1Password is in the default build.
    onepassword-profileDefault 1Password profile. 1Password is in the default build.
    svid-ttl-secsSVID lifetime (seconds) for broker self-login JWT-SVIDs and Workload API X.509-SVID issuance/refresh. Default 300.
    capability-policystrict (default) · degraded · off. See Capability & reconcile.
    no-reconcileSkip the startup key-existence reconcile. Escape hatch.
    audit-logAppend each authorization decision as JSONL to this file. See Audit logs.
    grace-versionsRotation grace window in key versions. Default 1; 0 = newest only. See Rotating keys.
    retain-versionsRetention floor; the sweep prunes archived versions below it. Omit to retain all.
    retention-sweep-secsSweep interval (seconds). Default 3600; 0 disables.
    max-encrypt-sizeCap on encrypt plaintext / decrypt ciphertext. Default 1 MiB.
    max-payload-sizeCap on set value / import material. Default 1 MiB.

    Socket access only controls who can connect; Basil still authorizes each RPC by peer credentials and policy. The [logging] section configures the stdout, journald, and opentelemetry log sinks (see Audit logs), and the opt-in [jwks] section publishes issuer public keys over HTTP when the binary is built with http support (see JWKS HTTP surface).

    Unlock config keys are covered in Unlock & the sealed bundle. Sealed invocation config is covered in Sealed invocations; the service is registered but disabled unless [invocation] enable = true, and enabling it without an audience, broker identity, response-signing key id, or request-encryption key id fails closed. Each accepted request must also name a valid requester response_encryption_key_id so Basil can return a signed, encrypted operation response.

    Allowed startup overrides 

    FlagEnvWhat it overrides
    -c, --configBASIL_CONFIGPath to the TOML startup config.
    --catalogBASIL_CATALOGPath to the catalog JSON. Required.
    --policyBASIL_POLICYPath to the policy JSON. Required.
    --bundleBASIL_BUNDLEPath to the 0600 sealed bundle. Required.
    --socketBASIL_SOCKETUnix socket to listen on.
    --vault-addrVAULT_ADDRDefault backend address when a credential pins none.
    ✅ Best practice

    Run basil config check --catalog … --policy … --bundle … in CI and before every deploy. It parses both files, confirms each backend can provide what the catalog requires, and probes the backend for declared keys. Add --require to exit non-zero when a required (missing=error) key is absent, turning a 3am surprise into a failed pipeline.

    📝 Note

    The catalog and policy are normally generated from your NixOS configuration (see Make it your own), which validates them a second time at build. The JSON files are the export Basil consumes; you rarely hand-edit them.

    Where to go next