hikyo
Documentation

Deployment adapters

Push selected published values to Forgejo or GitHub Actions without reading provider values back.

If a job can fetch values at runtime, use workload federation. Use a deployment adapter only for static values that must exist before the first fetch.

Both compiled-in adapters are one-way and value-blind. They write selected values from one Hikyo environment to a repository, organization, or GitHub repository-environment Actions namespace. Neither adapter links a variable GET or list endpoint. Secret planning reads names only; variable planning uses Hikyo’s ownership ledger. Hikyo therefore cannot detect provider-side value drift. Run a converge to restore the last published Hikyo state.

Requirements

  • Forgejo 1.21 or newer.
  • A repository target PAT with write:repository, or an organization target PAT with write:organization. Forgejo write scopes also permit GET requests, but Hikyo’s variable-read operations do not exist in the client.
  • GitHub requires a fine-grained PAT (github_pat_); classic ghp_ PATs are refused before provider contact. Grant the fine-grained Secrets and Variables write permissions for the chosen repository or organization. Creating a missing environment additionally requires Administration (write): either pre-create it in GitHub and keep the delivery token narrow, or grant that permission for Hikyo-managed creation.
  • manage-adapters for inspection and lifecycle operations. Creating, widening, adopting, changing a prefix, replacing a credential, and manually syncing also require reveal authority and adapter reauthentication for every environment synchronized by that adapter.

The credential is write-only after entry. hikyo adapter create and hikyo adapter credential set read it from a no-echo terminal by default, or from --stdin or --value-file. There is no credential-value argument.

Targets and names

An adapter owns one provider origin and credential. Each target binds one Hikyo environment to one provider destination and an explicit, non-empty key subset. Organization targets are wide: every repository in that organization allowed by its all, private, or selected visibility can consume the organization-level values. Selected repositories are stored and verified by immutable numeric id. Moving to all, changing between private and selected, or adding selected ids is a widening ceremony; all to private/selected and selected-id removal are plain narrowing. That breadth is a storage-level property, not an override guarantee: a repository- level secret or variable with the same name shadows the organization-level value for workflows in that repository.

For GitHub, a repository environment is the recommended multi-environment shape: map dev, stage, and production to separate GitHub environments with no prefix. A missing environment is created during the reauthenticated target-add flow with an empty settings body, then its repository id and environment id are pinned. Later absence or either id changing is refused; Hikyo never redirects the write to repository scope. Free-plan private repositories without environments must be reconfigured explicitly to repository scope with prefixes. Workflow output for an environment target includes the exact environment: selection before the canonical-name mapping.

Effective provider names are prefix + canonical key name. The optional prefix applies to every key and to the MANAGED_BY_HIKYO sentinels. Effective name sets for targets sharing a destination must be disjoint. Use distinct prefixes when multiple environments feed one repository.

Applications still consume canonical names. Generate the required workflow mapping without exposing values:

hikyo adapter target show tgt_123 --format workflow

For example, a PROD_ target produces mappings from DATABASE_URL to ${{ secrets.PROD_DATABASE_URL }}. Local execution, rendered env files, and the Kubernetes operator continue to use DATABASE_URL.

Lifecycle

Create validates the provider connection and destination identity before storing the adapter credential and first target. A missing GitHub environment is the configure flow’s first provider write: a durable generation-1 effect fence and correlated adapter.push_intent exist before its settings-free PUT, and adapter.push_outcome closes that fence.

GitHub storage caps are 100 secrets/500 variables per repository, 1,000/1,000 per organization, and 100/100 per environment. Plans warn exactly from the complete secret-name list and report variables as a lower bound because Hikyo cannot safely read provider variables. Organization workflows expose only the first 100 secrets alphabetically; combined organization and repository variables over 256 KB can also truncate at consumption time. Plan/sync warnings name both limits.

Published environment changes automatically queue a newest-wins converge for intersecting targets under their recorded authority. adapter plan reports names and dispositions only. Existing provider names are never captured silently: adoption requires an exact conflict artifact, generation, destination, and (surface, name) pairs. If multiple exact-match artifacts are eligible, pass --artifact explicitly.

hikyo adapter show includes every target’s sync status, converged revision, failed names, and pending conflict artifacts. Its table view prints the pending surface:name pairs; JSON retains the full artifact id, generation, destination, entries, and creation time required for an exact adoption.

Origin and destination changes are asynchronous scrub-before-switch moves. They block new pushes, scrub the old route, activate and test the pending route, then converge it. The update response contains a move id; API clients poll GET /api/v1/orgs/{org}/projects/{project}/adapter-moves/{move} for the state, per-target scrub/activation/converge jobs, pending route, and orphaned names. Credentials never appear in a move response. --keep-remote skips the scrub, releases custody immediately, and enumerates the resulting orphaned names. When an old credential is dead, the move uses that same orphan-and-release rule before proceeding.

Transient activation failures retry. An invalid pending credential or a collision on the new route leaves the move in attention_required; Hikyo does not silently roll back. PATCHing the concrete move replaces either its pending origin plus write-only credential or its complete pending target route, then resumes activation. DELETEing that move atomically cancels it, restores the old route, and queues old-route reconvergence. The CLI requires the move id through --move for either recovery path, so pending and live credential replacement cannot be confused. The browser starts an origin move from the adapter panel (Change origin), carries the move id in the page URL, polls the move in a Route move pane until the server settles it, and offers exactly the two exits of an attention-required move: Resume with a new credential and Cancel move. Target environments are immutable: cross-environment movement is remove plus add, so authority and custody are never silently rebound.

Adapter reauthentication is distinct from value reveal. Its signed windows bind purpose adapter, the exact operation, and the complete environment set. One TOTP proof covers environments whose effective window is nonzero; each effective-zero environment requires its own WebAuthn proof. The CLI handoff is state- and PKCE-bound, has a single-use authorization code, and rotates the CLI bearer. The CLI binds an ephemeral 127.0.0.1 callback before opening /reauth/cli?transaction=<opaque-state>. The browser reads only display policy, performs the required ceremonies, and returns only code plus the same state to that exact callback. Only redemption returns the bearer; the CLI replaces its mode-0600 local artifact without printing or logging it.

Removing a target or adapter scrubs ledger-owned names by default, with sentinels last. --keep-remote releases custody instead and loudly lists the orphaned names. Remove targets while the PAT still authenticates. If a dead or revoked credential prevents scrub, Hikyo completes local teardown but reports the remote names as orphaned.

Multiple targets: selection, health, and control

One published revision fans out to every target configured for its environment, across every adapter kind, through one immutable job per target pinned to that revision, the target’s configuration generation, and the adapter’s recorded authority. Targets never share a job: a failure, a pause, or a conflict at one destination cannot block, roll back, or falsely mark another target successful.

Selecting keys. Membership is always an explicit set of immutable key ids. --keys names ids; --names names keys exactly; --include and --exclude are bounded glob patterns over key names (DB_*, *_TEST); --classification keeps only secret or config keys. Patterns and classification are resolved to ids when the target is saved and are not stored: a key created later that would have matched is never added on its own. --exclude narrows only what the patterns selected, never an explicit id or name. Widening the resolved set is a routing mutation and carries the full reauthentication ceremony.

hikyo adapter target add --adapter adp_123 --env prod --kind repository \
  --owner acme --repo app --prefix PROD_ --include 'DB_*' --exclude '*_TEST' \
  --classification secret

Naming. The per-target prefix is the one structural name mapping: every name the target writes is prefix + canonical name, validated whole against the provider grammar and collision-checked case-insensitively across the target and against every other target on the same destination. Values are never transformed. There is no per-key rename table by design; see the deployment-adapter ADR.

Health. adapter target list and the target detail report one of:

StatusMeaning
neverNo converge has run.
pendingA converge is queued and not yet claimed.
convergingA worker holds the job.
convergedThe destination holds converged_revision.
degradedThe last attempt failed but an earlier revision converged; the destination still holds it.
failedThe last attempt failed and nothing has converged yet.
pausedAn operator paused the target.

Beside the status: the last successful and last attempted revisions and time, a bounded error class (auth, network, conflict, provider_limit, provider_ambiguous, refused; never a provider response body), the retry time when a retry is queued, and attention when the destination disagrees with the ownership ledger in a way only an operator can settle (an unowned name in the way, a destination whose identity moved, orphaned names). Attention clears on the next successful converge.

Pause and resume. hikyo adapter target pause <target> stops every push without deleting owned destination state: the active job is superseded, a worker still inside it is fenced, publishes skip the target, and a manual sync is refused. Pause narrows what Hikyo does and carries manage-adapters alone. hikyo adapter target resume <target> runs the sync ceremony, queues one catch-up converge, and prints the published revision it reaches.

Resync. hikyo adapter sync --target <target> is the idempotent resync: newest wins, a second request supersedes the first, and neither bypasses adoption nor prunes beyond the ledger.

Removal keeps the explicit retain-or-prune decision described above. The browser offers the same choice in a dialog with neither option preselected, for one target (Remove) and for the whole adapter (Delete adapter).

Credential custody and probes. The adapter panel replaces the write-only credential (Replace credential, behind the credential-set ceremony) and destroys custody (Revoke credential, stating that a later remote scrub may then be impossible). The target detail runs Plan (names and dispositions only) and Test connection (provider version, destination identity and credential expiry; no value, no write), the browser forms of adapter plan and adapter test.

Operators. hikyo doctor reports the instance-wide counts of targets that need attention, failed targets, paused targets, and queued jobs, and the same four label-free gauges appear on /metrics as hikyo_adapter_targets_failed, hikyo_adapter_targets_paused, hikyo_adapter_targets_attention, and hikyo_adapter_jobs_queued.

Crash windows. If a worker dies between a push’s INTENT and its OUTCOME, the next claim of that target settles the effect as OUTCOME unknown, correlated to the job that opened it; the ownership row stays presumed written and the retry converges it. On a multi-node installation every worker competes for jobs under the outbox lease; a worker that loses its lease can no longer push, mark success, retry, or scrub.

Private origins and GHES

Adapters deny private, loopback, link-local, documentation, and reserved addresses by default. Operators can add origin-scoped exceptions with HIKYO_ADAPTER_EGRESS_POLICY_FILE; see server configuration. HTTPS, certificate validation, DNS rebinding protection, and the no-redirect rule remain mandatory. GitHub Enterprise Server uses its configured HTTPS API base and inherits the GitHub contract best-effort; github.com contract pins do not claim every GHES version behaves identically.