Configuration¶
Configuration is loaded before the HTTP service starts. If a value is wrong, Gulo Gulo stops rather than guessing. That is useful in production: a typo in a secret reference or a security invariant should never quietly turn into a different deployment.
The contract¶
The normative machine-readable schema is config/schema.v1.json. The
implementation lives in src/runtime/config.ts and exposes two entry points:
loadConfiguration()returns the complete, frozen contract;loadConfig()keeps the small M0 runtime shape (host,port,serviceName,environment, andshutdownTimeoutMs) while attaching the complete contract as non-enumerable metadata.
The contract is versioned with schemaVersion: 1. It also carries the build
version and build digest used by health responses and structured logs.
Precedence¶
Values are resolved in this order:
- environment variables;
- the JSON file mounted at
GULOGULO_CONFIG_FILE; - safe built-in defaults.
The default path /etc/gulogulo/config.json is optional. A path explicitly set
through GULOGULO_CONFIG_FILE is mandatory: a missing or malformed file fails
closed. The mounted file is limited to 1 MiB and rejects unknown keys.
Canonical GULOGULO_* names win over the short compatibility names when both
are present. For example, GULOGULO_PORT wins over PORT.
Safe defaults and fixed policy¶
The defaults keep the empty scaffold useful without external services:
host: 0.0.0.0
port: 8080
environment: development
identity source: ldap (standalone may switch to database)
LDAP: disabled
PostgreSQL: disabled
upstream Plesk/cPanel tenant tool: disabled
IMAP IDLE: enabled
catch-all: false
automatic user forwarding: false
SMTP inbound: 25/TCP
SMTP submission: 587/TCP (465/TCP optional)
IMAPS: 993/TCP
scanner failure mode: fail_closed
mailbox root: /var/lib/gulogulo/mail
trash retention: 28 days
API/MCP: read-only
upgrade strategy: in_place
patching mode: build_and_operator
alerting: disabled
See Identity and PostgreSQL for identity.source
and Runtime and observability for alerting.*.
The parser rejects attempts to enable catch-all, user forwarding, or a
write-capable API. Enabling LDAP requires ldap.bindDn, ldap.bindSecretRef,
and ldap.userBaseDn; enabling PostgreSQL requires postgres.dsnSecretRef.
The M3 mail limits (maxMessageBytes, maxRecipients, submission rate, and
queue retry settings) are bounded integers. The Rspamd and ClamAV flags describe
required adapter wiring; they do not place a scanner credential or endpoint in
the configuration file. A scanner failure cannot be configured as permissive.
The full mail behavior, adapter interfaces, queue states, Sieve rules, and vendor-wiring checklist are in M3 mail core.
Optional upstream Plesk or cPanel¶
The optional controlPanel section describes a tenant's upstream hosting
panel. Supported provider values are plesk, cpanel, and none (the default).
When enabled, it requires an HTTPS baseUrl, an external accountRef, and a
credentialSecretRef; webhook and hybrid synchronization additionally
require a webhookSecretRef. Environment variables use the same names with
the CONTROL_PANEL_ prefix, for example:
CONTROL_PANEL_ENABLED=false
CONTROL_PANEL_PROVIDER=none
CONTROL_PANEL_BASE_URL=
CONTROL_PANEL_ACCOUNT_REF=
CONTROL_PANEL_CREDENTIAL_SECRET_REF=
CONTROL_PANEL_WEBHOOK_SECRET_REF=
CONTROL_PANEL_SYNC_MODE=pull
The panel may own its hosting account and, if the provider chooses, DNS
workflow. Gulo Gulo remains authoritative for tenant policy, identity,
PostgreSQL state, mailbox/DAV content, retention, and audit. The contract is
read-only and rejects embedded URL credentials, arbitrary writes, shell/SSH,
Docker-socket, and unrestricted kubectl access. See Optional Plesk and cPanel
integration for the binding and field runbook.
External scanner signature volume¶
Rspamd and ClamAV definition data is intentionally not stored in the package.
GULOGULO_SCANNER_SIGNATURE_ROOT,
GULOGULO_SCANNER_SIGNATURE_MAX_AGE_SECONDS, and
GULOGULO_LP3_SCANNER_SIGNATURES_VOLUME describe the provider-owned shared
volume and its freshness policy. The host-side updater is the single writer;
scanner containers mount the volume read-only and fail closed for missing,
stale, or invalid generations. See Shared scanner-signature volume.
Secrets¶
Do not put passwords, DSNs, tokens, cookies, or private keys in the JSON file
or in a normal environment variable. The contract accepts references such as
ldap-bind and postgres-dsn; the deployment platform resolves those
references separately. Configuration output, health responses, and logs never
echo the secret value.
The committed .env.example contains only empty reference fields and loopback
placeholders. Copy it for local work, but never commit a populated .env file.
Build metadata¶
Set these values from the package build or deployment pipeline:
$env:GULOGULO_VERSION = '0.1.8'
$env:GULOGULO_BUILD_DIGEST = 'sha256:example'
GULOGULO_BUILD_VERSION remains a compatibility alias for
GULOGULO_VERSION. Build metadata is safe to expose; it must not contain a
credential or a message identifier.
Troubleshooting¶
Run the compiled config tests directly when Windows process isolation prevents the Node test runner from spawning child workers:
npm run build:server
node dist/server/src/core/foundation/config.test.js
For a normal package run, use npm test. CI executes the same script with the
lockfile installed by npm ci.