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 | none | Mode-0600, 64-hex-character root key. |
--auto-migrate=BOOL | true | Apply pending migrations before serving. |
--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 Forgejo origin-to-CIDR exception policy. |
When origin is unset, Hikyo derives https://<listen> with native TLS and
http://<listen> otherwise. Proxy mode should set the public HTTPS origin
explicitly because the private listener cannot infer it.
Native TLS is the default direct-deployment posture. Hikyo validates the pair at
startup, rejects expired or mismatched files, warns within 14 days of expiry,
polls for replacements every 10 seconds, and also reloads on SIGHUP. A bad
replacement leaves the last known-good certificate live and increments
hikyo_tls_reload_failures_total.
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.
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. |
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 ashttps://<listen>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 — keep the operational bind private and scrape it from
the host or a trusted network.
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 | — | 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 | — | Derived Argon2id verification slots. |
hikyo_admission_in_flight | gauge | — | Verification slots currently held. |
hikyo_admission_queue_depth_limit | gauge | — | Bound on simultaneous pre-auth waiters. |
hikyo_admission_queue_waiting | gauge | — | Waiters currently queued for a slot. |
hikyo_admission_active_backoffs | gauge | — | 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.
Adapter egress is public-address-only by default. To approve a private Forgejo
origin, 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 PATH, 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
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 or 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.