Server configuration
Configure the datastore, network, cryptography, authentication cost, and backup policy.
Production configuration is explicit. Unknown HIKYO_* variables produce a
warning, while malformed security settings stop startup.
Server flags
| Flag | Default | Purpose |
|---|---|---|
--listen ADDR | 127.0.0.1:8080 | Public API/UI listener; HTTPS when a TLS pair is configured. |
--operational-listen ADDR | 127.0.0.1:8081 | Plaintext health, readiness, and metrics listener. |
--tls-cert-file PATH | none | PEM certificate chain; configure with the key file. |
--tls-key-file PATH | none | Matching PEM private key; mode 0400 or 0600. |
--root-key-file PATH | HIKYO_ROOT_KEY_FILE | Mode-0600, 64-hex-character root key; flag takes precedence. The environment path also supplies host-local custody commands. |
--auto-migrate=BOOL | true | Apply pending migrations before serving. |
--upgrade-unattended | off | Run explicitly enrolled unattended container upgrade preparation before serving; see container upgrades. |
--cli-socket PATH | HIKYO_CLI_SOCKET | Same-user Unix socket for local CLI traffic. |
--config-rollout-enrollment PATH | none | Operator-installed Kubernetes rollout enrollment JSON. |
--config-rollout-signing-key PATH | none | Separate private deployment-authorization signer; see configuration rollouts. |
--dev | off | Evaluation datastore, root key, and text logs. |
Do not use --dev for real secret material or any non-loopback deployment.
Production network baseline
| Variable | Meaning |
|---|---|
HIKYO_DB | Required: sqlite:PATH or postgres://... datastore. |
HIKYO_PG_POOL_MAX | Optional positive PostgreSQL connection-pool maximum. |
HIKYO_EXTERNAL_ORIGIN | Set explicitly to the canonical public HTTPS origin. |
HIKYO_TLS_CERT_FILE, HIKYO_TLS_KEY_FILE | Native TLS pair; both or neither. |
HIKYO_OPERATIONAL_LISTEN | Separate operational bind; default 127.0.0.1:8081. |
HIKYO_TRUSTED_PROXY_CIDRS | Alternative proxy mode for a non-loopback plaintext public listener. |
HIKYO_ADAPTER_EGRESS_POLICY_FILE | Optional startup-only adapter origin-to-CIDR exception policy, for any provider. |
HIKYO_ADAPTER_AWS_WORKLOAD_IDENTITY | allow lets AWS Secrets Manager adapters use this node’s own AWS identity (ambient, assume-role, web-identity). Default deny: only static access keys are accepted. |
HIKYO_SEALED_WEBHOOK_FILE | Optional startup-only registry of sealed webhook receivers; see below. |
The server’s public origin must use a hostname. WebAuthn 0.18 rejects IP-address
relying-party IDs, so an explicit IP-address HIKYO_EXTERNAL_ORIGIN now stops
startup with migration guidance. Use http://localhost:8080 for local
development and the instance’s HTTPS hostname for deployment. The listener may
still bind to an IP address.
When origin is unset, the default loopback and wildcard binds derive a
localhost origin with the listener’s port and TLS scheme. Other loopback
aliases and nonlocal IP binds need an explicit hostname origin that resolves to
the correct address. A hostname bind retains its hostname. Proxy mode must set
the public HTTPS origin explicitly because the private listener cannot infer it.
A derived origin does not count as an explicit public origin for registration.
Legacy managed snapshots that saved the exact listen-derived default are
recognized when bootstrap has no explicit origin; they derive the current
hostname default during adoption. A different stored IP origin is still refused.
A process origin statement matching the exact current hostname default counts
as explicit; unrelated or invalid stale seed values do not replace adopted
managed configuration. Fresh bootstrap IP statements are refused, including
ones that happen to match the listener.
Before upgrading an installation with an IP origin, use its existing binary to change the effective managed configuration to a hostname origin, then update any bootstrap setting. Configure and verify DNS and TLS for that hostname. If the new binary already refuses startup because the stored origin is an IP address, return to the previous binary to make this change; changing only the environment cannot replace an adopted managed value. Offline administration, migration and backup commands still accept the old IP setting, but do not rewrite it.
Changing the RP hostname affects passkey compatibility; follow the origin-change authentication requirements in managed configuration. This change does not migrate stored credentials to another RP.
Native TLS is the default direct-deployment posture. Hikyo validates the pair at
startup and rejects expired, not-yet-valid, or mismatched files. The bootstrap
pair is imported once into the managed node configuration and never watched:
SIGHUP refuses to reload it, and a replacement is published and applied as
the node certificate through managed configuration.
A failed candidate is reported by the apply job and never replaces the live
certificate; hikyo_tls_reload_failures_total stays 0 by construction.
Proxy mode requires trusted CIDRs when the plaintext public listener is not loopback. The list names proxies, not browser client networks. Operational routes never exist on the public router; keep the operational bind private.
For HIKYO_MAIL_* bootstrap inputs, managed mail settings and an explicit
delivery test, see signup mail. Provider client setup and
registration admission are covered by social sign-in.
Project key capacity
| Variable | Default | Meaning |
|---|---|---|
HIKYO_DEFAULT_KEYS_PER_PROJECT | 1000 | Declared-key limit inherited by projects without an override, from 1 to 10000; cannot exceed the instance maximum. |
HIKYO_MAX_KEYS_PER_PROJECT | 10000 | Operator-controlled ceiling for every project’s effective key limit, from 1 to 10000. |
On an unmanaged installation, these environment variables supply the initial
configuration. After adoption, edit the keys in the Hikyo configuration
project’s Matrix, Publish, and Apply the selected revision. An old environment
variable cannot override the managed settings. In Project settings > Policy >
Key capacity, choose Inherit instance default or a custom limit within
the instance maximum. For example, one project can use 5000 while other
projects inherit 1000.
The effective limit is the smaller of the saved override (or inherited default) and the instance maximum. Lowering the maximum retains a larger saved override and caps its effective limit; raising the maximum can restore it. Lowering a limit preserves existing keys and delivery, while refusing additional growth above the limit. The separate 100,000-cell project budget still applies. See Change the project key limit for the workflow, rationale and behavior when reducing the limit.
MCP endpoint
The MCP endpoint is disabled by default. These settings control its exact public exposure:
| Variable | Default | Meaning |
|---|---|---|
HIKYO_MCP_ENABLED | false | Enables the stateless JSON POST /mcp endpoint. |
HIKYO_MCP_ALLOWED_ORIGINS | empty | Comma-separated, exact browser origins admitted at /mcp. Wildcards, null, paths, queries, fragments, and credentials are rejected. |
HIKYO_MCP_WRITE_ENABLED | false | Installs the hikyo_stage_change and hikyo_validate_change tools. Requires HIKYO_MCP_ENABLED=true. Grants no authority: each call needs an edit grant on the environment and is audited. Nothing publishes or delivers over MCP. |
Production requires an HTTPS HIKYO_EXTERNAL_ORIGIN. Development permits
plain HTTP only on a loopback origin. Requests without an Origin header are
accepted for non-browser MCP clients. An allowed browser origin does not enable
CORS response headers, so it is an additional request check rather than a
cross-origin browser API grant.
Treat every configured MCP client and its model provider as a recipient of MCP tool results. The five read tools expose bounded key definitions, environments, configuration cells, caller-owned pending changes, and revision metadata. Results may include non-secret configuration plaintext and caller-owned non-secret draft plaintext. Secret values and secret draft material are never returned. Tool calls accept only an existing workload or automation service-account bearer and repeat current authorization on every page.
For Helm and Compose deployment, client profiles, least-privilege grants, rotation, public validation, and failure diagnosis, use the MCP operations runbook.
Security response headers
Every public response carries the same set, refusals and API errors included:
| Header | Value |
|---|---|
Content-Security-Policy | Self-only baseline, no inline script or style, frame-ancestors 'none'. Served documents extend connect-src with the configured remote origins only. |
X-Content-Type-Options | nosniff |
Referrer-Policy | no-referrer |
Cross-Origin-Opener-Policy | same-origin-allow-popups |
Cross-Origin-Resource-Policy | same-origin |
Permissions-Policy | camera=(), microphone=(), geolocation=() |
Strict-Transport-Security | max-age=31536000, conditional; see below. |
Cross-Origin-Opener-Policy is same-origin-allow-popups rather than
same-origin because the browser UI runs the identity-provider and workspace
handoff ceremonies in popups that navigate to another origin and then close
themselves; strict same-origin leaves those windows stranded open. The
popups are opened with noopener and answer over a same-origin
BroadcastChannel, so nothing reaches back through window.opener either way.
Permissions-Policy names no WebAuthn feature on purpose:
publickey-credentials-get and publickey-credentials-create default to
self, which is what passkey sign-in and reauthentication need.
HSTS
Strict-Transport-Security: max-age=31536000 is emitted when both hold:
HIKYO_EXTERNAL_ORIGINhas thehttpsscheme (explicitly set, or derived as an HTTPS hostname origin because a native TLS pair is configured), and- the external origin’s browser-visible host is not loopback.
The scheme of the configured public origin decides, not which process holds the
certificate, so a reverse proxy terminating TLS in front of the plaintext
listener emits it too, including when that internal listener binds to loopback.
A plain-http external origin and any loopback external-origin host never emit
it. A development instance must not pin localhost to https in the browser’s
HSTS store for every other project on the machine.
If your reverse proxy already adds Strict-Transport-Security, do not also let
it rewrite or append: two headers, or one with a shorter max-age, is a
policy the browser resolves by rules you did not intend. Either let Hikyo emit
it and pass the header through untouched, or strip Hikyo’s at the proxy and own
the value there, including includeSubDomains or preload, which Hikyo
deliberately does not send because they commit hostnames it does not serve.
Operational metrics
/metrics on the operational listener serves Prometheus text exposition
(text/plain; version=0.0.4) using the official Prometheus Go client. Scraping
requires no sidecar, external service, or egress. It is plaintext and
unauthenticated by design and restricted to actual loopback peers. IPv4/IPv6
non-loopback scrapes return HTTP 403; forwarded headers do not change that
decision. Keep the operational bind private and scrape from the host or a
collector in the same Kubernetes pod. The chart exposes no metrics Service;
configure the collector’s authenticated forwarding separately.
Alongside the retention and TLS gauges it exposes RED-style request metrics
keyed by a closed surface class: auth, hierarchy, values,
revisions, delivery, scim, admin, and a fail-closed other. There are
no raw paths and no identifiers in any label, so cardinality is bounded no
matter the traffic.
| Metric | Type | Labels | Meaning |
|---|---|---|---|
hikyo_http_requests_total | counter | class, status (2xx/3xx/4xx/5xx/other) | Requests per surface class and status bucket. A recovered panic counts as 5xx. |
hikyo_http_request_errors_total | counter | class, status (4xx/5xx) | Client and server errors per surface class. |
hikyo_http_requests_in_flight | gauge | none | Requests currently executing in the API stack. |
hikyo_http_request_duration_seconds | histogram | class | Request latency; fixed buckets 0.005, 0.025, 0.1, 0.5, 1, 5 seconds plus +Inf. |
hikyo_mcp_requests_total | counter | method, tool, status | MCP requests by closed protocol method, registered tool name, and status bucket. |
hikyo_mcp_requests_in_flight | gauge | none | MCP requests currently executing. |
hikyo_mcp_request_duration_seconds | histogram | method, tool | MCP request latency using the fixed HTTP bucket grid. |
hikyo_admission_concurrency_limit | gauge | none | Derived Argon2id verification slots. |
hikyo_admission_in_flight | gauge | none | Verification slots currently held. |
hikyo_admission_queue_depth_limit | gauge | none | Bound on simultaneous pre-auth waiters. |
hikyo_admission_queue_waiting | gauge | none | Waiters currently queued for a slot. |
hikyo_admission_active_backoffs | gauge | none | Accounts currently past the failure-backoff threshold. |
Counters cover all /api/v1/* traffic. Unmatched paths, unsupported methods,
and CORS preflights use the closed other class.
Under --dev the server also emits a per-request access log (method, surface
class, status, duration) at debug level. The production handler starts at Info,
so these Debug request lines are absent unless debug logging is enabled.
Sealed webhook receivers are listed in the JSON file named by
HIKYO_SEALED_WEBHOOK_FILE. Only origins listed here can be bound by a
project’s sealed-webhook adapter:
{
"instance_id": "hikyo-prod",
"signing_key_file": "sealed-webhook-signing.pem",
"targets": [{
"id": "payments-receiver",
"origin": "https://recv.example",
"recipient_key": "age1...",
"ack_key": "ed25519:...",
"generation": 1,
"timeout_seconds": 10,
"max_response_bytes": 65536,
"confirmed_fingerprint": "sha256:..."
}]
}signing_key_file is an Ed25519 PKCS#8 PEM key (openssl genpkey -algorithm ed25519), relative to this file, and must be mode 0600. Each target
activates only when confirmed_fingerprint matches the fingerprint the
receiver operator verified with you out of band. Unknown fields, a missing
fingerprint, a timeout above 10 seconds, or a response bound above 64 KiB
refuse startup. A private receiver address still needs an entry in
HIKYO_ADAPTER_EGRESS_POLICY_FILE.
Adapter egress is public-address-only by default. To approve a private adapter
origin (Forgejo or GitHub Enterprise Server), point HIKYO_ADAPTER_EGRESS_POLICY_FILE at a JSON object whose keys are
exact canonical bare HTTPS origins and whose values are CIDR lists:
{
"https://git.internal.example": ["10.40.0.0/24", "fd42:40::/64"]
}The file is read and validated only at startup. Origins must have no path, query, fragment, or user information. An exception applies only when both the configured origin and every dialed IP match the same entry. It does not disable TLS verification, redirects remain refused, and DNS answers are checked again when connecting.
Root key
Choose exactly one source:
--root-key-file PATHorHIKYO_ROOT_KEY_FILE, including a service-manager credential path.HIKYO_ROOT_KEY, the weakest delivery tier.
The key must be 64 hexadecimal characters. Keep it outside the datastore and outside the backup identity’s custody store.
Datastore
Production boot also requires a signed executable and authenticated offline
release bundle. Configure HIKYO_UPGRADE_BUNDLE,
HIKYO_UPGRADE_OPERATOR_PUBLIC_KEY and HIKYO_UPGRADE_STATE_DIR for the default
manual installation. See self-hosting and
manual upgrade inputs for provisioning
and populated-datastore evidence. Datastore and root-key settings alone are not
a complete production configuration.
SQLite uses HIKYO_DB=sqlite:/absolute/path/hikyo.db. Ensure the service user
owns the parent directory and no other user can read it. The write pool uses one
connection and the WAL read pool is capped at four, so concurrent readers cannot
open unlimited connections or retain unbounded WAL snapshots.
Remote PostgreSQL must name one host explicitly and use sslmode=verify-full
for certificate and hostname verification. CA-only sslmode=verify-ca,
multi-host DSNs and remote plaintext connections are refused. The connection-pool maximum defaults to 10.
Set HIKYO_PG_POOL_MAX to a positive 32-bit integer for larger hardware, or put
pool_max_conns=N in the PostgreSQL DSN. The environment variable takes
precedence when both are present. Hikyo logs the effective pool maximum once at
startup for either datastore.
Authentication cost controls
| Variable | Default | Meaning |
|---|---|---|
HIKYO_ARGON2_MEMORY_KIB | 65536 | Memory per Argon2id operation. |
HIKYO_ARGON2_TIME | 3 | Argon2id time cost. |
HIKYO_REAUTH_WINDOW_SECONDS | 0 (900 under --dev) | Instance-default disclosure reauthentication window. 0 means every reveal takes its own ceremony, which only a passkey can satisfy; a positive value lets an authenticator code open a sliding window. Environments override it in project settings; protected environments are always 0. |
HIKYO_ARGON2_PARALLELISM | 2 | Argon2id parallelism. |
HIKYO_ADMISSION_BUDGET_MIB | 272 | Process-wide memory admission budget. |
Tune these together under representative load. A malformed value is a startup error, not a fallback to the default.
Update notifications
| Variable | Default | Meaning |
|---|---|---|
HIKYO_UPDATE_CHANNEL | stable | Server-wide WebUI notification track: stable, nightly, or off. |
The server checks Hikyo’s public GitHub releases at most once every six hours.
Only signed-in principals holding instance-config@instance see a toast and a
badge on the profile icon. Dismissing the toast keeps the badge until the
installed version catches up. Checks never download or install software, and a
release-source outage does not stop the server.
HIKYO_DEV_ADMISSION_PER_IP_PER_MINUTE is for development only. A production
server refuses to start when it is present.
Backup policy
| Variable | Meaning |
|---|---|
HIKYO_BACKUP_RECIPIENTS | Comma-separated age public recipients. |
HIKYO_BACKUP_DIR | Destination for automatic pre-migration exports. |
Recipients without a destination are a configuration error. When neither is configured, migrations continue but record a loud skip with no recovery archive.
Client variables
| Variable | Purpose |
|---|---|
HIKYO_STATE_DIR | Override local trust, context, and session storage. |
HIKYO_TRUST_BUNDLE | Provision trust for non-interactive automation. |
HIKYO_CONTEXT | Select a named context for one invocation. |
HIKYO_INSTANCE, HIKYO_ORG, HIKYO_PROJECT, HIKYO_ENV | Supply target dimensions. |
HIKYO_TOKEN | Supply a machine credential when a token file is unavailable. |
The CLI channel is local machine state, separate from the server-wide setting:
hikyo update channel stable
hikyo update channel nightly
hikyo update channel off
hikyo update checkStable and nightly release artifacts start on their matching channel. Direct
source builds stay on off; install a published artifact before selecting a
release channel. Interactive command invocations refresh release
metadata before dispatch at most once per 24 hours and offer an in-place binary update when one
is available; hikyo update check refreshes immediately.
Prefer flags for visible, one-off target changes and --token-file for machine
credentials. See contexts and targets.
Audit retention
HIKYO_AUDIT_ACCESS_RETAIN_DAYS defaults to 90;
HIKYO_AUDIT_SECURITY_RETAIN_DAYS defaults to 365. Both require whole days
within 1..3650, with security at least access retention. Invalid values refuse
startup. Host-local policy changes and deletion batches are audited.
The upgraded scheduler deletes eligible pre-existing audit history. Review policy before starting it and preserve any independently required evidence. Correlated units survive until every member expires. See audit retention and upgrade precautions.
Managed Hikyo configuration
For owner-local mail and release-notification settings, use Manage Hikyo with Hikyo: edit the protected project, publish an exact revision, reauthenticate and apply. That guide covers adoption, per-node convergence, independent remote projects and rollback.