Configuration
LatchGate is configured through a latchgate.toml file. A full example is at latchgate.example.toml.
Generating a starter config
Section titled “Generating a starter config”latchgate init # interactive wizard (preset picker)latchgate init --preset coding # specific preset, skip wizardlatchgate init --preset lockdown --location user # user-global installSee Presets for available presets and their security postures.
Configuration precedence
Section titled “Configuration precedence”Configuration values are resolved in this order (highest wins):
- Environment variables (
LATCHGATE_*) — highest priority - TOML file (
latchgate.toml) - Compiled defaults — lowest priority
This allows operators to override any setting via environment variables without modifying the TOML file. LatchGate Platform uses this to configure per-tenant instances via env at process start time.
Invalid environment variable values are a hard startup error (fail-closed). An operator who sets LATCHGATE_LEASE_TTL_SECONDS=abc gets a startup failure, not a silent fallback to the default.
Transport
Section titled “Transport”LatchGate exposes two sockets: a client socket (for agent processes) and an admin socket (for operator tools). They are separate so agent processes cannot reach approval, audit, or revocation endpoints.
# Primary: Unix Domain Socket (production default)listen_uds_path = "/run/latchgate/gate.sock"listen_admin_uds_path = "/run/latchgate/gate-admin.sock"
# Optional TCP (dev only). Requires the unsafe flag.# listen_http_addr = "127.0.0.1:3000"# listen_admin_http_addr = "127.0.0.1:3001"# unsafe_expose_http = falseProduction deployments should use UDS-only transport. The TCP listener is gated behind unsafe_expose_http = true, which is recorded in audit events.
Admin mTLS (managed deployments)
Section titled “Admin mTLS (managed deployments)”When all three TLS fields are set, the admin TCP listener uses mutual TLS. This does not require unsafe_expose_http = true — mTLS is a production-grade transport.
admin_tls_cert = "/certs/server.crt"admin_tls_key = "/certs/server.key"admin_tls_ca = "/certs/ca.crt"Both sides must present certificates signed by the same CA. Clients without a valid certificate are rejected at the TLS handshake — before any application logic runs.
Setting one or two fields but not all three is a hard startup error (fail-closed). LatchGate will not silently fall back to plain HTTP on a partially configured admin listener.
In managed deployments these are typically set via environment variables (LATCHGATE_ADMIN_TLS_CERT, LATCHGATE_ADMIN_TLS_KEY, LATCHGATE_ADMIN_TLS_CA).
Identity
Section titled “Identity”The [identity] section controls how callers are authenticated at lease issuance.
peercred (production default)
Section titled “peercred (production default)”Uses SO_PEERCRED on the Unix socket to map the caller’s UID to a principal and scope set.
Manage principals from the operator TUI (Setup → Principals, 6 → 3): press a to add a principal — the form prompts for UID, name, scopes, and owner — and r to remove the selected one.
CLI equivalent
latchgate config add-principal --uid 1001 --name agent-jira \ --scopes tools:call --owner alice@company.comlatchgate config remove-principal --uid 1001See CLI Reference.
The TUI and CLI both edit the [identity.peercred.principals] table, which can also be written by hand:
[identity]provider = "peercred"
[identity.peercred]allow_unmapped = false # deny UIDs not in the map
[identity.peercred.principals]1001 = { principal = "agent-jira", scopes = ["tools:call"], owner = "alice@company.com" }1002 = { principal = "agent-email", scopes = ["tools:call", "email:send"], owner = "bob@company.com" }The optional owner field records which person or team is responsible for the agent principal. It is informational — LatchGate does not enforce it — but it is useful for forensic attribution and organizational mapping. Existing configs without owner continue to work.
none (dev only)
Section titled “none (dev only)”Returns a synthetic dev:anonymous principal. Rejected at production startup unless unsafe dev mode is active (requires both the unsafe-dev Cargo feature and LATCHGATE_UNSAFE_DEV=1).
Operator credentials
Section titled “Operator credentials”Approval and admin endpoints require operator authentication. Production requires named credentials with DPoP proof-of-possession.
Manage operators from the operator TUI (Setup → Operators, 6 → 2): press a to add an operator — this generates a DPoP keypair, writes the PEM, and updates TOML in one step, displaying the API key once — and r to remove the selected one.
CLI equivalent
latchgate config add-operator --name alice # generates keypair, writes PEM, updates TOMLlatchgate config remove-operator --name aliceFor manual keypair generation, use latchgate operator keygen.
Both paths write to the [operator_credentials] table:
[operator_credentials.alice]api_key = "key-alice-random-secret"dpop_jkt = "base64url-sha256-thumbprint"
[operator_credentials.bob]api_key = "key-bob-random-secret"dpop_jkt = "base64url-sha256-thumbprint"In dev mode, a single credential without dpop_jkt is accepted. The CLI auto-discovers it — no --operator-key flag needed. Production requires named credentials with DPoP.
Signing keys
Section titled “Signing keys”receipt_signing_key_path = "/etc/latchgate/receipt-signing.key"grant_signing_key_path = "/etc/latchgate/grant-signing.key"receipt_keys_jwks_path = "/etc/latchgate/receipt-keys.jwks"Ed25519 key files are auto-generated on first run (32-byte seed, mode 0600). Back these up. The JWKS file accumulates all historical receipt verifying keys so old receipts remain verifiable after rotation.
State and policy backends
Section titled “State and policy backends”redis_url = "redis://127.0.0.1:6379"opa_url = "http://127.0.0.1:8181"Both are optional. When omitted:
- No
redis_url: the gate uses SQLite for budgets and approvals, and a bounded in-memory DashMap (100k entries) for replay protection. Production-grade for single-instance deployments. - No
opa_url: the gate uses the embedded regorus engine (Rego evaluation in-process). Same policies, same data, no network dependency.
Set these when you need HA replay protection (Redis), external policy management (OPA), or multi-instance coordination.
What Redis stores (when configured)
Section titled “What Redis stores (when configured)”Redis holds four categories of operational state: DPoP jti replay cache entries (short TTL), per-session budget counters (call count, keyed by session ID), pending approval records (short TTL), and the revocation epoch counter.
Redis persistence (when configured)
Section titled “Redis persistence (when configured)”If Redis restarts without persistence, the replay cache and budget counters reset. This means: previously-seen jti values could be replayed within the replay TTL window (short-lived, low risk), budget counters reset to zero (sessions get a fresh budget — operationally noisy but not a security breach), and pending approvals are lost (agents must re-submit). The revocation epoch resets to 0, which is safe — new leases carry the new epoch.
For production with Redis, configure at minimum appendonly yes (AOF). The --infra Docker stack uses --save "" --appendonly no (volatile) — this is intentional for dev/eval only.
# Redis persistence recommendation for production:# In your redis.conf (not in latchgate.toml):# appendonly yes# appendfsync everysecSecrets (SOPS)
Section titled “Secrets (SOPS)”LatchGate decrypts secrets from a SOPS-encrypted file at each action execution and injects only the secrets declared in the action manifest (least privilege). Undeclared keys in the SOPS file are discarded.
# Path to the SOPS-encrypted secrets file.# When set, the Gate decrypts this file at each action call and injects# only the secrets declared in the action manifest.# When unset, actions that declare secrets receive an empty env map.[secrets]sops_secrets_file = "/etc/latchgate/secrets.enc.yaml"
# Optional: path to an age key file for SOPS decryption.# When set, exported as SOPS_AGE_KEY_FILE when invoking SOPS.# When unset, SOPS uses its default key discovery.sops_key_file = "/etc/latchgate/sops-age.key"Secrets are decrypted at each action execution and filtered to only the manifest-declared keys (least privilege). See Secrets Management for setup, encryption backends, and the full security model.
Filesystem root
Section titled “Filesystem root”# Root directory for fs_read, fs_write, fs_delete operations.# All paths in manifests and learned paths are resolved relative to this directory.# Actions that declare filesystem operations will fail at dispatch with# "no fs_root_path configured" if this is unset.# fs_root_path = "/home/agent/project"
# Allowed prefixes for per-session filesystem roots.# MCP sessions request their root at lease time (from IDE project CWD).# The gate validates the requested path starts with one of these prefixes.# Default: [$HOME]. Empty list disables per-session roots entirely.# fs_root_allowed_prefixes = ["/home/user/projects", "/tmp/sandboxes"]When fs_root_path is set, all filesystem provider operations are confined to this directory tree. Relative paths in action manifests and learned paths are resolved against fs_root_path. The gate refuses to resolve paths that escape this root via .. traversal.
When unset, filesystem actions fail at dispatch time — there is no fallback to “allow everything”. This is intentional: filesystem access must be explicitly configured.
fs_root_allowed_prefixes controls which directories MCP sessions may use as per-session filesystem roots. When an MCP adapter requests a session-scoped fs_root at lease time (typically the IDE’s project directory), the gate checks that the requested path starts with one of these prefixes. If not, the lease is issued without a session root and the global fs_root_path applies. Defaults to [$HOME]; set to an empty list to disable per-session roots entirely.
Host I/O clients
Section titled “Host I/O clients”Infrastructure connection strings for provider I/O. Each backend is a named section under [host_io] with at least a url field. Omitting a backend disables the corresponding latchgate:io/* import — actions that declare it will fail at runtime.
[host_io.database]url = "postgres://user:pass@localhost:5432/latchgate"
[host_io.queue]url = "amqp://user:pass@localhost:5672/%2f"
[host_io.storage]url = "s3://my-artifact-bucket"
[host_io.smtp]url = "smtp://user:pass@smtp.example.com:587"Connection strings (including credentials) are operator-managed and set at startup. Providers cannot influence which server is targeted — only the operation parameters are provider-controlled and validated against allowed_sinks.
In v0.1 only the http_api provider ships. The database, queue, storage, and smtp backends are accepted by the parser for forward compatibility but have no effect — the providers that consume them are not yet built. Configuring them now is harmless but does not enable those actions.
Egress proxy
Section titled “Egress proxy”For defense-in-depth, configure a forward proxy (Squid) for outbound HTTP from WASM providers. This enforces a domain allowlist independently of the kernel’s validate_sink() check.
# Forward proxy for outbound HTTP from WASM providers.# SECURITY: strongly recommended in production.# egress_proxy_url = "http://squid:3128"When set, all outbound HTTP requests from providers are routed through this proxy. The proxy enforces its own domain allowlist (deploy/squid/allowed_domains), providing a second layer of egress control beyond the kernel’s sink validation.
Runtime allowlist narrowing
Section titled “Runtime allowlist narrowing”Per-deployment operators can further narrow the effective allowlist without changing manifests:
# Intersect manifest allowlists with this operator-controlled set.# Can only REMOVE domains from the manifest set — never add.# egress_runtime_allowlist = ["api.github.com", "hooks.slack.com"]The effective allowlist the kernel enforces is manifest_domains ∩ egress_runtime_allowlist. A domain absent from a manifest cannot become reachable through this field — it is a narrowing lens, not an expansion. Typical use: deploying a subset of actions and limiting network reach to only the domains those actions actually need, or constraining a CI environment to a known-safe set.
SECURITY: operator-controlled only. Agent or caller input must never flow into this field — it is a deployment-time infrastructure constraint.
This field is TOML-only — it cannot be overridden via environment variable. Modify latchgate.toml directly or use latchgate config set.
Live allowlist file and reload
Section titled “Live allowlist file and reload”When a Squid sidecar enforces egress, the gate can maintain a live allowlist file for Squid to read:
# Atomic write target for the effective allowlist (manifest ∪ learned ∩ runtime).# Rewritten on startup, after `domains add/remove/clear`, and after a# domain is learned through the approval flow.# egress_live_allowlist_path = "/etc/squid/allowed_domains.txt"
# Command executed after each rewrite. Typical values:# "squid -k reconfigure"# "docker compose exec squid squid -k reconfigure"# Leave empty to reload manually or via an external file-watcher.# egress_reload_command = ""The effective domain set written to this file is (manifest_domains ∪ learned_domains) ∩ egress_runtime_allowlist. The file is rewritten atomically (tmp => fsync => rename) on three occasions: server startup (initial sync after registry and ledger are ready), after latchgate domains add/remove/clear (CLI-driven ledger mutation), and after a domain is learned through the approval flow. If egress_reload_command is set, it is executed after each successful rewrite.
Leases
Section titled “Leases”lease_ttl_seconds = 300 # default lease lifetime (5 min)Webhooks
Section titled “Webhooks”Outbound webhook notifications for security events. Configure zero or more endpoints — each subscribes to specific event types with independent retry policies.
Webhook delivery mode controls how events are persisted and delivered:
# Delivery mode: "outbox" (default) or "async"# outbox: events persisted to SQLite before delivery — zero event loss# async: in-process channel, fire-and-forget (dev only)webhook_mode = "outbox"[[webhooks]]name = "slack-approvals"url = "https://hooks.slack.com/services/T.../B.../xxx"secret = "whsec_your-signing-secret-here"events = ["approval.pending", "approval.expired"]
[[webhooks]]name = "security-siem"url = "https://siem.corp.internal/api/v1/events"secret = "whsec_siem-secret"events = ["action.denied", "revocation", "action.executed"]headers = { "Authorization" = "Bearer ${SIEM_TOKEN}" }Generate signing secrets with openssl rand -hex 32. HTTPS is required in production — HTTP is allowed only for localhost in dev mode. Header values support ${ENV_VAR} expansion.
For the full field reference, event types, payload format, signing verification examples, and integration guides (Slack, Teams, Discord, PagerDuty, Opsgenie, SIEM), see Webhooks.
Evidence ledger
Section titled “Evidence ledger”ledger_db_path = "data/audit.db" # SQLite databaseledger_jsonl_path = "data/audit.jsonl" # optional JSONL for SIEM exportResponse schema enforcement
Section titled “Response schema enforcement”response_schema_enforcement = "deny" # "deny" (production) or "warn" (onboarding)In "deny" mode (production default), responses that fail schema validation are rejected. In "warn" mode, the violation is logged but the response is returned to the caller. Use "warn" only during initial onboarding to identify schema mismatches — switch to "deny" before production.
Registry and providers
Section titled “Registry and providers”manifests_dir = "definitions/manifests" # action manifest YAML fileswasm_providers_dir = "target/providers" # compiled .wasm provider modulesManifests and providers are loaded from these directories on startup. The defaults are relative to the working directory. For Docker deployments, these are set to /opt/latchgate/definitions/manifests and /opt/latchgate/providers respectively.
Logging
Section titled “Logging”log_level = "info" # trace, debug, info, warn, errorlog_format = "auto" # auto (default), json (production), pretty (dev)# log_file = "/var/log/latchgate/gate.log" # optional JSON-lines log file# log_rotation = "daily" # daily (default), hourly, never — only when log_file is set# log_max_files = 7 # rotated files to keep (default: 7)auto selects pretty when running in an interactive terminal (TTY), and json otherwise. Production deployments should use json explicitly for structured log aggregation.
When log_file is set, every log event is appended as a JSON line to that file regardless of log_format. The file is created if it does not exist; the parent directory must exist. latchgate up sets this automatically; latchgate serve defaults to stderr only.
The log level can also be overridden at runtime via the RUST_LOG environment variable (standard tracing/env_filter behavior). The LATCHGATE_LOG_LEVEL env var overrides the log_level config field.
public_base_url = "http://localhost:3000" # canonical URL for htu computationThe public_base_url is used for DPoP htu (HTTP URI) claim validation. It must match the URL that clients use to reach the gate. Never derive this from the Host header.
Sandbox
Section titled “Sandbox”Two independent sandboxing layers are configured here: WASM provider isolation and agent process containment.
WASM provider sandbox
Section titled “WASM provider sandbox”[sandbox]mode = "strict" # "strict" or "degraded_ok"strict_for_actions = true # enforce minimum hardening for action containersstrict mode (recommended for CI/production) requires seccomp + no-new-privileges. AppArmor or SELinux is required on Linux. degraded_ok mode is for development hosts lacking AppArmor/SELinux — it records sandbox_degraded=true in audit events.
Agent process sandbox
Section titled “Agent process sandbox”The [sandbox.agent] section configures latchgate sandbox — Linux namespace containment for the agent process itself. These values serve as defaults; CLI flags override them.
[sandbox.agent]workspace = "/home/dev/myproject" # Host dir mounted as /workspace (RW). Default: CWD.allow_hosts = [ # Proxy allowlist — HTTPS only, port 443. "api.anthropic.com", "api.openai.com", "generativelanguage.googleapis.com",]ro_mounts = ["/opt/node-22"] # Extra read-only bind mounts.pass_env = ["TERM", "LANG", "ANTHROPIC_API_KEY"] # Env vars to pass through.gate_socket = "/run/latchgate/gate.sock" # Gate UDS path on host.For the full security model, platform requirements, and namespace details, see Agent Sandbox.
Rate limiting
Section titled “Rate limiting”The [rate_limits] section configures five independent token-bucket limiters:
[rate_limits]lease_rps = 50 # POST /v1/leases (default: 50)operator_write_rps = 20 # operator write endpoints (default: 20)operator_read_rps = 100 # operator read endpoints (default: 100)execute_rps_per_session = 20 # execute path, per session (default: 20)execute_rps_anonymous = 5 # execute path, unauthenticated peers (default: 5)lease_rps—POST /v1/leases(default 50 rps). DPoP verification is CPU-heavy; this caps CPU DoS risk. Shared per process.operator_write_rps— operator write endpoints: approve, deny, drain, revoke (default 20 rps). Shared per process.operator_read_rps— operator read endpoints: list approvals, get approval, audit query, status (default 100 rps). Shared per process.execute_rps_per_session—POST /v1/actions/{id}/execute, per session (default 20 rps). Each session (identified from the lease JWT payload) gets its own bucket. Applied before DPoP verification — a misbehaving agent cannot burn CPU on cryptographic checks without hitting its bucket first.execute_rps_anonymous— execute path for requests without a parseable session hint (default 5 rps). Per-peer buckets (keyed by UID on UDS, remote IP on TCP). Lower than session rate because anonymous requests are suspicious.
Each bucket has capacity equal to its rps and refills to full in one second. Requests over the limit return 429 with {"error": "rate_limit_exceeded"}. Tune downward for hardened deployments; the limiters are a brute-force / DoS mitigation, not a substitute for authentication or policy.
Environment variables
Section titled “Environment variables”Control variables
Section titled “Control variables”These variables control startup behavior and are not config field overrides:
| Variable | Effect |
|---|---|
LATCHGATE_UNSAFE_DEV=1 | Bypass all production startup checks. Requires the binary to be compiled with --features unsafe-dev. Both gates must be satisfied — the env var alone has no effect on a release binary. See Unsafe dev mode. |
LATCHGATE_CONFIG | Override config file path (alternative to --config flag) |
RUST_LOG | Override tracing log filter at runtime (standard tracing behavior) |
Config field overrides
Section titled “Config field overrides”Config fields can be overridden via a LATCHGATE_* environment variable. Env vars take precedence over TOML values. Invalid values cause a hard startup error (fail-closed).
| Variable | Config field | Type | Notes |
|---|---|---|---|
LATCHGATE_LOG_LEVEL | log_level | string | trace, debug, info, warn, error |
LATCHGATE_LOG_FORMAT | log_format | enum | auto, json, or pretty |
LATCHGATE_LOG_FILE | log_file | string | empty = unset (stderr only) |
LATCHGATE_LOG_ROTATION | log_rotation | enum | daily, hourly, or never |
LATCHGATE_LOG_MAX_FILES | log_max_files | usize | rotated files to keep (default: 7) |
LATCHGATE_LISTEN_UDS_PATH | listen_uds_path | string | |
LATCHGATE_LISTEN_ADMIN_UDS_PATH | listen_admin_uds_path | string | |
LATCHGATE_LISTEN_HTTP_ADDR | listen_http_addr | socket addr | e.g. 127.0.0.1:3000 |
LATCHGATE_LISTEN_ADMIN_HTTP_ADDR | listen_admin_http_addr | socket addr | e.g. 127.0.0.1:3001 |
LATCHGATE_UNSAFE_EXPOSE_HTTP | unsafe_expose_http | bool | true/false/1/0 only |
LATCHGATE_ADMIN_TLS_CERT | admin_tls_cert | string | empty = unset. All three admin TLS fields must be set together or not at all. |
LATCHGATE_ADMIN_TLS_KEY | admin_tls_key | string | empty = unset. Partial configuration is a startup error. |
LATCHGATE_ADMIN_TLS_CA | admin_tls_ca | string | empty = unset. CA used to verify both server and client certificates. |
LATCHGATE_PUBLIC_BASE_URL | public_base_url | string | |
LATCHGATE_REDIS_URL | redis_url | string | |
LATCHGATE_OPA_URL | opa_url | string | |
LATCHGATE_LEASE_TTL_SECONDS | lease_ttl_seconds | u64 | |
LATCHGATE_MANIFESTS_DIR | manifests_dir | string | |
LATCHGATE_WASM_PROVIDERS_DIR | wasm_providers_dir | string | |
LATCHGATE_LEDGER_DB_PATH | ledger_db_path | string | |
LATCHGATE_LEDGER_JSONL_PATH | ledger_jsonl_path | string | empty = unset |
LATCHGATE_RECEIPT_SIGNING_KEY_PATH | receipt_signing_key_path | string | empty = unset |
LATCHGATE_GRANT_SIGNING_KEY_PATH | grant_signing_key_path | string | empty = unset |
LATCHGATE_RECEIPT_KEYS_JWKS_PATH | receipt_keys_jwks_path | string | empty = unset |
LATCHGATE_RESPONSE_SCHEMA_ENFORCEMENT | response_schema_enforcement | enum | deny or warn |
LATCHGATE_SOPS_KEY_FILE | sops_key_file | string | empty = unset |
LATCHGATE_SOPS_SECRETS_FILE | sops_secrets_file | string | empty = unset |
LATCHGATE_HOST_IO_DATABASE_URL | host_io.database.url | string | empty = disable backend |
LATCHGATE_HOST_IO_QUEUE_URL | host_io.queue.url | string | empty = disable backend |
LATCHGATE_HOST_IO_STORAGE_URL | host_io.storage.url | string | empty = disable backend |
LATCHGATE_HOST_IO_SMTP_URL | host_io.smtp.url | string | empty = disable backend |
LATCHGATE_OPERATOR_API_KEY | operator_credentials (platform) | string | sets a single “platform” credential |
LATCHGATE_OPERATOR_DPOP_JKT | operator_credentials (platform) | string | DPoP thumbprint for platform credential |
LATCHGATE_FS_ROOT_ALLOWED_PREFIXES | fs_root_allowed_prefixes | colon-separated paths | empty = disable per-session roots |
For Option<String> fields (marked “empty = unset”), setting the env var to an empty string (LATCHGATE_LEDGER_JSONL_PATH="") unsets the field (maps to None). For Host I/O backends, an empty value disables the backend (LATCHGATE_HOST_IO_DATABASE_URL="" removes the database backend).
Boolean fields accept only true, false, 1, 0 (case-insensitive). Ambiguous values like yes or on are rejected.
Fields without env var overrides
Section titled “Fields without env var overrides”The following configuration fields are TOML-only — they cannot be overridden via environment variables:
webhook_mode— webhook delivery mode (outboxorasync)egress_live_allowlist_path— Squid live-sync file pathegress_reload_command— command to run after allowlist rewriteegress_runtime_allowlist— operator-controlled domain narrowing setfs_root_path— filesystem root for provider I/Osandbox.mode— sandbox strictness levelsandbox.strict_for_actions— minimum hardening enforcementsandbox.agent.*— agent process sandbox configurationrate_limits.*— token-bucket capacities (lease_rps,operator_write_rps,operator_read_rps,execute_rps_per_session,execute_rps_anonymous)
Modify latchgate.toml directly or use latchgate config set.
Security constants
Section titled “Security constants”The following values are compile-time constants in latchgate-core/src/security_constants.rs. They are not configurable via TOML or environment variables. Each was previously a config field whose only safe value was the default; exposing them invited misconfiguration with no legitimate upside.
| Constant | Value | Purpose |
|---|---|---|
REPLAY_TTL_SECS | 180 | Anti-replay cache TTL. Tied to allowed_clock_skew + max_proof_age. |
OPA_TIMEOUT_MS | 1000 | Policy evaluation timeout (ms). Timeout fires => DENY. |
MAX_LEASE_TTL_SECS | 3600 | Hard ceiling on lease lifetime. lease_ttl_seconds cannot exceed this. |
APPROVAL_TTL_SECS | 300 | Pending approval expiry. Unapproved requests are auto-denied after this window. |
MAX_CONCURRENT_EXECUTIONS | 4 | WASM concurrency semaphore permits. |
MAX_REQUEST_BODY_BYTES | 1 048 576 | Transport-level HTTP body size limit (1 MB). |
REDIS_KEY_PREFIX | latchgate:jti: | Redis key namespace prefix. |
SOPS_BIN | sops | Path to the SOPS binary. |
SOPS_CACHE_TTL_SECS | 30 | In-memory decrypted secrets cache TTL. |
FS_ROOT_BLOCKED_PATHS | 21 system paths | Paths never valid as a per-session fs_root, regardless of fs_root_allowed_prefixes. Blocks /, /bin, /boot, /dev, /etc, /home, /lib, /lib64, /media, /mnt, /opt, /proc, /root, /run, /sbin, /snap, /srv, /sys, /tmp, /usr, /var. Defense-in-depth for misconfigured allowlists. |
SESSION_FS_ROOT_EVICTION_GRACE_SECS | 60 | Grace period added to lease TTL before evicting a session’s fs_root. Prevents disruption of in-flight executions at lease expiry. |
If a deployment genuinely requires a different value, the constant must be changed at the source level and the binary recompiled.
Unsafe dev mode
Section titled “Unsafe dev mode”Dev mode bypasses all production startup checks. It is double-gated to prevent accidental activation:
- Compile-time gate: the binary must be compiled with
--features unsafe-dev. Release binaries published to GHCR and Homebrew do not include this feature —LATCHGATE_UNSAFE_DEVhas no effect on them. - Runtime gate:
LATCHGATE_UNSAFE_DEV=1(ortrue) must be set in the environment.
Both gates must be satisfied. If the binary lacks the feature, the env var is ignored. If the feature is compiled in but the env var is absent, dev mode is inactive.
When active, the gate emits a loud ERROR-level log line at startup: UNSAFE DEV MODE ACTIVE. If this line appears in a production log, the build is wrong.
latchgate up uses a binary compiled with the unsafe-dev feature and sets LATCHGATE_UNSAFE_DEV=1 internally. This is the intended way to run in dev mode for evaluation and development. Never deploy a binary compiled with --features unsafe-dev.
Production startup checks
Section titled “Production startup checks”Outside unsafe dev mode, the server refuses to start if:
- Identity provider is
none allow_unmapped = trueon peercred provider- Empty peercred principal map
- Signing keys are ephemeral (paths not configured)
- Receipt JWKS path is missing
- Operator credentials missing or missing
dpop_jkt unsafe_expose_httpset without the flagresponse_schema_enforcement = "warn"- Actions declare
egress_profile = proxy_allowlistbutegress_proxy_urlis not configured file://object storage URL in productionwebhook_mode = asyncwith configured endpoints- Wildcard ACL contains high/critical-risk actions
These exist to prevent insecure configurations from reaching production.
Security posture
Section titled “Security posture”Each production check can be individually relaxed under a [posture] section. Every field defaults to false (the check is enforced). Setting a field to true disables exactly one check — the rest stay enforced. This is how the generated config records the relaxations applied by latchgate up, so the posture survives restarts and is inherited by child processes.
[posture]identity_insecure = false # skip caller identity validation (allow provider = none)signing_insecure = false # skip persistent signing key / JWKS validation (ephemeral keys)schema_insecure = false # allow response_schema_enforcement = "warn"operator_auth_insecure = false # skip operator DPoP proof-of-possession validationstorage_insecure = false # allow file:// object storagewebhooks_insecure = false # allow webhook_mode = "async" with endpointsegress_insecure = false # skip egress proxy coverage validationacl_insecure = false # skip wildcard ACL risk validationPrefer the latchgate up flags over editing this section by hand — they set the same fields and surface the relaxation in the startup posture banner. The flags map directly:
| Flag | Field |
|---|---|
--insecure-identity | identity_insecure |
--insecure-signing | signing_insecure |
--schema-warn | schema_insecure |
--insecure-operator-auth | operator_auth_insecure |
--insecure-storage | storage_insecure |
--insecure-webhooks | webhooks_insecure |
--insecure-egress | egress_insecure |
--insecure-acl | acl_insecure |
Any relaxed field weakens a production guarantee. Set these only for local development with no untrusted network exposure, never in a deployed gate.
For production hardening and UDS transport setup, see Deployment. For a first-run walkthrough, see Getting Started. For the full CLI reference, see CLI Reference. For secrets setup, see Secrets Management.