| audience | data owner, operator, developer |
|---|---|
| type | reference |
| canonical_id | cli-surface |
The data-owner, operator, and developer reference for the hot-path
uv run okg commands. It is not exhaustive — run uv run okg --help (and
uv run okg <verb> --help) for the full command tree, the source of
truth. The default listing leaves out a few internal commands used by this
repository's own agents, CI and benchmarks, and names them in its closing
note; each still runs and has its own --help. It also leaves out, without
naming them, the cross-deployment exchange (federation) commands, which this
release does not support. The commands cover four
responsibilities: import information, store the graph, publish a version, and
search and use data.
For the software boundary behind these commands, see component responsibilities. The identifiers below are the public CLI names.
The machine-readable error code registry is the authority for stable error identifiers and recovery hints.
Conventions:
--json(default underOKG_AGENT=1) — machine-readable envelope on stdout. Errors land on stderr as a single JSON object witherror.code+error.fix_hint.- Deployment-scoped commands resolve the database from the deployment
manifest. They use
OKG_DSNonly when that manifest explicitly references${OKG_DSN}. Commands that do not name a deployment may accept--dsnorOKG_DSNas direct operator input. - Write commands accept
--apply; underOKG_AGENT=1they refuse without it.
Renamed flags. The bare --profile flag used to select seven unrelated
things depending on the command. Each of those flags now has a name that says
what it selects. The old spellings still work for one release: they are hidden
from --help, and each use prints one warning line on stderr (never stdout,
so --json output stays valid). okg install --profile and
okg init --profile keep their name: there it means the deployment profile.
| Old spelling | New spelling | Commands |
|---|---|---|
--profile dev|prod |
--target |
okg up, okg deployment up, okg deployment backup verify, okg deployment read-status |
--profile general|dogfood|ci|standard |
--checks |
okg deployment ready, okg deployment prove-live |
--readiness-profile |
--checks |
okg up, okg deployment up, okg deployment nomos-source-lineage-rollout-audit |
--profile deep|off |
--capture |
okg run, okg ingest |
--profile-collector |
--capture-collector |
okg run, okg ingest |
okg profile |
okg perf |
the capture inspection verb |
--profile |
--permissions |
okg agent-runtime-launch |
--profile |
--scope |
okg search |
--search-profile |
--scope |
okg chat setup |
--tool-profile |
--tools |
okg mcp-serve |
--tool-profile-ceiling |
--tools-ceiling |
okg mcp-serve |
--profile |
--provider |
okg nomos provider fixture |
Verbs are grouped by category, alphabetical within a category. Sub-verbs follow their parent.
Publisher.reserve() raises PublisherLeaseHeld when another publisher or
source-sync publication owns the deployment's coordination guard.
PublisherLockHeld is a compatibility alias for the same Python exception;
structured errors use publisher_lease_held.
{
"error": {
"code": "publisher_lease_held",
"message": "publisher lock is held for deployment='<name>'",
"fix_hint": "wait for the active publisher or source-sync publication to finish, then retry",
"data": {
"deployment_name": "<name>",
"coordination_mode": "dbos",
"advisory_key": [123, 456],
"wait_seconds": 30.0
}
}
}When source-sync publication has priority, data instead includes
reason: "source_sync_active" and blocking_workflow_id. The command fails
rather than running a concurrent publisher or reporting the held lease as
success. Wait for the owner or inspect the workflow the error names before
retrying.
The daily-driver surface. Run a publish cycle, import sources, inspect what was accepted, and trace a record through storage and derivation.
okg admission {list, show, submit, decide} — inspect admission
envelopes and lifecycle evidence.
Common shape across the four sub-verbs: --deployment, --dsn,
--json. Each sub-verb has its own required args.
Implementation: src/okg/substrate/cli/main.py (_cmd_admission)
okg admission list [--status <s>] [--mode <m> | --held] [--source <s>] [--limit N]
— list recent admission envelopes. --held keeps the review- and
quarantine-mode envelopes, the runs that wait for okg admission decide.
okg admission show <admission_id> — full envelope with events
and record refs.
okg admission submit --deployment <d> --producer-id <id> [--producer-kind agent] [--intent review_proposal] (--payload-json '<json>' | --payload-file <file>) [--authority-json '<json>'] [--check-summary-json '<json>'] [--idempotency-key <key>]
— submit a review-mode node/edge payload through DBOS. Intent is a bounded
machine identifier; invalid values fail before enqueue. Deployments may opt
into the bounded, fail-closed runtime approver described in
deployment authority guide.
okg admission decide <admission_id> --decision {accept|reject|quarantine} [--reason ...]
— accept, reject, or quarantine a review-mode admission. Also opens
lens-promotion envelopes with --action promote --request-id <uuid>.
okg alias {add, collisions, list, remove, seed-from-subtype} — manage the
alias dictionary (the manual operator path for entity_mention
resolution).
Implementation: src/okg/substrate/cli/main.py (_cmd_alias)
okg alias add <surface> <canonical> [--scope <s>] — record one
alias.
okg alias list [--scope <s>] [--limit N] — read the dictionary.
okg alias collisions [--limit N] — list ambiguous alias values that
deterministic catalog seeding refused at the active catalog version.
okg alias remove <alias> — drop one entry.
okg alias seed-from-subtype --subtype <name> --field <attr> [--apply]
— bulk-import aliases from every node of a subtype's attribute value.
okg catalog {load, history, ownership, repair-restamp-ownership} —
compose / install deployment catalog (modules + bridges).
Implementation: src/okg/substrate/cli/main.py (_cmd_catalog)
okg catalog load --deployment <path-or-environment> --writer-scope {deployment_root,environment} [--environment-id <id>] [--apply] — compose subtypes + narrowings + aliases + identifier patterns
from the deployment's modules + bridges. Dry-run by default; --apply writes
a fresh catalog_versions row. A deployment directory uses
deployment_root; a sealed environment uses environment plus its explicit
environment id. Ambient OKG_ENVIRONMENT_ID state does not select authority.
okg catalog ownership {diagnose,claim} --deployment <path-or-environment> --writer-scope {deployment_root,environment} [--environment-id <id>] — inspect
or claim the same explicit catalog-writer authority a publish will use.
Environment scope requires an environment id; deployment-root scope rejects
one. Recovery commands in the result repeat the selected authority flags.
okg catalog history [--limit N] — list recent catalog versions
from okg.catalog_versions.
okg catalog repair-restamp-ownership --deployment <name> [--dsn ...] [--branch default] [--apply] — return graph nodes that an earlier catalog
load took over to the source that wrote them. In earlier versions of okg, a
catalog load that changed a subtype's searchable-text fields rewrote every node of that subtype under the source _catalog:restamp, and
the original source could then never retract those nodes. The command reads
the row version each takeover replaced and reports the nodes per original
source.
- Without
--applyit writes no graph rows, takes no branch lock, runs no DDL and rolls its transaction back. With--applyit takes the branch write lock before reading, so graph writers wait for it. - A node whose source the branch history cannot name is reported under its
reason (
no_earlier_version, which also covers an earlier version overwritten in place, compacted away or left in a node table that schema branching has since replaced;ambiguous_earlier_version;earlier_version_deleted;earlier_version_unattributed;earlier_version_no_lineage) and left alone, and the command exits 1. With--applythe nodes it could resolve are still written and committed; the output says so. - A node the source stopped emitting while
_catalog:restampowned it is not in the source's stored record set, so the source's next normal run does not retract it, even after the repair. The output lists those nodes per source and the follow-up that retracts them and nothing the source still emits:okg ingest --deployment <name> --include <source> --no-predecessors --inline --reset-cursor. Without--no-predecessorsthe source's predecessors would be reset too. If the source's current read emits no records, add--allow-full-retraction <source>; the full-retraction guard refuses that run otherwise. A source with no stored record set gets no command: a reset-cursor run would not retract its nodes, so the output marks it for inspection. - Rerunning after a successful apply finds nothing.
okg doctor [--check <name>] [--deployment <d>] [--dsn ...] — verify
Postgres and deployment health.
Built-in checks include: extraction, extraction-wrap-invariant,
acceptance-criteria, extractors, integrity, admission,
derived-admission, agent-skills, agent-runtimes, mcp-surface-freshness,
external-live-authority, nomos, source-auth, database,
grant-registry-drift, pending-grants,
declared-attr-indexes, vector-sets, deferred-edges, sources. Run okg doctor --check <unknown> for the authoritative set.
Deployment checks come from deployment.yaml.
With --deployment, vector_sets reports per set: coverage at the pin
(sampled above 50,000 nodes with non-blank text: method: sample, missing_upper_95),
unpublished writes, index state, spend, fill age (flagged when never or
over 10 publishes old), worker fill errors and config drift, plus
old-column vectors no set owns, as warnings. --check vector-sets is
exact, untimed, and exits 1 on any finding. State is explicit: clean
proves readiness; stale found outdated state; unavailable could not prove
state; command_failed reports a failed helper. The last two block readiness.
With --deployment, doctor fails latest_publish_blocked when the newest
publish attempt was blocked, and names the fix commands.
With --deployment, the record_set_blobs section checks the stored record
sets of each source. A record set larger than 65,536 bytes (override with
OKG_SOURCE_RUNNER_INLINE_RECORD_SET_MAX_BYTES) is stored once in
okg.source_record_set_blobs, and the source's watermark keeps only a
reference to it. Doctor warns source_record_set_blob_missing, naming the
source and sha256, when a watermark refers to a blob row that is gone: the
next run of that source then reads no prior record set and cannot retract
what the lost set held. After each accepted run, the source's older
record-set and cursor blobs that no watermark or held run names are deleted.
Blobs written within the grace window are kept: source_blob_prune_grace_seconds
in deployment.yaml (default 21600, six hours; 0 turns the prune off). A
running sync's blobs are protected only for that window: a run that outlives
it can lose them, so a short window is unsafe while inline or CLI runs of the
same source can overlap. The ingest report shows the rows and bytes deleted under record_set_blob_prune.
A failed prune leaves the run accepted; doctor then warns
source_record_set_blob_prune_failed for that source until a later prune
succeeds.
Implementation: src/okg/substrate/cli/main.py (_cmd_doctor)
Related: runbooks/generation-blocked.md
okg generation {list, describe} — inspect published Chronos
checkpoint generations.
Implementation: src/okg/substrate/cli/main.py (_cmd_generation)
okg generation list [--deployment <d>] [--limit N] — recent
published generations from okg.v_generations.
okg generation describe <generation_id> — checkpoint and graph counts.
New checkpoints report the publisher-selected core_revision,
deployment_revision, and full-repository deployment_tree_sha under
repository_provenance with status: exact. Legacy checkpoints report
status: unavailable and null values, which cannot prove an exact pair.
ingest_completeness says which sources were not current when the version
was published (see okg ingest); it is null for a version published before
that record existed.
okg ingest --deployment <d> [--include <list>] [--exclude <list>] [--no-predecessors] [--no-publish] [--force-enrichers] [--progress]
— drive the selected sources in a deployment's source_registry in
topological order; publish. --no-predecessors keeps an explicit
--include exact instead of expanding through predecessor closure.
Repeat --include or --exclude to add sources: --include a,b --include c
selects a, b and c. On okg provision, okg deployment up and okg up, repeating
--include likewise adds to the list.
--no-publish leaves accepted changes in working state for inspection; it
does not make them searchable. The maintained-knowledge
lifecycle defines that boundary, while
Consistency architecture defines
the evidence required before a derived result may be reused.
With no --include, ingest drives every source in the registry except those
setting enabled_by_default: false (an opt-out for sources like an
unconfigured dblp crawl). A source leaving the flag unset still runs, so a
hand-authored registry keeps its all-sources behavior. Predecessors of a
running source are pulled in even when opted out; name an opted-out source
with --include to run it.
--force-enrichers is the one-shot recovery lever for an ingest whose source
scan is unchanged but whose derived graph state must be recomputed. It
bypasses the ingest no_source_work skip and the publisher's unchanged-input
reuse, then runs enrichers, gates, and a new checkpoint. It passes through
inline and DBOS-queued ingest and is not persisted: the next unforced
invocation resumes normal skip behavior. Do not use it routinely instead of
fixing a source or catalog delta: it requests complete computation for one
cycle, and proves neither that the previous reuse decision was unsafe nor that
a resulting claim is true. See Incremental maintenance and
consistency.
When some selected sources fail and others succeed, ingest still publishes
the successful sources' changes and exits 1. The failed sources' own writes
are undone first. The new version records which sources are not current
under ingest_completeness in its metadata (shown by okg generation describe): status is incomplete, and failed_sources and
dependency_blocked_sources name each source by its registry name and
ownership_id. A source stays listed until a run of it succeeds, or until it
is removed from the source registry; later publishes keep the record. The
worker behaves the same way, and other sources keep syncing.
The record only comes from runs that publish. A run in which every selected
source fails publishes nothing, so a source that fails in runs of its own is
not listed there; its failed admissions are the record of that. Only when a
failed source's undo did not finish does the run still cut a blocked
checkpoint that demands an exact repair run (repair_required). That repair
run publishes like any other run once its failed sources' changes are undone,
even if they fail again, and the hold ends.
Implementation: src/okg/substrate/cli/main.py (_cmd_ingest)
okg deployment source-types lists the supported types and their settings.
okg deployment add-source <deployment> <name> --type <type> [--setting k=v ...] [--credential ENV_NAME ...] [--registration <asset.json>] [--deployments-root <dir>] [--dsn <dsn>] [--apply] [--yes]
— the guided flow of Connect your data:
preview without --apply; with --apply save, import, check, publish, and
print one searchable sample with its source; re-run the same command to retry
a failed step. --registration takes a packaged connector declaration instead
of --type; --manifest <file> takes a
source manifest instead of --type, --setting and
--credential.
Implementation: src/okg/substrate/cli/source_add.py
okg source draft <file> [<file> ...] [--type <name>] or
okg source draft '<url>' [--auth <mode>:<ENV> ...] [--type <name>]
— read up to 200 sample records from local JSON, JSON Lines, CSV or YAML files,
or from one GET of a JSON API, and print a commented
source manifest to standard output: the record list
path, paging, field types, dates, nested types and links, a key, a title
and a text field. The key: and deletion: lines are marked GUESSED.
--auth names environment variables, never values: bearer:ENV,
header:Name:ENV, basic:USER_ENV:PASSWORD_ENV or
client_certificate:CERT_ENV:KEY_ENV[:CA_DIR_ENV]. It writes nothing and
never adds the source; preview the reviewed file with
okg deployment add-source <deployment> <name> --manifest <file>.
See Bring your own data with a manifest.
Implementation: src/okg/substrate/cli/source_draft.py
okg deployment remove-source <deployment> <source> [--deployments-root <dir>] [--dsn <dsn>] [--apply] [--yes]
— the counterpart of add-source, described in Remove a
source. <source> is the registry
name or the ownership id. Without --apply it previews the records and
relationships to retract and the registry entry to remove; with --apply it
disables the source (stopping scheduled and default imports of it; a re-run
retracts records an explicit import wrote before the publish, and refuses
records that return after it, tracked in issue 3265), retracts its records
through the reviewed source retirement, publishes, removes the registry entry once the published version
records the retirement, re-claims the deployment identity, and updates the
access policy. A source that never reached the graph loses only its registry
entry. --apply prompts for confirmation; --yes is required when input is
not a terminal or with --json. Run the same command again to resume a
failed step. Refusals are remove_source_refused.
Implementation: src/okg/substrate/cli/source_remove.py
okg deployment conformance schema --kind <request|receipt> [--protocol-version <v1|v2>]
— print the public JSON Schema of one external-distribution conformance wire
document.
okg deployment conformance validate --request <request.json> [--receipt <receipt.json>] [--trusted-external-review <review.json>] [--json]
— check inert conformance JSON. Nothing is executed, no database is opened and
no network call is made, so a consumer can run it from outside any checkout.
Without --receipt only the request is checked; with one, the complete
exchange. Exit 0 is valid, 1 a refusal carrying a diagnostic, 2 a usage
refusal on stderr — an input the command could not read, or flags that do not
go together.
--trusted-external-review takes the acknowledgement record a reviewer
published out of band — the validator has no way to fetch it, so a receipt
claiming a current acknowledgement is otherwise always refused
conformance_external_acknowledgement_pending. The file is read as data by
the same rules as the request and the receipt: duplicate keys refused, unknown
fields refused, every failure a stable code. It requires --receipt, since
only a receipt carries an acknowledgement. Supplying it adds
acknowledgement_bindings to the --json result — the consumer id, source
revision, acknowledged schema digest and status the receipt asserted, and the
names of any fields on which the review disagreed. Only a current
acknowledgement is weighed against the review, so against a historical one
that list is always empty. Without the flag the result keys and bytes are
unchanged.
Implementation: src/okg/substrate/cli/deployment.py (_cmd_deployment_conformance_validate)
okg provision --deployment <d> [--include <sources...>] [--readiness-sources] [--no-predecessors] [--publish-once]
— bootstrap a deployment through extensions, schema migration, catalog apply,
source ingest, and publish. --readiness-sources selects the standardized
agent-context source set and publishes once after ingest so readiness checks do
not depend on broader registry closure.
| Exit | --json status |
Meaning |
|---|---|---|
| 0 | Provisioned: the publish proof passed. | |
| 8 | capacity_warning |
Provisioned, and the publish committed, but it held the publisher lock longer than the capacity bar: the smaller of the running statement timeout and 900 s, divided by publisher.lock_hold_headroom_factor (default 2, so 450 s). capacity_warning and publish_proof.capacity_warning carry the hold, the bar and how the bar was derived. A caller that treats only failures as fatal proceeds; the Helm chart's bootstrap init container does. |
| 1 | A step or the publish proof failed, for example a blocked generation, no row delta, dead-letter work, or an unmeasured lock hold. | |
| 2 | Usage or extension error, as for every okg command. |
Implementation: src/okg/substrate/cli/provision.py (_cmd_provision)
okg remediate entity-merge --survivor <canonical-id> --retire <duplicate-id> --deployment-name <d> [--branch <b>] — plan a bounded duplicate-entity repair.
Dry-run is the default. The result identifies attribute and edge conflicts and pins
the published checkpoint, deployment catalog, complete incident-edge footprint,
and plan_digest; it does not write graph state.
After reviewing the plan, apply exactly that digest:
uv run okg remediate entity-merge \
--survivor region:signal \
--retire region:signal-batch-7 \
--deployment-name cooper \
--expected-plan-digest <digest-from-dry-run> \
--accept-survivor-attrs \
--actor <operator-id> \
--apply --jsonEvery attribute that differs between the two nodes needs a disposition, or the command refuses rather than guess:
| Disposition | Effect |
|---|---|
union |
Keep every entry from both lists. Both values must be lists. |
keep_survivor |
Take the surviving node's value. |
keep_retiree |
Take the retired node's value. |
A disposition comes from --merge-attr <name>=<disposition> (repeatable), or
from the schema, where an attribute can declare annotations.merge_semantics.
--merge-attr outranks the schema; --accept-survivor-attrs fills in
keep_survivor only for attributes neither covers, so it cannot undo a
declared union. It never applies to an attribute that is a list on both
sides: taking one side discards entries, so a differing list always needs its
own disposition. Use union for a citation list such as grounded_in.
Apply also refuses a changed published checkpoint, catalog, graph footprint,
unsafe narrowing, edge identity conflict, or self-loop. Two edges that assert
the same thing but were written by different extraction runs are recognised as
one edge and deduplicated; what the surviving edge does not carry over is
recorded in the plan under provenance_variance.
Different software producers are never treated as equivalent merely because they
reach the same (source node, edge type, destination node). The deployment catalog
must declare entity_merge_equivalence on that exact edge narrowing, including
the oriented survivor/retiree producer and provenance pair, the producer-owned
audit attributes that may differ, and the supported confidence rule. The service
also requires equal semantic attributes, source records, normalized Nomos
lineage, and effective read-trust class, plus a survivor confidence at least as
strong as the retiree. There is no per-edge CLI
override: the CLI approves and digest-binds the destructive node repair, while
the versioned deployment catalog owns repeatable assertion semantics. Missing,
reversed, malformed, or nonmatching policy continues to block.
The declaration is an annotation on the deployment's EdgeNarrowings
attribute; catalog composition expands it onto the exact source subtype, edge
type, and destination subtype:
annotations:
edge_archetype: references
src_subtypes: DocumentChunk
dst_subtypes: ExperimentalUncertainty
entity_merge_equivalence:
schema_version: okg.entity_merge.edge_equivalence.v1
survivor:
producer: _enricher:mention_grounding
provenance: derived_deterministic
retiree:
producer: _enricher:physics_extraction
provenance: explicit
audit_attrs: [evidence_basis, provenance_kind, src_kind]
confidence_rule: survivor_gteGovernance and trust-marker attributes cannot be declared audit-only. This capability does not silently enable any deployment: the measured Cooper repairs remain blocked until Cooper publishes its reviewed rule in its own catalog.
Every safe edge deduplication includes versioned assertion_variance and
equivalence_evidence in the dry-run plan and durable receipt, retaining the
discarded assertion fields, exact catalog rule, and read-trust profile evidence
that justified keeping the already-live edge unchanged. A rule, profile, or
assertion change therefore changes plan_digest and makes a stale apply refuse,
and a merge is refused when rewriting the survivor's attributes would shrink its
recorded provenance. A successful apply returns a durable receipt in
awaiting_publish; the deployment's publish coordinator must cut and verify the
repair generation before treating the repair as live. okg trace node <duplicate-id> reports the survivor and receipt after the retired node leaves
the live graph. Replaying the same apply requires the same survivor and digest
and does not mutate the graph again.
Implementation: src/okg/substrate/cli/main.py (_cmd_remediate)
okg library {list, show} — AKMON runtime library refs (invariants,
linker routes, source modules, and deployment templates). Ontology definitions
remain available under library/ontologies/ and are selected through a
deployment's modules: list rather than okg.ontology.* dotted refs.
Implementation: src/okg/substrate/cli/main.py (_cmd_library)
okg library list [--kind {invariants|linker_routes|modules|templates}]
— enumerate library refs.
okg library show <ref> — print the resolved YAML for one ref
(for example, okg.modules.openspec, a compatibility source module; it is
not the PACT authority for changes).
okg metrics {--llm | --qa-snapshot} [--since 1h] [--deployment <d>]
— roll-up metrics. --llm returns token / cost spend; --qa-snapshot
returns a QA-gate snapshot for the deployment's latest published
generation.
Implementation: src/okg/substrate/cli/main.py (_cmd_metrics)
okg run --once --deployment <d> --apply [--verbose] [--force-enrichers] [--include-llm-invariants] [--llm-invariant <name>] [--json]
— run one publish cycle. The runtime worker owns continuous and scheduled
work. The cycle publishes what okg ingest or source sync already staged; it
runs no source itself.
By default the command prints a short summary: whether it published, the
graph version, records and relationships added, updated and retracted, the
graph totals, and, with --include-llm-invariants, the LLM-invariant fire,
call-failure and parse-error counts. Source results come from okg ingest,
not from this command. Every warning the cycle reports (a blocked publish,
excluded sources, consistency violations, failed or degraded views, rejected
derived output, pending migrations, or any stage whose status is a failure or
a value the summary does not know) prints after it with a fix: line that
inspects the result, usually okg generation describe <version>. A refusal
prints its code and fix hint on stderr.
--verbose adds the full cycle report: stage timings, gates, views and
receipts. --json prints the full cycle report plus the same summary under
run_summary.
The exit code follows the publish verdict. Both paths classify the cycle
summary the same way, so the same cycle gives the same outcome and code.
--json carries the verdict as run_summary.outcome and
run_summary.exit_code on the in-process path, and as top-level outcome
and exit_code on the runtime-queue path (runtime enabled, no --inline),
which prints an outcome= line instead of the summary.
| Exit | Outcome | Meaning |
|---|---|---|
| 0 | published, no_change |
The cycle published a new graph version, or ran and found nothing new, so the current version stays. Warnings on a published cycle, such as excluded sources or rejected derived output, keep exit 0; they print with their fix: line. |
| 0 | skipped |
The runtime did not run the cycle, for example while a source sync is active. Nothing was checked and nothing was published; readers still see the previous version. |
| 3 | blocked |
A gate refused the graph version the cycle built. Readers still see the previous version. A block wins over a failure status reported with it. |
| 1 | failed, unknown |
The cycle reported a failure, or a status nothing classifies, or no cycle summary. |
| 1 | not_awaited |
Runtime queue only: the command stopped waiting before the cycle finished. |
| 1 | A refusal or error with its code and fix hint. This includes a refusal before the cycle and an --include-llm-invariants step that fails after the cycle published. In that case the graph version is published even though the command exits 1. |
|
| 2 | Usage or extension error, as for every okg command. |
--force-enrichers applies the same one-shot recovery semantics to the
publish cycle: bypass unchanged-input reuse, run enrichers and gates, and
cut a checkpoint. The invalidation receipt records operator_forced; the
next run is unforced unless the flag is supplied again. The consistency
architecture explains why missing
reuse evidence causes more computation and why a passed publication check is
not proof of factual truth.
Implementation: src/okg/substrate/cli/main.py (_cmd_run)
okg search --deployment <d> --query <text> [--method lexical|semantic|alias] [--limit N] [--offset N] [--generation-id <id>] [--select f1,f2] [--subtype <name>] [--scope <name>] [--source-family <name>] [--max-per-parent N] [--max-chars N] [--attr-filter key=value] [--embedder <spec>] [--allow-unverified-embedding-provenance] [--json]
— search nodes at the pinned generation. The
search guide explains how to keep the
generation_id pin across related reads.
The JSON shape matches the MCP result envelope.
--select is comma-separated and repeatable; an unknown field fails with
select_unknown_field and lists valid_fields. --attr-filter is repeatable
and exact-match; a value is a JSON scalar when it parses as one (true,
null, numbers, quoted strings), otherwise a raw string.
Human output is the read commands' text form; --select adds a score.
Control characters print as spaces.
--scope <name> selects a deployment-declared search profile: the subtypes
it counts as content, with their refinements. --scope all is always
unscoped. A search.default_profile applies when you name neither --scope
nor --subtype; a note says so:
! search profile analyst applied: 10 subtypes
--source-family (repeatable), --max-per-parent, and --max-chars add the
authority, diversity, and preview controls; --scope all turns off every
profile-declared one. Their envelope fields, applicability, empty-page counts,
and refusals: MCP search methods.
The query embedder is the search vector set's backend
(embeddings); --embedder and
OKG_MCP_EMBEDDER may repeat it (deprecated, a mismatch fails). A
standalone query needs --embedder, else semantic_embedder_not_configured.
--embedder with another method is an error.
Pages carry embedder_spec, embedder_model_id, embedder_source, the
plan fingerprint and embedding_coverage. They rank the set once its fill
completed by the pin (semantic search),
else the node columns, where semantic_index_empty refuses a scope with no
vector and embedding_model_skew another model.
--allow-unverified-embedding-provenance admits node-column rows of unknown
model as embedding_provenance: unknown; a set refuses it. With embeddings
disabled, semantic calls refuse.
Example:
okg --json search \
--deployment my-graph \
--query "context efficient" \
--method lexical \
--limit 10 \
--offset 10 \
--select node_id,subtype,excerptDefault lexical rows are lean (node_id, canonical_node_id, subtype,
method, match_kind, score, excerpt, provenance). Narrowing --select
drops the match-context fields too, so node_id alone gets a minimal row.
Request --select attrs only when the excerpt is insufficient.
Implementation: src/okg/substrate/cli/_read_side.py
okg <operator> [--deployment <d>] [--<argument> <value>] [--args <json>|@<file>|-] [--json]
calls one MCP read tool; OKG_DEPLOYMENT names the default.
Each argument is a same-named flag; --help gives types and examples.
Values are JSON, else a string or comma list; --args takes the
object. expand, inspect and query take one argument bare. okg guide
shows pipe examples, then a read-only MCP client's connect text; --json
adds bytes and sha256.
--where k=v repeats (!=, >, <, >=, <=, ~ prefix); --type
names a subtype. --jsonl prints rows, --ids ids,
--all every page; status to stderr. - or --stdin reads ids;
inspect texts - their texts (--lines 1-40).
Output is text (over 20 rows: a pipe hint); --json prints one document;
--all joins pages (all_pages); several stdin calls print an array.
Exit 0 served, even cut or capped (the --all cap prints resume:);
2 refused, or a page with no next call (stderr; retry: okg ... when
fixable); 1 error. OKG_READ_DEBUG=1 shows timings.
okg search runs the MCP tool for a tool-only flag or
--method grep|attrs|identifier_mentions; there --all means every page.
okg filter --type ticket --where attrs.status=Open --all --ids | okg inspect texts -Implementation: src/okg/substrate/cli/_read_operators.py
okg chat-instance {up,status,supervise,down,front-door} manages the
deployment's Open WebUI container. up requires a separate, reachable app
database (OKG_CHAT_APP_DATABASE_URL), a session-signing key
(OKG_CHAT_WEBUI_SECRET_KEY; --admin-secret-env renames it, and it is not
an admin API token), and a resolvable graph DSN to check database isolation.
down removes the container and record, preserving chat history.
Declare chat.image (pinned :vX.Y.Z or @sha256:) and
chat.instance_port, or supply --image / --instance-port to up.
The chat image is not published: build ops/openwebui/Dockerfile (Open WebUI
v0.11.4), set chat.image, and add its digest to TESTED_IMAGES mapped to
"0.11.4" (see ops/openwebui/README.md).
Conflicts and untested images refuse; only the patched Open WebUI v0.11.4
image is tested. up also starts the container with the update check and
easter eggs off (ENABLE_VERSION_UPDATE_CHECK=false,
ENABLE_EASTER_EGGS=false); for a container started without them up
refuses instance_settings_changed until down and up again. Records live
under ${XDG_DATA_HOME:-$HOME/.local/share}/okg/chat-instances/<name>/;
legacy deployments/<name>/ records migrate, and duplicates refuse
instance_state_ambiguous.
status derives instance state and exit status from GET /api/version and
observes the chat.mcp.port listener separately. supervise restarts the
chat container, never mcp-serve: it reports unmanaged: ["mcp-serve"] and
mcp_endpoint_dead when that listener fails. A listener does not prove MCP
tool discovery.
front-door returns 503 at /console until --console-bundle-index or
--console-base-aware proves base-aware routing; --console-upstream then
supplies the proxy target, never chat.deep_links.operator_ui_base.
Every verb here refuses a deployment that declares no enabled chat: block.
No profile ships one; okg chat setup writes it from --provider,
--model, --instance-port and optional --provider-key-env,
--mcp-port, --scope and --search-subtype, recording the
provider credential as a variable name only. --configure-only writes or
checks the block and stops. A re-run changes nothing, and an answer that
disagrees with an existing block refuses chat_block_answer_conflicts.
Set up chat walks through the answers.
okg chat setup --deployment <d> is the one guided entry point: it runs
chat-instance up, creates or signs in to the admin account, registers
chat.provider, checks chat.models.default is served, starts mcp-serve
when chat.mcp.port is silent, runs sync, then asks the preset one
question with tool_ids, in a new chat titled "okg chat setup: test
question" that stays in the admin's chat list. It reports configuration
converged apart from conversation ready, exits 0 only when that stored
answer records a graph tool call the graph answered without refusing
(tool_use_unobserved when it records none, graph_tool_calls_refused when
the graph refused every call, graph_tool_calls_failed when no call reached
the graph). A turn slower than the request's timeout is read back until it
finishes, for up to the same time again (probe_turn_timeout after that).
On failure it names the step, cause, and
variable to fix before re-running. OKG_CHAT_ADMIN_PASSWORD
(with OKG_CHAT_ADMIN_EMAIL) creates the first account on an empty instance
and signs in afterwards; OKG_CHAT_ADMIN_TOKEN wins when set.
chat.provider declares base_url as the container reaches it and
api_key_env, the variable holding the key. Secrets are never printed;
reapply preserves accounts.
The verbs below take OKG_CHAT_INSTANCE_URL and OKG_CHAT_ADMIN_TOKEN.
Declare the preset's base model in chat.models.default and
chat.models.builtin; no default hides the preset.
A selected bundle supplies branding defaults and instruction assets; the
deployment may replace branding but only narrow the bundle's connections and
tools. --install-plan <dir> composes the bundle's contribution; on
chat-instance up it sets the bundle's site name and must be passed on each
up. Packaged
playbooks and logos remain undetermined, not applied.
okg chat plan --deployment <d> --out <new dir> writes deterministic
chat-plan.json for seven configuration surfaces without contacting the
instance; it records credential references, never values, and never
overwrites. Apply with okg chat sync --deployment <d> --plan <file>;
manifest drift refuses chat_plan_drift, and sync records plan_digest.
okg chat readback --deployment <d> --plan <file> --out <new dir> compares
live settings read-only and exits nonzero on chat_readback_deviation.
Graph connections use the bearer named by chat.mcp.auth_token_env; an
undeclared credential means auth_type: none, and a declared but unset one
refuses mcp_connection_credential_unresolved before any change. The
instance's verify route dials with the credential in your environment, not
the one the site stored, so it proves your token works, not that chat turns
use it. Readback therefore also compares the stored credential with the
variable's value, by keyed digest inside one run, and reports
mcp_connection_credential_stale when they differ, for example after a token
rotation without okg chat sync. Readback also deviates when it cannot make
that comparison: mcp_connection_credential_unmeasured when the Open WebUI
version hides the stored key (0.11.4 does not), and
mcp_connection_duplicate_id when two connections carry the same id. A
rejected credential is a deviation; a missing verify route remains unmeasured.
chat.mcp.live_tools declares names, url, and auth_token_env for a
separate live-service connection that never inherits the graph bearer. Tools
must also be granted under chat.mcp.audiences.<n>.live_tools; no audience
means no connection. Names overlapping graph operators refuse
chat_bundle_live_tool_conflict; undeclared names or audiences with different
live-tool sets refuse chat_live_tool_slot_refused. Connection filters do not
replace backend authentication or Nomos authorization.
Sync sets the browser's initial model to the deployment preset, which
carries its tools and prompt; chat.models.default remains the base provider
model. chat.models.ui_default, a member of builtin, opens on a raw model
instead, without graph tools or the preset prompt. After applying all
surfaces, sync checks the vendor's first-entry rule and refuses a wrong
selection or missing model row.
Sync also switches off Open WebUI features the site does not offer, and
readback reports any that drift back on: the What's New dialog (site default
and the signed-in admin), the vendor's suggested prompts, follow-up chips,
tag, retrieval-query and autocomplete generation, community sharing, user
status, calendar, automations, the arena and channels (a manifest may turn
those two back on), voice mode, and the preset's built-in knowledge tools.
Notes stay on. Voice mode and Workspace > Knowledge are per-user
permissions, so admins keep them.
Successful sync/readback confirms measured settings; inspect undetermined,
dropped, and no_consumer entries for gaps. Sync alone does not prove a
model can answer or used graph tools; chat setup does. The pinned vendor's
POST /api/chat/completions attaches the preset's tools only when the caller
lists tool_ids: ["server:mcp:okg_<d>"]. The nightly real-container arm
is still missing.
Open WebUI names each tool <server id>_<tool>, so server ids use only
underscores: okg_<d> with each - as _, plus _write, _live,
_admin. Sync replaces connections an earlier release registered as
okg-<d>, re-points its presets, and names other models still using the old
id. Preset ids keep hyphens.
okg chat-instance lifecycle-receipt --kind {upgrade,rollback,retire} --out <new dir> writes a files-only record and performs no upgrade, rollback, or
retirement. Upgrade receipts require clean readback; rollback receipts reverse
a recorded upgrade and refuse a baseline outside the tested set; retirement
receipts require a live read showing OKG-owned surfaces removed, not that the
container is gone. Record the baseline first, verify container changes
separately, and keep the app database and graph intact.
Implementation: src/okg/substrate/chat/instance_cli.py
okg chat codex-shim --codex <path> --codex-home <dir> --mcp-url <url> --mcp-token-env <VAR> --mcp-tools <list> lets the chat site answer with the
operator's ChatGPT plan through the official Codex CLI. It serves
GET /v1/models and POST /v1/chat/completions (streaming and not) on
127.0.0.1:8790, and only there. Every call needs Authorization: Bearer
with the value of --token-env (default OKG_CODEX_SHIM_TOKEN, at least 32
characters); without it the shim does not start.
Cost and terms. Every person using the chat site spends the operator's personal ChatGPT plan. Sharing one person's access this way may breach OpenAI's terms, and that is the operator's decision. Each turn is a fresh Codex run that replays the whole conversation: a two-message chat used about 39,000 prompt tokens, and one grounded question about 121,000.
Isolation. --codex-home names a Codex home used only by the shim. Sign
it in once with CODEX_HOME=<dir> codex login. The shim never reads or copies
credentials. It refuses a home that holds AGENTS.md, config.toml or
skills, and refuses the operator's own home. Codex runs with an empty HOME
and bundled skills off, because --ignore-user-config alone still loads the
operator's instructions and skills. At start the shim runs codex debug prompt-input and refuses if that context appears. Runs use a read-only
sandbox in a scratch folder outside every repository, with the shell and
other built-in tools off. A run that executes a command, changes a file or
searches the web is stopped (codex_containment_breach). The shim refuses an
untested Codex version at start and stops answering if the CLI changes
underneath it.
Graph tools. When the site sends its tools, Codex calls the graph through
--mcp-url, and every call is approved. The shim checks that the server
exposes only --mcp-tools, at start and every few minutes. Start mcp-serve
with OKG_MCP_TOOL_ALLOWLIST set to the same list. A request without tools
runs with no graph at all. The site's per-tool switches cannot narrow the
set, so the first answer in a conversation names the tools Codex has.
Codex's progress notes arrive as reasoning, apart from the answer.
Only models that can call tools under these limits are listed (on codex-cli
0.155, gpt-5.5). Failures return a named error, such as
codex_not_signed_in or codex_rate_limited, and never an invented reply.
Point chat.provider at http://host.docker.internal:8790/v1 with
api_key_env: OKG_CODEX_SHIM_TOKEN and tools: provider. The site records
no graph tool calls of its own. With tools: provider, chat setup reads
the shim's okg_codex.tool_calls record and reports it as
provider_reported. It is ready only when at least one call answered.
Implementation: src/okg/substrate/chat/codex_shim.py
okg status --deployment <d> [--dsn ...] — deployment state
summary (publishing stage, sources, watermarks).
Implementation: src/okg/substrate/cli/main.py (_cmd_status)
okg status --json returns a diagnostic snapshot:
{
"deployment_name": "acme-ops",
"stage": "running",
"publishing": {
"latest_published_generation": "<checkpoint-id>",
"latest_published_target_generation": "<checkpoint-id>",
"latest_published_status": "published",
"latest_published_catalog_version": 5,
"latest_published_node_count": 120,
"latest_published_edge_count": 240,
"latest_catalog_version": 5,
"pending_generation": null,
"latest_attempt": {
"checkpoint_id": "<checkpoint-id>",
"status": "published | blocked",
"created_at": "<timestamp>",
"blocked": false,
"blocked_detail": "<reason; failed: <names>; hint: ...> | null"
}
},
"sources": [
{
"source": "github_issues",
"last_reconcile_at": "2026-04-23T10:00:00Z",
"projection_cursor": "<cursor>",
"n_node_facts": 120,
"n_edge_facts": 240,
"last_observed_at": null
}
],
"source_counts": {"generation": "<checkpoint>", "available": true, "reason": null},
"live_subtypes": [{"subtype": "document", "count": 10}],
"errors": [{"code": "status_probe_failed", "message": "..."}]
}n_node_facts and n_edge_facts are the source's node and edge rows in the
latest published generation, read from the count rollup the publisher
maintains, so they change only when a generation publishes, not during an
ingest. They are 0 before the first publish. When the published
generation has no count rollup, or OKG_IVM_CONSOLE_COUNT_ROLLUP=0 turns
the rollup off, they are null, the table shows ?, and
source_counts reads {"available": false, "reason": "..."}. This does not
change the exit code. last_observed_at is always null; it came from the
retired fact log.
stage is scaffolded, migrated, running, or unknown. Unobserved
snapshot values are null. publishing starts empty, and live_subtypes may
be absent when a probe fails before those fields are collected. Consumers must
inspect errors; missing fields are unavailable evidence, not a healthy result.
next_actions values, when present, come from the closed enum in
Error codes.
latest_attempt is the newest publish attempt on the default branch,
published or not; latest_published_* is the newest one that published.
They differ when a gate refused the latest publish. It is null when the
deployment has never attempted a publish, and absent when stage is not
running or migrated or when reading it failed (then errors names
latest_publish_attempt_unknown). An attempt retired with okg runtime supersede-checkpoint stays blocked, so status keeps exiting 3 until a newer
publish succeeds. A publish refused before it records an attempt, such as a
provenance refusal, does not appear here.
| Exit | Meaning |
|---|---|
| 0 | The status was read and the newest publish attempt published, or there has been none. |
| 3 | The newest publish attempt was blocked. Readers still see the previous version, or none. One error: latest_publish_blocked: line on stderr names the attempt and the reason. |
| 1 | A probe failed, or the newest attempt records a status that is neither published nor blocked. errors names it. |
okg status --watch shows a BLOCKED latest publish attempt line when the
newest attempt was blocked, keeps refreshing until interrupted, and exits 0.
When the latest checkpoint is blocked in repair_required (a source failed
after another source in its run succeeded), every publishing source-sync run
that does not repeat the checkpoint's exact selection, run mode and scope is
refused before it starts and records no admission, so the other sources stop
updating. Status then carries source_sync_hold (the checkpoint,
required_selection, failed_sources, not_admitted_since_block and the
dry-run escape_hatch_command), prints one error: source_sync_held: line
beside latest_publish_blocked, and --watch shows a HELD line. okg doctor --check sources fails with a source_sync_held error, or warns
source_sync_hold_unchecked when it cannot read the database; a read that
fails is source_sync_hold_unknown.
okg start --deployment <d> — start the deployment's Docker
stack. Reads deployment.yaml.docker.{compose_path, project_name}
and delegates to docker compose start. Idempotent (Docker
no-ops on an already-running stack).
Implementation: src/okg/substrate/cli/lifecycle.py (cmd_start)
okg stop --deployment <d> — stop the deployment's Docker
stack. Data persists (the volume is not removed).
Implementation: src/okg/substrate/cli/lifecycle.py (cmd_stop)
okg restart --deployment <d> — restart the deployment's Docker
stack (equivalent to okg stop then okg start, atomic via
docker compose restart).
Implementation: src/okg/substrate/cli/lifecycle.py (cmd_restart)
okg fetch-deployments --repo URL [--branch B] [--source-name N] [--list]
— clone (or git pull) the okg-deployments repo into the XDG
cache (${XDG_CACHE_HOME:-$HOME/.cache}/okg/deployments/<slug>/).
After fetching, okg install --profile <name> resolves any cached
profile via the search-path. The reference-deployments repository is not
published, and this copy names no default: pass --repo or set
OKG_DEPLOYMENTS_REPO, or the command refuses with repo_required.
Implementation: src/okg/substrate/cli/fetch.py (cmd_fetch_deployments)
okg sync {explain, health, poll, webhook} — source-sync producer
control plane.
Implementation: src/okg/substrate/cli/main.py (_cmd_sync)
Related: runbooks/source-sync-not-firing.md
okg sync explain --deployment <d> [--source <s>] [--path <p>]
— static source-sync policy (or trace one path event).
okg sync health --deployment <d> — producer health, queue depth,
DLQ state.
okg sync poll --deployment <d> — run declared cheap probes once
and enqueue any changed sources.
okg sync webhook --deployment <d> --source <s> [--payload-json <json> | --payload-file <file>] [--signature <sig>] [--no-publish] — map one webhook payload to a
source-sync workflow run (for replay / test). With neither payload flag the
payload is {}; when both are given, --payload-json wins.
okg trace {node} — historical fact-log and current Chronos-checkout inspection (one
sub-verb today).
Implementation: src/okg/substrate/cli/main.py (_cmd_trace)
okg trace node <node_id> [--generation-id <id>] [--dsn ...] — dump the
legacy fact log and narrate the node from the selected Chronos checkout.
Chronos-native nodes may have no okg.node_facts rows. For a present graph
node, derivation_story orders origin and selected lineage before up to 25
adjacent edges, includes edge provenance, and reports total/cap/truncation
metadata. A requested generation pins both the node and edge reads. Successful
graph narration uses diagnostic: fact_log_empty_graph_node_present and does
not recommend raw SQL; missing or unavailable graph state remains explicit and
may include a diagnostic recovery command.
okg ui --deployment <d> [--dsn <dsn>] [--generation <g>] [--host <h>] [--port <p>] [--auth-token-env <VAR>] [--dist <dir>]
— serve the read-only operator UI for one
deployment: the read API and the built frontend on one port (default
127.0.0.1:8765). The DSN resolves from --deployment as for the other read
commands. The frontend must be built first (npm run operator-ui:build);
with no bundle the command refuses with operator_ui_bundle_missing. Only
GET, HEAD and OPTIONS are served (HEAD for the page and assets; the
read API's routes are GET-only); anything else gets HTTP 405 with
operator_ui_read_only. It binds loopback only (localhost, 127.0.0.0/8,
::1), token or not; any other --host, or a port outside 1-65535, refuses
with operator_ui_bind_refused. Use an SSH tunnel for remote access. A
manifest with nomos.runtime_enforcement audit or enforce makes
--auth-token-env mandatory (viz_read_auth_required), whatever
OKG_NOMOS_RUNTIME_ENFORCEMENT says.
Implementation: src/okg/viz/ui_server.py; dispatch in
src/okg/substrate/cli/main.py (_cmd_ui)
Runtime-internal control surfaces. Most are mocked in tests and run against a real DBOS / Postgres cluster in production.
okg db {list, status, gc, dbos-gc, prune-blobs, current-runtime-dlq, acknowledge-runtime-dlq, remove-job-runtime-subsystem, drop, migrations, migrate-down, doctor}
— inspect AKMON database inventory, status, and migration
drift.
Implementation: src/okg/substrate/cli/main.py (_cmd_db)
okg db list [--cluster] [--cluster-dsn ...] — list deployment
databases.
okg db status [--deployment <d>] [--brief] [--top N] — inspect
one database.
okg db gc --cluster-dsn ... [--apply] — sweep ephemeral test databases.
Dry-run by default (report-only, with per-database sizes); --apply drops
ONLY databases matching the exact allowlist prefixes okg_test_,
okg_upgrade_verify_, and okg_golden. Non-allowlisted databases always
survive and never abort the sweep.
okg db current-runtime-dlq --deployment <d> — report raw, historical,
superseded, acknowledged, invalidly acknowledged, and still-blocking DBOS
terminal failures without rewriting workflow history.
okg db acknowledge-runtime-dlq --deployment <d> --request <json> [--apply --confirm-plan-id <sha256>]
— dry-run and then atomically append exact, witnessed dispositions for retained
terminal failures. Apply requires the exact fresh plan ID and an operator/admin
database role.
okg db dbos-gc [--stale-after-seconds N] [--status PENDING|ENQUEUED] [--confirm --apply]
— dry-run or cancel stale DBOS PENDING/ENQUEUED workflows.
okg db prune-blobs --kind image [--deployment <d>] [--grace-seconds N] [--confirm --apply]
— delete rows of one kind from the blob store okg.blobs that nothing
references any more. Dry run by default; --confirm --apply deletes. A row is
deleted only when no retained generation references it and its last_used_at
is older than the grace window (default 86400 seconds). The grace window must be
at least 3600 seconds; a smaller value is refused with
blob_prune_grace_too_short, because an ingest that just reused a row
references it only once its run publishes. When the database has no registered
graph node storage, for example a fresh environment clone that kept okg.blobs
but not the graph, nothing reads as referenced: a dry run reports this in its
note, and --apply refuses with blob_prune_no_graph instead of deleting
every row past grace. For image, a
reference is the png.id of an image value in any retained row of the graph's
node storage, its substitute-declarer storage, or the attribute overlays.
History compaction deletes the rows of the generations it retires, so an image
only those rows used becomes deletable; overlay rows are not compacted.
The scan reads in bounded batches inside one snapshot; deletes run in bounded
batches under a lock timeout and re-check the grace window, so an ingest that
reuses a row while the command runs keeps it. The JSON report lists the rows
scanned per relation, the rows kept (referenced, within_grace,
refreshed_since_scan), and the rows and bytes deleted or, on a dry run, the
ones that would be.
okg db remove-job-runtime-subsystem [--deployment <d>] [--confirm --apply] [--accept-retired-ledger-rows --retired-rows-dump-ref <path>]
— irreversibly remove obsolete OKG job tables, views, functions, leases, and
cron entries after active-work safety checks. The retired subsystem has no
worker left to drain its rows, so --accept-retired-ledger-rows (which
requires a pre-drop dump reference, and records the destroyed row counts in the
migration ledger) is the audited way past a refusal that would otherwise be
permanent.
okg db drop --name <db> --cluster-dsn ... [--confirm --apply]
— drop one database safely.
okg db migrations [--deployment <d>] — report schema migration
ledger status.
okg db doctor [--deployment <d>] — run database health check.
okg db role-passwords [--deployment <d>] [--apply [--builtin-dev-passwords]] [--json] — report or replace the passwords of okg's database login roles.
Without --apply it lists which managed roles still accept a
built-in public password and whether the owner-only secrets file exists. With
--apply it gives okg_mcp and app_rw LOGIN and a random password, also
rotates app_ro and catalog_admin when they can log in, proves each login,
and writes the role connection strings to a mode-0600 file under
$XDG_CONFIG_HOME/okg/role-secrets/ (override: OKG_ROLE_SECRETS_FILE). It
never prints a password and sends PostgreSQL only the SCRAM verifier. A role
whose override variable (OKG_MCP_RO_DSN, OKG_MCP_RW_DSN) is set is left
alone. --builtin-dev-passwords sets the public built-in passwords instead and
is refused unless OKG_ALLOW_BUILTIN_ROLE_PASSWORDS=1. Local databases only. See
Security: database role passwords.
embed-backfill --set <name> [--max-usd <usd>] [--dry-run] links one
registered set's nodes to documents at the latest publish and embeds each
distinct document lacking a vector; no node rows; publish to read them. One
run per set; a paid set needs --max-usd, counted per set in
okg.llm_calls. --dry-run reports missing documents, tokens and dollars
per set and subtype from rates in okg.llm_calls (else dollars unknown),
without calls or writes. embed-show --set <name> --node <id> prints a
node's document, doc_hash, vector presence and readable_at_pin.
embed-index --set <name> --apply builds the registered index
concurrently, then drops the others. semantic-index --apply builds the
node-column HNSW index. Paid openrouter: calls: backfill (capped,
recorded), one per semantic query.
okg db reclaim-index-space --deployment <d> [--apply] [--min-index-bytes N] [--publish-lock-wait-seconds S] [--lock-timeout-seconds S] [--json] — rebuild
the indexes of okg-schema tables that okg doctor --check disk reports as
index_larger_than_table. A failed or cancelled publish leaves the index pages
of the rows it rolled back; VACUUM empties the table but never shrinks an index. Without --apply the command lists the flagged tables, their table
and index bytes, and the REINDEX TABLE CONCURRENTLY statements it would run;
it exits 1 with dry_run_blocked when --apply would be refused now, naming
each blocker in blocked_by.
With --apply it holds the deployment's publisher lock for the whole run, so an
ingest's end-of-run publish during the run gets PublisherLeaseHeld. It
refuses (each with its own data.reason) when a publish holds that lock
(publisher_lock_held), when a source run of the deployment is still writing
(source_run_in_progress, the signal that also refuses a publish), and when a
target table has an invalid index (invalid_index_present,
for example a *_ccnew left by an interrupted rebuild; the refusal prints the
DROP INDEX CONCURRENTLY command). A writer that starts later makes the
rebuild's bounded wait fail (reindex_failed; the command then drops the
invalid <index>_ccnew / _ccold copies its own rebuild left, never another
index, and reports any it could not drop, including a TOAST _ccnew, with its
drop command) or the fingerprint differ (fingerprint_changed). It rebuilds one table at a time
with a bounded lock wait, and compares an authority fingerprint before and
after: index and constraint definitions, owners, grants, reloptions, comments
and row digests of the rebuilt tables, plus row digests of
the generation ordinals, the Chronos checkpoints, the
record-set and record-authority tables and the graph rows. Any difference fails
the command with both values and names the tables it already rebuilt (a rebuilt
table stays rebuilt). The fingerprint reads every row of those tables twice;
a source run that starts after the in-progress check makes it differ, so run
the command when no ingest is scheduled.
The publisher worker, okg run --once, okg ingest (once, after its last
layer; so also the source-sync workflow and provisioning) and merge-back
also rebuild bloated indexes on their own after a
successful publish; the MCP connector ingest and the exchange
receiver leave it to the next worker or run cycle. After the publish has
committed and released its locks, the rebuild compares each btree index of
the two graph tables with its estimated packed size (from pg_class.reltuples and
pg_stats, after an ANALYZE of the suspect tables). It rebuilds with
REINDEX INDEX CONCURRENTLY each index that is at least
OKG_POST_PUBLISH_INDEX_REBUILD_RATIO (default 1.5) times its estimate and at
least OKG_POST_PUBLISH_INDEX_REBUILD_MIN_BYTES (default 64 MiB) over it.
OKG_POST_PUBLISH_INDEX_REBUILD=0 turns this off. Each ANALYZE and each
rebuild takes the publisher lock without waiting and releases it at once;
each lock wait lasts at most
OKG_POST_PUBLISH_INDEX_REBUILD_LOCK_TIMEOUT_SECONDS (default 5; one
concurrent rebuild can wait several times, plus its cleanup drop), and no
step starts after OKG_POST_PUBLISH_INDEX_REBUILD_BUDGET_SECONDS
(default 30). Before each step, the rebuild stops and leaves the rest to the
next successful publish when a publish is waiting for the lock or holds it, a
source run is writing, or another session's transaction with a snapshot is older than the lock timeout.
The old-snapshot stop is retried first: the run waits OKG_POST_PUBLISH_INDEX_REBUILD_OLD_SNAPSHOT_RETRY_WAIT_SECONDS
(default 1, doubling) and checks again, at most
OKG_POST_PUBLISH_INDEX_REBUILD_OLD_SNAPSHOT_RETRIES times in all (default 3),
inside the budget and before the lock is taken; the checks are listed under
old_snapshot_waits in the report. A transaction that stays open costs each
cycle with rebuild work that wait (7 s by default); 0 retries stops at once.
A publish that starts during the rebuild therefore waits for one step at
most. The rebuild first finishes any graph-index change a deferred okg migrate left pending, and drops an invalid _ccnew/_ccold copy an earlier
rebuild of the same table left when no index build is in progress there. It
skips a table that has any other invalid index and reports it with its drop
command. It never fails the publish. Its report is in the publish summary
under post_publish_index_rebuild. That includes status, rebuilt (per
index: bytes before and after, estimate, ratio, seconds), reclaimed_bytes,
seconds, max_publisher_lock_hold_seconds, cleaned_up_leftovers and
leftover_invalid_indexes. It never judges an index named okg_dattr_*
(the declared-attribute indexes below), and it leaves a graph table for the
next run, listed in deferred_graph_tables, while another session holds the
graph-index DDL lock (a declared-attribute index build, a cold-history
rotation, a migrate).
okg db attr-index reconcile [--deployment <d>] [--dsn <dsn>] [--apply] [--retry-failed] [--lock-timeout-seconds S] [--json] builds one partial
expression index per declared filterable attribute on the Chronos node
table, named okg_dattr_n_<hash> (MCP filter reads use them by
default, see okg mcp-serve). It builds none on the edge table, and drops an
okg_dattr_e_ index an earlier version built (last_reason
kind_not_indexed; doctor lists it at INFO until then). Without --apply it
prints the plan (would_build, would_drop, failures stopped by the cap,
okg_dattr_ indexes it does not own) and changes nothing. --apply needs
--deployment (refused, exit 1, without it): the publish-lock and
source-run checks below are per deployment, and the command assumes none;
--dsn may still name the database. --apply runs the
reconciler once and also opts the deployment in: from then on the worker's
okg_declared_attr_index_maintenance workflow, on the
okg-<deployment>-index-maintenance queue every five minutes, keeps the
indexes in step with the declarations, then runs the drift checks below. It
skips the drift checks when the reconcile was refused, preempted or failed,
and a run whose reconcile or drift check raised ends as a failed workflow
with the error in doctor's last_run. OKG_DECLARED_ATTR_INDEXES=off makes
that workflow do nothing. A run holds the graph-index DDL lock (refused,
exit 1, while another session holds it), drops its own failed and retired
indexes first, then builds each missing index with CREATE INDEX CONCURRENTLY outside any transaction. It waits up to
--lock-timeout-seconds (default OKG_DECLARED_ATTR_INDEX_LOCK_TIMEOUT_SECONDS
or 600) for older transactions instead of failing. It builds nothing while a
publish holds the publisher lock, a source run is writing, or a transaction
is older than 60 seconds, and nothing on a table that has any invalid index,
whoever made it. Text indexes need a database collation that orders strings
by code point (libc C, POSIX, C.UTF-8, C.utf8; builtin C or
C.UTF-8). A failed build is retried after 1, 2, 4 and 8 runs (at most a day
apart); after five consecutive failures it stays failed until --apply --retry-failed, and the command exits 1. okg migrate and catalog apply
cancel a build that holds the lock; the build is dropped on the next run and
not counted as a failure.
It also keeps one okg_dstat_ statistics object per declared attribute.
okg doctor --check declared-attr-indexes reports the lifecycle states,
invalid indexes on the graph tables, the failure cap, the collation and long
values, and one attr_not_indexed finding per declared node attribute
without a ready index; it exits 1 on any ERROR finding: a capped failure, a
ready index that is gone, or a declared attribute never enabled, refused by
the collation, or with maintenance off (OKG_DECLARED_ATTR_INDEXES=off, read
from doctor's own environment).
Upgrading to the version that drops attr_index. Migration 0043_attr_index_teardown
drops okg.attr_index, its coverage markers, rebuild queue and derive front
(about 11.7 GB on one large deployment). Before deploying, on the old code: run
okg db attr-index reconcile --deployment <d> --apply and check that
okg doctor --check declared-attr-indexes reports OK. Then stop the worker
and the MCP servers (a step-3 reader holds locks on okg.attr_index, and the
migration waits at most 5 seconds for them), deploy, run okg migrate --apply, and start them again. okg db rebuild-attr-index and okg doctor --check attr-index-coverage no longer exist. There is no cheap rollback:
old code would recreate empty tables, so recovery is a forward fix.
okg agent connect --deployment <d> [--agent claude-code|claude-desktop|cursor|codex ...] [--writes] [--dry-run] [--replace] [--probe-timeout <s>] [--json] registers
the deployment's MCP server with each agent and installs the bundled user skills
where it reads them (connect an agent).
Without --agent it connects every agent present and reports the rest as
not found (exit 0); a named agent that is absent is not found (exit 1).
Each agent's result is connected, unchanged, not found or refused
(an unparseable config file, or a duplicate entry for the deployment without
--replace); any refused exits 1. Before writing, it starts the entry the
way the agent will (cwd /) and refuses with the code okg mcp-serve
printed, writing nothing, when the server does not list its tools; it also
refuses deployment_not_found, agent_connect_ephemeral_okg (the running
okg is inside uv's cache), agent_connect_writes_need_role_passwords
(--writes while okg_mcp or app_rw still uses its built-in password),
agent_connect_server_failed, agent_connect_server_timeout and
agent_connect_tools_missing. The entry runs okg agent-runtime-launch
with --workspace-root <deployment folder>, --deployments-dir, and, when
they exist, --local-env and --role-secrets-file; the launcher refuses a
path that is not absolute, missing, a symlink, the wrong kind, another
user's, writable by group or others, or writable by the agent run (inside
the profile's write roots) with agent_runtime_path_binding_invalid, and
--allow-builtin-role-passwords for a profile that can write. A
write-enabled profile's write root is the deployment folder minus its
credentials file (.okg/local.env, and the role-password file if it sits
there): the launcher exports the excluded paths as
OKG_AGENT_DENIED_WRITE_PATHS, and check_agent_write in
agent_runtimes/launcher.py refuses a write to them with
agent_runtime.write_denied. Every distinct entry is started once before
anything is written; the Codex entry gets startup_timeout_sec (60, or three
times the measured start, at most 180). okg agent disconnect --deployment <d> [--agent ...] [--dry-run] removes the entry and the skills
connect's sync manifest records; a skills folder another agent still reads
for a connect-written entry is kept. Both commands refuse Claude Desktop while
the app runs (it rewrites its config from memory), read each written file
back, and keep <file>.okg-backup as the latest version of the file with no
connect-written entry.
Implementation: src/okg/substrate/library/agent_runtimes/connect.py
(run_connect, run_disconnect)
okg mcp-serve --deployment <d> [options] — run the MCP server for one
deployment. It refuses to start until the deployment has a published
generation. Connect an agent shows how to use it;
MCP graph reads defines the tools.
| Option | Meaning |
|---|---|
--deployment <d> |
The deployment to serve; its manifest names the database (default default, a deployment of that name; $OKG_DEPLOYMENT is not read). |
--dsn <dsn> |
A database address to use directly, checked against the deployment's manifest when one exists. $OKG_DSN is not a fallback here. |
--branch <name> |
The graph branch to read (default default); one server reads one branch. |
--embedder <name> |
The semantic search backend for a --dsn launch: hash (tests) or sentence-transformers:<model>. |
--allow-writes |
Also serve the write and admin tools, over a separate write account. Off by default. |
--tools <name> |
Serve only a named tool profile: discovery, reader, full, or one the deployment declares. |
--tools-ceiling <name> |
Let a session widen its own tools up to this profile without a restart. |
--transport <t> |
stdio (default), for an agent that starts the server, or streamable-http, which serves http://<host>:<port>/mcp. |
--host <address> |
The HTTP bind address (default 127.0.0.1). Without --auth-token-env or --oidc-auth the HTTP server has no login, and --allow-writes on another address is refused. |
--port <n> |
The HTTP port (default 8100). |
--allowed-host <host:port> |
Accept this Host header; repeat for each name clients use. |
--auth-token-env <VAR> |
Require a bearer token on HTTP, read from environment variable VAR. |
--multi-user-principals |
Name each request's user from a header a trusted proxy sets; needs --auth-token-env. See Multi-user HTTP principals. |
--oidc-auth |
Verify each person's own token from the deployment's identity provider. HTTP only, and not with --auth-token-env, --multi-user-principals or writes; see Multi-user HTTP principals. |
--auth-token |
Always refused: a token on the command line is visible to other processes. |
Implementation: src/okg/substrate/cli/main.py (_cmd_mcp_serve)
OKG_ATTR_FILTER_INDEX picks which index narrows an attribute-filtered
filter, map or search read before the exact predicate decides each
row; both values return the same rows in the same order:
expression, or unset (the default): the declared node expression indexesokg db attr-index reconcilebuilds, at the branch head, for each attribute whose index is ready. Every other declared attribute the predicate narrows by does not narrow the read, and the response carries{"pushdown": "not_covered", "attrs": [{"attr", "reason"}]}, with reasonkind_not_indexed(an edge attribute: no edge index is built),index_missing,index_building,index_failed,declaration_changedorpinned_generation(a read pinned to an older generation). The ready attributes still narrow. Prefix filters are not narrowed by any index, except asearchwhereprefix onpath(below).off: no index; the exact predicate scans the effective attributes.
expression also lets a query-less search with a top-level
where prefix on path or attrs.path narrow through the path-prefix
index, under Nomos off only. path must be a declared filterable text
attribute whose index is ready. Otherwise nothing narrows and the response
carries {"pushdown": "not_covered", "attrs": [{"attr": "path", "reason"}]}. The reason is not_declared, a readiness reason above,
pinned_generation, path_index_missing, or broad_prefix: more than
10,000 visible candidates.
attr_index and dual, values from earlier versions, are refused:
an attribute-filtered read fails with invalid_configuration and
data.reason removed_value; other reads still work, and the MCP server
logs one ERROR at start. OKG_ATTR_FILTER_DUAL_SAMPLE_EVERY and
OKG_ATTR_FILTER_DUAL_TIMEOUT_MS have no effect; the server names them in
one WARNING at start. Any other value fails those reads with
invalid_configuration. A ts bound that the strict ISO-8601 reading
(okg.attr_ts_v4) refuses takes its attribute out of the narrowing
(reason bound_not_strict_iso; the other clauses still narrow); stored
ts values compare through that reading, and a float range bound
compares in double precision, so a stored number beyond double range no
longer fails the read with SQLSTATE 22003. okg doctor --check declared-attr-indexes reports each declared node attribute without a
ready index (attr_not_indexed).
okg nomos {provider, binding, grant} {add, disable, list} [--dsn <dsn>] --deployment <d> [--json] — register identity providers, map users to
Nomos subjects, and grant subjects actions, without Python. add never
rewrites or re-enables an existing row; disable keeps the row and
records who, when and why; list includes disabled rows.
okg nomos provider add --for-oidc-auth fills the provider from the
manifest's nomos.oidc_auth block. Usage and refusal codes:
Nomos IAM runbook.
Implementation: src/okg/substrate/cli/nomos.py (cmd_nomos)
okg runtime {bootstrap, openshell-policy, worker, debug-workflow, cancel-workflow, smoke, drain, reset, escalation, measure-recovery, get-event, list-schedules, trigger-schedule, enqueue-fenced-recovery}
— DBOS runtime layer.
Implementation: src/okg/substrate/cli/main.py (_cmd_runtime)
Related: runbooks/dbos-runtime.md
okg runtime bootstrap --deployment <d> — install DBOS schema and role
split (idempotent).
okg runtime openshell-policy --deployment <d> [--dsn <dsn>] [--output <path>] [--json]
— render an OpenShell sandbox policy for the deployment's runtime worker from
deployment.yaml and source_registry.yaml: filesystem access limited to the
deployment root and Python venv, egress from declared source endpoints plus the
deployment Postgres DSN, and landlock.compatibility set to hard_requirement.
The command fails loudly when an endpoint cannot be derived; it does not
generate wildcard egress. --dsn takes precedence over ambient OKG_DSN,
including registry ${OKG_DSN} placeholders. --output writes YAML to a
file and still prints the rendered policy to stdout unless --json is
selected. It only renders policy; a separate runtime proof is required
before claiming that an OpenShell-contained worker can run and publish
under hard Landlock plus proxy-mode egress.
okg runtime worker --deployment <d> — long-lived DBOS worker.
okg runtime debug-workflow <workflow_id> --deployment <d> — print a
structured debug report for one DBOS workflow.
okg runtime cancel-workflow <workflow_id> --deployment <d> — cancel one
ENQUEUED/PENDING/DELAYED workflow wedging the queue or blocking
readiness; refuses anything else.
okg runtime smoke --deployment <d> — LISTEN/NOTIFY round-trip test.
okg runtime drain --deployment <d> — wait for in-flight DBOS workflows on
the deployment's queue to finish (before runtime reset).
okg runtime reset --deployment <d> --confirm — reset DBOS state
(dev only; refused without the flag).
okg runtime escalation {respond, list, show} — operator responses to
SDK escalations.
okg runtime measure-recovery --deployment <d> — measure DBOS
worker-recovery time (Decision 5).
okg runtime get-event <workflow_id> <key> --deployment <d> — read a
workflow event by key.
okg runtime list-schedules --deployment <d> — list DBOS schedules.
okg runtime trigger-schedule <s> --deployment <d> —
trigger one DBOS schedule out-of-cycle.
okg runtime enqueue-fenced-recovery <doctor_workflow|okg_ci_resource_monitor|okg_ci_git_history_reconcile> --deployment <d> --promotion-id <id>
— enqueue exactly one allowlisted recovery-witness workflow under the active
promotion owner while recurring schedules stay removed. Refuses a missing,
released, or differently owned fence.
okg runtime pause-schedule <s> --deployment <d> — persistently pause one
schedule, verify that exact row is paused, and fence any running internal
scheduler wrapper before returning. A paused okg_publisher_cycle stays
ready: doctor warns publisher_workflow_paused (_paused_long after 24
hours); a failed, stuck or cancelled running cycle fails until a cycle
succeeds, including one cleared during an upgrade.
okg runtime resume-schedule <s> --deployment <d> — resume one
paused schedule and verify that its persisted state is active.
Rarely-run AKMON runtime maintenance.
okg consistency-rerun [--deployment <d>] [--generation <id>] —
run deployment post-publish consistency checks for an existing
published generation.
Implementation: src/okg/substrate/cli/main.py (_cmd_consistency_rerun)
okg runtime deferred-edges --deployment <d> [--json] — list cross-source
edges waiting for another source's record: counts by producer, edge type,
endpoint subtype and state, the oldest age, and a bounded sample of ids. The
ids are operator-only. okg doctor --check deferred-edges reports the same
counts and warns past consistency.deferred_edge_warn_after.
Implementation: src/okg/substrate/cli/runtime.py
okg migrate [--apply] [--dsn ...] — apply the v1 schema
idempotently. Dry-run by default. It first builds missing graph read
indexes concurrently and drops retired ones.
After the schema commit, migrate also retires, once, the repair requirement
of a repair_required checkpoint that an earlier release cut for a partially
failed ingest. It runs the same write as okg runtime supersede-checkpoint --apply, with actor okg migrate and reason
upgrade_3342_publish_incomplete, both recorded on the checkpoint. Nothing is
deleted and the checkpoint stays blocked. A checkpoint this release blocks on
purpose, because a failed source's changes could not be undone, carries
freeze_policy: post_3342_undo_unfinished and is left alone. A bare okg migrate covers every deployment in the database; okg deployment up passes
--deployment, so it covers that deployment only. Check
incomplete_checkpoint_retirements in the output: it lists each retirement
(empty when there was nothing to retire), and an entry with skipped means a
publish moved the checkpoint meanwhile, so the next migrate retries it. okg deployment up --skip-migrate skips this step too. To see what would be retired first, run okg runtime supersede-checkpoint without --apply.
With --deployment, migrate also retires a repair_required checkpoint whose
held-back sources have all been removed from the source registry or renamed
(okg runtime worker start does the same, as actor okg runtime worker). It
retires only when the deployment and code repositories are committed, the
checkpoint was recorded for that deployment name, and no unfinished undo or
kept rerun blocks publishing; otherwise it changes nothing and says why in
source_contract_retirements. Without --deployment it lists such
checkpoints as candidates and retires nothing. Every command that migrates a
named deployment does this step too: okg provision, okg init, deployment
promotion and the exchange harness, as well as okg migrate --deployment. A
running worker that started before the source was removed must be restarted to
retire it.
Implementation: src/okg/substrate/cli/main.py (_cmd_migrate)
okg snapshot-load --deployment <d> --snapshot <file> [--apply]
— load a JSON snapshot as the deployment's first published
generation (renamed from bootstrap).
Implementation: src/okg/substrate/cli/main.py (_cmd_bootstrap)
Investigation surfaces. Not in the operator hot path; reach for these when something is wrong.
okg branch {list, show, create, abandon, delete} — inspect AKMON
branches (okg.branches).
Implementation: src/okg/substrate/cli/main.py (_cmd_branch)
okg branch list --deployment <d> — branches with role / status /
latest-gen.
okg branch show --deployment <d> <name> — one branch's metadata
- generation-count summary.
okg branch create --deployment <d> --role {experiment|quarantine} [--base-branch <b>] <name>
— create an experiment or quarantine branch.
okg branch abandon --deployment <d> <name> — mark an experiment
or quarantine branch as abandoned (refuses the default branch).
A metadata tombstone only: the Chronos branch row survives and its
interval space stays allocated. Use okg branch delete to remove it.
okg branch delete --deployment <d> [--cascade] [--confirm] <name> —
remove the Chronos branch row, decrementing the parent's child count and
requesting an interval GC pass. Refuses main, the default branch, and
any branch owned by a different deployment. Dry-runs without --confirm;
refuses a branch that still has children unless --cascade is passed.
Deleting (or abandoning) an orphaned fork branch unblocks history compaction for its parent — a live child branch still reads the parent's history, so compaction refuses while one exists.
okg perf {list, show, compare} — inspect and compare
performance captures. okg profile is the deprecated old name: it still
runs and prints a rename warning on stderr for one release.
Implementation: src/okg/substrate/cli/main.py (_cmd_profile)
okg perf list [--deployment <d>] — profiling captures.
okg perf show <capture_id> — one profiling capture.
okg perf compare <a> <b> — diff two profiling captures.
Runtime developer scaffolders + lints.
okg lint deployments — boundary / cross-cutting lints. Walks
every deployment manifest and checks
runtime.workflows Python modules for imports outside the
runtime's allowed surface.
Implementation: src/okg/substrate/cli/main.py (_cmd_lint)
(see parent — only sub-verb today)
okg new-module <name> [--in-tree | --deployment <d>] — scaffold a new
ontology module skeleton (module.yaml, schema.yaml, tests/,
README.md) under src/okg/substrate/library/ontologies/<name>/ (the
default, --in-tree) or under modules/<name>/ in deployment <d>'s
folder, found as okg deployment add-source finds it (a name, folder or
deployment.yaml path). An unknown deployment is refused; nothing is
written.
Implementation: src/okg/substrate/cli/main.py (_cmd_new_module)
Retired, and hidden from okg --help. It used to write a reader skeleton
that was not a working reader. Any invocation now exits
1 with the error command_retired and writes no file. To author a source,
copy the working connector in examples/json-record-connector/ and add it
with `okg deployment add-source --registration
okg schema <yaml-path> — emit JSON Schema for one authored file
(deployment.yaml, source_registry.yaml, extractors.yaml,
module.yaml, schema.yaml).
Implementation: src/okg/substrate/cli/main.py (_cmd_schema)
Bootstrap a new deployment or inspect one that exists.
The retained packaged release entry supports --update-source <existing-source-id>
with the original installation arguments and --verify-query. It verifies the
original release and completed installation, then runs scoped inline ingest
including declared predecessors. It does not bootstrap or provision again.
See packaged source updates
for the command, refusals and supported scope.
For authorized installed reads, the same retained entry accepts
--search-query <text> and optional --search-generation <published-id> with
the original installation selection. It invokes the existing lexical search
under the verified deployment's enforced Nomos policy, preserving normal
results and source metadata. Empty allowed results succeed. Search cannot be
combined with update or staging flags and does not ingest or publish.
okg install [--profile <name>] [--deployment-name <n>] [--repo-root <r>] [--scaffold-only] [--no-publish] ...
— the one documented way in, and the only verb classified
newcomer_bring_up (see
src/okg/substrate/cli/entry_roles.py).
The framework owns the verb: --profile selects what to build, and a profile
shipping its own installer contributes its flags to this parser. With no
--profile it installs the minimal starter: one small ontology module, no
sources, Nomos runtime enforcement off, migrated into the database
--postgres-dsn names and published as an empty first generation; grow it
with okg deployment add-source. okg install --help lists every
installable profile. --profile academic-researcher runs that profile's own
installer (Docker + Postgres + scaffold + agent-runtime MCP registration,
then a conversational wizard for source setup). Any other profile — a
library profile such as workspace-graph, or a reference deployment fetched
with okg fetch-deployments — is written out and brought up to a first
published generation. A flag one profile's installer contributes (such as
--orcid) is refused when another profile runs, rather than ignored. Profile init_questions become
--<question-id> flags. --scaffold-only skips a profile's own
installer; --template <name> scaffolds a legacy library template.
A successful install prints a short summary of about ten lines: the graph's
name, the deployment folder, the database, how many records and links the
graph now holds, one search to try, and how to connect an agent. It prints a
warning only when you must act on it, with the fix. --verbose prints the
full report instead: every file written, the module composition, the first
publish proof, and every warning, including skipped_extends (an optional
link to a module the profile does not load; it needs no action).
--json prints the same detail as one JSON document, with the same keys
with or without --verbose. A profile with its own installer
(academic-researcher) and --template installs ignore --verbose.
--dry-run prints what the install would do and exits without doing any of
it — no file or directory created, no database connection, no container
started, no schema migration or publish. That holds on every route install
and init take; the flag is framework-owned, so it is present whichever
profiles are installed.
What the plan contains depends on the route, so read plan_complete first
— it is on every plan, and false means the command could not describe the
run (a required answer is missing, say). The status
field is dry_run_ok or dry_run_blocked, and a blocked plan exits nonzero
so a wrapper script cannot read it as a clear one.
- Profiles served by the generic scaffold (
minimal,workspace-graph, fetched reference deployments,--scaffold-only, and the legacy--templateroute) reportfiles_that_would_be_written,directories_that_would_be_created, and whether the deployment directory would be created, refused, or overwritten — plus, under--force --yes, thefiles_that_would_be_deletedthat overwrite would destroy. - Profiles shipping their own installer (
academic-researcher) report a plan of what that installer probes instead: Docker daemon and image presence, chosen ports, scaffold-directory collision, which sources will need setup approval, and agent-runtime config collisions. Implementation:src/okg/substrate/cli/main.py(_cmd_install)
An alias for the documented door: okg init <args> runs
okg install <args> --scaffold-only, and says so on stderr. It keeps older
scripts and runbooks working; new guidance should name okg install. Flag
surface, semantics, and end state are the door's — see
okg install.
Implementation: src/okg/substrate/cli/main.py (_cmd_init_alias)
okg deployment {show, new, list, fork-worktree, gc-forks, gc-scaffolds, env, isolated, release, up, apply, ready, plan, lint, profile, nomos-rollout-audit, nomos-source-lineage-rollout-audit, bug-report, bootstrap} —
deployment authoring, host inventory, worktree forks and scaffold GC,
environment, isolated worktree database lifecycle, release contract,
deploy/upgrade, readiness, Nomos rollout audits, and bootstrap workflows.
Implementation: src/okg/substrate/cli/main.py (_cmd_deployment)
okg deployment new <name> [--deployments-root <dir>] [--force] [--yes] --apply
— create a loader-valid starter deployment. --apply is required and writes its files.
okg deployment show <d> — print the resolved deployment manifest
(with runtime block). <d> is a deployment name or a path to its
deployment.yaml.
okg deployment env <d> — inspect or write the deployment local
environment profile used for manifest DSN interpolation.
okg deployment env doctor <d> — diagnose DSN authority without
writing local env. The report names manifest env refs, process env,
local env, worktree fallback, the selected authority source, target
database, conflicts, and recovery commands. Because doctor is the
nested diagnostic verb, it is reserved under deployment env.
okg deployment plan <d> — inspect resolved deployment wiring before
runtime. The plan shows source declarations, module defaults, enrichers,
extractors, alias resolver backends, declared job descriptors, checks,
views, and operator surfaces.
okg deployment lint <d> — run static authoring diagnostics over the
deployment manifest and related config. Findings include a code,
severity, stage, location, why it matters, and next action.
okg deployment compat <d> [--commands FILE] [--against CONTRACT] [--json]
— check, before an upgrade, whether a deployment works with the okg that is
running. It needs no database. It imports the deployment's own adapter
modules, so anything those modules do at import time happens. It runs:
okg deployment lint.- For every source-registry entry, with
code_reposexpanded: the module imports, the class exists (a missing one lists the classes the module does define), and the params fit the constructor. This imports the deployment's own adapter modules, as ingest would; it constructs nothing. - When the deployment runs Nomos (
nomos.runtime_enforcementisenforceoraudit), the Nomos deployment policy audit withcode_reposexpanded, asokg provisionruns it. - With
--commands: eachokgcommand line in the file, one per line, still parses against this okg, after the same rewritesokgapplies and with a profile's answer flags when the line names--profile.#lines are comments.okg pact,--helpand--versionlines are reported as not checked; nothing is executed. - With
--against: the breaking changes between an older release contract and this okg's. Changes that touch a key, source class, module, verb or flag this deployment uses are listed first, and each is a blocker,compat.breaking.touches_deployment, whose fix points at the migration note in the release notes; breaking changes the deployment does not use are listed but do not block. Without--against, a deployment that namesrelease.tested_with: "<version>"(besiderelease.channel; quote it, since an unquoted0.10is the number 0.1 and is refused as unknown) is compared with that release's contract. The wheel attached to each GitHub release carries the contract of every earlier release, gzipped underokg/_release_contracts/. Two limits: comparisons start after v0.2.0, the first release with a contract; and any other build -- a dev wheel, an install from a checkout, a container image -- carries only the contracts present in its build directory when it was built, normally none. When the named release's contract is not carried, compat reportscompat.release.tested_with_not_bundledas a warning (exit 0) and names the fix: pass--againstwith that release'sokg-contract-v<version>.jsonasset. A carried contract that cannot be read makes the verdict unknown (exit 2).
| Exit | verdict |
Meaning |
|---|---|---|
| 0 | compatible |
No blocker. Warnings may still print. |
| 1 | incompatible |
At least one blocker, including a breaking change since the compared release that touches something this deployment uses. |
| 2 | unknown |
The deployment was not found, or a check could not run, so compatibility is not known. It is never reported as compatible. |
A release contract (okg.release_contract.v1) is okg's public surface, read
from the installed package: CLI verbs, every spelling of each flag with how
many values it takes, positionals by position, exit
codes, deployment.yaml and source-registry key paths, reserved source
params, source classes, ontology modules and MCP tool names.
scripts/release/build_contract.py, given --out FILE, writes the running okg's
contract. Environment variables, MCP tool parameters, error codes and
profile-injected flags are not in it yet.
okg deployment profile {list,explain} — inspect built-in deployment
profiles and their defaults without reading secret values.
okg deployment isolated {list,gc} <d> — list or garbage-collect
worktree-isolated deployment databases. gc is dry-run unless --yes
is supplied.
okg deployment up <d> [--target dev|prod] [--include <source> ...] [--skip-migrate]
— idempotent deploy/upgrade path. It resolves the target database,
applies pending migrations when needed, applies the catalog, runs
scope-complete ingest/publish, and reports the runtime-worker restart as
the next action. --target dev uses the worktree-isolated
database; --target prod uses the declared deployment database.
For a deployment that runs from an installed okg package, okg up stops a
running worker, reloads it and starts it again through the deployment's own
worker service: on Linux the enabled instance okg-runtime-worker@<d>.service
whose shared template is the one OKG renders for <d>, on macOS the launchd
plist. okg up and okg up --plan refuse before changing anything
(installed_upgrade_worker_unsupervised) when a reload of a systemd system
service is needed and okg up is not running as root (run it with sudo), or,
on Linux with no service of its own, when the deployment has
runtime.enabled: true, a running worker, or a worker that ran before (install
the service as root first: okg dogfood service install --deployment <d> --adapter systemd --apply), or when the reload check cannot be read. Per-user
systemd worker services are planned as the first follow-up after 2026-10-17.
okg deployment backup restore <d> --archive <file> [--apply] [--json] —
for a deployment that runs from an installed okg package: go back to the
backup okg up took before an upgrade. Without --apply it prints the plan
and changes nothing. It refuses (installed_restore_refused) any archive
other than the recorded pre_upgrade_archive, an archive without its
owner-only <archive>.json sidecar, one whose bytes changed, one taken from
another database, and a role that is not a superuser when the database uses
TimescaleDB (or, without it, lacks CREATEDB or membership in the database
owner's role). With --apply it stops the runtime worker,
restores the archive into a fresh database on the same server through the
verified restore path, replays the archive's owners, grants and default
privileges, checks data and access against the sidecar, copies the live
database's owner, grants and settings, and swaps it in by renaming; the live
database is kept as <database>_pre_restore_<time>. It refuses while another
session stays connected, ending only TimescaleDB background workers and
starting them again after the swap, and does not restart the runtime worker. Run
it with the okg that took the backup, then reinstall the older wheel. See
Upgrade an installed deployment.
okg deployment apply <d> — read-only by default. It compares the selected
deployment with live catalog ownership/content, the active Nomos baseline,
source supervision, and the worker configuration, then prints a digest-bound
ordered plan. Execute that exact plan with
okg deployment apply <d> --apply --confirm <plan-id>.
Apply preserves each subsystem's fail-closed guards, records stage receipts,
and waits for the replacement worker to acknowledge the exact source and
schedule set. A second converged apply is write-free. --dsn works for
inspection, but explicit apply refuses a DSN override: the supervisor must
restart against the manifest/local-environment target. Use deployment up for release work such as backups, migrations, dependency updates, and a
smoke publish; use deployment apply after declarative catalog, Nomos, source,
or runtime edits on an already provisioned deployment.
okg deployment ready <d> — report whether the deployment is ready for
agent graph reads. The report covers env resolution, migrations, latest
published generation, queue/DLQ, source freshness, doctor status, and MCP
read alignment. Required readiness dependency failures appear as
phase-specific blockers with status: unavailable,
reason: readiness_dependency_failed, the dependency name, the exception
text, and the shared state semantics. They must not be collapsed into
ok: true, empty data, or a generic clean/stale readiness result.
Under --checks dogfood, the publisher.lock_headroom check compares the
worst measured publish-lock hold with the capacity bar. A hold over the bar is
a warning, not a blocker: the check reports status: warning, its details
carry status: capacity_warning, the violations and
next_action: reduce_publish_lock_hold, and readiness can still be clean. An
unmeasured hold, or a disabled statement timeout, is reported as unknown.
--checks standard is general plus 14-day freshness for sources marked
required_for_baseline, a named sources.registry.unavailable blocker when the
source registry does not load, the runtime checks when runtime.enabled is
true, and the doctor. With no baseline source, freshness is not_checked
(no_baseline_sources_declared) and a sources.freshness_advisory warning
lists declared sources older than 14 days; the chat banner and the console
show no staleness then. general stays the default.
okg dogfood status and okg dogfood doctor run the profile the deployment
declares: readiness.profile, else an environment readiness.policy of
dogfood, ci or standard (sealed path only), else standard. The payload's
readiness_profile block names the profile and where it came from. An invalid
value, a value that disagrees with the environment policy, or an authored
readiness.environment_policy runs no checks: trust is unavailable with one
readiness.profile.unavailable blocker naming the field.
okg deployment bootstrap {plan|inspect-source|draft|verify|apply} ...
— plan, inspect, draft, verify, and apply a new deployment.
okg deployment bug-report --deployment <d> [...] — capture a
structured bug-report bundle for a deployment.
Implementation: src/okg/substrate/support/bug_report.py
okg environment {show|plan|ready|service|promote} operates a versioned,
long-lived staging or production environment descriptor. Pass an environment
id from ops/environments/, or an explicit environment.yaml path. Override
the descriptor root with --environments-root or OKG_ENVIRONMENTS_DIR.
Implementation: src/okg/substrate/cli/environment.py
okg environment show <environment> prints the resolved descriptor with
secrets redacted. Confirm the deployment, database, endpoints, release
paths, and service identities before an operation.
okg environment plan <environment> validates the descriptor against the
current host topology without changing services, releases, or databases.
okg environment ready <environment> --adapter {launchd|systemd} probes the
installed UI, webhook, MCP, generation, policy, database, and service state.
Use --launcher-python, --systemd-scope, and --postgres-image when those
installed values differ from the defaults.
uv run okg environment ready staging --adapter systemd --jsonAfter an upgrade that changes the rendered Postgres compose file, ready
reports services.installation as stale until you apply the plan. This
release adds shm_size: 2gb to that file when the environment declares no
database.shm_size. Run okg environment service install --apply, then
okg environment service restart --apply. Docker Compose recreates the
Postgres container once, so the database restarts briefly. The data volume is
kept. ready itself changes nothing.
okg environment service {render|install|start|stop|restart|status|logs} <environment> --adapter {launchd|systemd} owns the environment's database,
runtime worker, webhook, MCP, and UI services. render is read-only; the other
actions emit a plan unless --apply is supplied.
uv run okg environment service render staging --adapter systemd --json
uv run okg environment service restart staging --adapter systemd --apply --jsonokg environment promote <staging> <production> validates and then executes
an evidence-bound promotion of one sealed artifact. --artifact is the
artifact's sha256:<64-hex-digest> identity in the configured artifact store.
The command also requires --promotion-id, --approved-by,
--supervision-evidence, and --adapter; pass --staging-evidence for a new
approval. The default is a read-only approval plan. --apply performs the
serialized, fenced promotion or resumes recovery for the same promotion
identity.
uv run okg environment promote staging production \
--artifact sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
--promotion-id promote-2026-07-10 \
--approved-by operator@example.org \
--staging-evidence .scratch/staging-ready.json \
--supervision-evidence .scratch/staging-services.json \
--adapter systemd --jsonVerbs registered by an installed profile (each lives under
library/profiles/<name>/cli/). Today's only profile shipping CLI
verbs is academic-researcher; see
src/okg/substrate/library/profiles/academic-researcher/INSTALL.md
for the install flow.
okg add <source> --deployment <d> — low-level recovery command that
adds one local, path or URL source to an existing Academic Researcher
deployment. okg add --help lists the accepted short names. To add a
local folder or GitHub repository to any deployment, use
okg deployment add-source.
Implementation: src/okg/substrate/library/profiles/academic-researcher/cli/install.py (add)
okg remove <source> --deployment <d> — remove one source from an
Academic Researcher deployment's source registry. Records already imported
from that source stay in the graph; later publishes add no new ones.
It does not revoke stored tokens. okg remove --help lists the
accepted short names.
Implementation: src/okg/substrate/library/profiles/academic-researcher/cli/install.py (remove)
okg reset --deployment <d> — remove one installed deployment, whatever its
profile: its directory and agent-runtime registrations. When the
academic-researcher installer started the deployment's Postgres in Docker
(recorded in the docker: block of deployment.yaml), reset also removes that
Docker project and its data volume. Any other database, such as the one
--postgres-dsn named, is not touched and keeps its published graph versions;
the result says database: not_touched. If docker compose down -v fails, or
docker is not on PATH for a recorded project, reset exits non-zero with
docker_teardown: failed and keeps the deployment directory so a retry still
knows the project. Confirms first unless --yes.
Implementation: src/okg/substrate/library/profiles/academic-researcher/cli/install.py (reset)