hikyo
Documentation

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

FlagDefaultPurpose
--listen ADDR127.0.0.1:8080Public API/UI listener; HTTPS when a TLS pair is configured.
--operational-listen ADDR127.0.0.1:8081Plaintext health, readiness, and metrics listener.
--tls-cert-file PATHnonePEM certificate chain; configure with the key file.
--tls-key-file PATHnoneMatching PEM private key; mode 0400 or 0600.
--root-key-file PATHHIKYO_ROOT_KEY_FILEMode-0600, 64-hex-character root key; flag takes precedence. The environment path also supplies host-local custody commands.
--auto-migrate=BOOLtrueApply pending migrations before serving.
--upgrade-unattendedoffRun explicitly enrolled unattended container upgrade preparation before serving; see container upgrades.
--cli-socket PATHHIKYO_CLI_SOCKETSame-user Unix socket for local CLI traffic.
--config-rollout-enrollment PATHnoneOperator-installed Kubernetes rollout enrollment JSON.
--config-rollout-signing-key PATHnoneSeparate private deployment-authorization signer; see configuration rollouts.
--devoffEvaluation datastore, root key, and text logs.

Do not use --dev for real secret material or any non-loopback deployment.

Production network baseline

VariableMeaning
HIKYO_DBRequired: sqlite:PATH or postgres://... datastore.
HIKYO_PG_POOL_MAXOptional positive PostgreSQL connection-pool maximum.
HIKYO_EXTERNAL_ORIGINSet explicitly to the canonical public HTTPS origin.
HIKYO_TLS_CERT_FILE, HIKYO_TLS_KEY_FILENative TLS pair; both or neither.
HIKYO_OPERATIONAL_LISTENSeparate operational bind; default 127.0.0.1:8081.
HIKYO_TRUSTED_PROXY_CIDRSAlternative proxy mode for a non-loopback plaintext public listener.
HIKYO_ADAPTER_EGRESS_POLICY_FILEOptional startup-only adapter origin-to-CIDR exception policy, for any provider.
HIKYO_ADAPTER_AWS_WORKLOAD_IDENTITYallow 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_FILEOptional 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

VariableDefaultMeaning
HIKYO_DEFAULT_KEYS_PER_PROJECT1000Declared-key limit inherited by projects without an override, from 1 to 10000; cannot exceed the instance maximum.
HIKYO_MAX_KEYS_PER_PROJECT10000Operator-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:

VariableDefaultMeaning
HIKYO_MCP_ENABLEDfalseEnables the stateless JSON POST /mcp endpoint.
HIKYO_MCP_ALLOWED_ORIGINSemptyComma-separated, exact browser origins admitted at /mcp. Wildcards, null, paths, queries, fragments, and credentials are rejected.
HIKYO_MCP_WRITE_ENABLEDfalseInstalls 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:

HeaderValue
Content-Security-PolicySelf-only baseline, no inline script or style, frame-ancestors 'none'. Served documents extend connect-src with the configured remote origins only.
X-Content-Type-Optionsnosniff
Referrer-Policyno-referrer
Cross-Origin-Opener-Policysame-origin-allow-popups
Cross-Origin-Resource-Policysame-origin
Permissions-Policycamera=(), microphone=(), geolocation=()
Strict-Transport-Securitymax-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_ORIGIN has the https scheme (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.

MetricTypeLabelsMeaning
hikyo_http_requests_totalcounterclass, status (2xx/3xx/4xx/5xx/other)Requests per surface class and status bucket. A recovered panic counts as 5xx.
hikyo_http_request_errors_totalcounterclass, status (4xx/5xx)Client and server errors per surface class.
hikyo_http_requests_in_flightgaugenoneRequests currently executing in the API stack.
hikyo_http_request_duration_secondshistogramclassRequest latency; fixed buckets 0.005, 0.025, 0.1, 0.5, 1, 5 seconds plus +Inf.
hikyo_mcp_requests_totalcountermethod, tool, statusMCP requests by closed protocol method, registered tool name, and status bucket.
hikyo_mcp_requests_in_flightgaugenoneMCP requests currently executing.
hikyo_mcp_request_duration_secondshistogrammethod, toolMCP request latency using the fixed HTTP bucket grid.
hikyo_admission_concurrency_limitgaugenoneDerived Argon2id verification slots.
hikyo_admission_in_flightgaugenoneVerification slots currently held.
hikyo_admission_queue_depth_limitgaugenoneBound on simultaneous pre-auth waiters.
hikyo_admission_queue_waitinggaugenoneWaiters currently queued for a slot.
hikyo_admission_active_backoffsgaugenoneAccounts 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 PATH or HIKYO_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

VariableDefaultMeaning
HIKYO_ARGON2_MEMORY_KIB65536Memory per Argon2id operation.
HIKYO_ARGON2_TIME3Argon2id time cost.
HIKYO_REAUTH_WINDOW_SECONDS0 (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_PARALLELISM2Argon2id parallelism.
HIKYO_ADMISSION_BUDGET_MIB272Process-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

VariableDefaultMeaning
HIKYO_UPDATE_CHANNELstableServer-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

VariableMeaning
HIKYO_BACKUP_RECIPIENTSComma-separated age public recipients.
HIKYO_BACKUP_DIRDestination 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

VariablePurpose
HIKYO_STATE_DIROverride local trust, context, and session storage.
HIKYO_TRUST_BUNDLEProvision trust for non-interactive automation.
HIKYO_CONTEXTSelect a named context for one invocation.
HIKYO_INSTANCE, HIKYO_ORG, HIKYO_PROJECT, HIKYO_ENVSupply target dimensions.
HIKYO_TOKENSupply 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 check

Stable 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.