UNPKG

@kontextmind/kxm

Version:

KXM local-first multi-agent orchestration and operator dashboard

204 lines (162 loc) • 9.14 kB
# Hub synchronization contract > **Status.** `kxm.sync-event.v1` is a **schema-tested contract with an > implementation** (cross-host P5): the event store `outbox` (v7, which adds > `attempt_count` and the refusal columns named below), the `sync-transform.ts` > derivation under the default policy, supervisor push to > `POST /v1/sync/events`, and hub ingestion into `sync_events` (hub store v5). > Custom project policies and on-demand content transfer remain unimplemented. > Phase 8 gate: [implementation plan](../../plans/implementation-plan.md#phase-8-multi-project-hub-kxm). Synchronization is summary-first, project-scoped, at-least-once, and allowlist-based. The full local event is never placed directly in the outbox. ## Representations ### Local event The home Runtime's immutable recovery record. It may contain bounded local data or references to protected local content. ### Sync-safe event A new object conforming to `kxm.sync-event.v1`. It is derived before the outbox and contains only schema-declared fields. It references its source event by ID and preserves the run sequence. ```text local event → choose allowed event fields → apply project policy → replace registered secret values → remove absolute paths → enforce type and length bounds → classify/redact → validate kxm.sync-event.v1 → append outbox row ``` A denylist-only sanitizer is insufficient. ## Default policy ```yaml prompts: title-only results: bounded-summary evidence: references artifacts: metadata fileChanges: paths-only rawLogs: false diffs: false environmentValues: false ``` Normal projects do not need to materialize this policy. ## Field allowlist | Category | Synchronized by default | Excluded by default | |---|---|---| | Identity | Project, run, workflow, step, assignment, attempt, Runtime IDs | Local account names not needed for authorization | | Lifecycle | Event type, sequence, state, outcome, retry/transition counts | Raw child process output | | Recovery/control | Resolved effect policy, lease/fencing identity and expiry, actor identity, resolution action, supplied evidence refs | Unstructured operator/tool bodies or secret-bearing receipt URLs | | Time | UTC event times, monotonic-derived durations | Host clock internals | | Model | Exact provider/model/profile/tags, capability result | Provider tokens and raw requests | | Usage | Input/output/cache tokens, reported/estimated cost and currency | Provider billing credentials | | Prompt | Bounded display title and prompt hash | Full prompt | | Result | Bounded summary and outcome | Full model output or reasoning | | Repository | Logical ID, commit/ref/PR receipt, relative changed paths | Absolute paths, file content, diff | | Environment | Variable name, scope, resolution status, source kind/version | Values and low-entropy value hashes | | Evidence | ID, kind, hash, status, producer metadata permitted by policy | Evidence body | | Artifact | ID, kind, hash, size, classification, availability | Artifact bytes | | Error | Allowlisted class, component, retryability | Raw provider/tool error body | | Learning | Bounded candidate summary, confidence, evidence references | Raw reasoning or automatic activation command | | Secret | Reference name/version and grant status when required | Secret value, derived value, credential-store path | Repository paths are normalized, repository-relative, and policy-filtered. Sensitive-path policy may replace even a relative path with a classification or hash. ## Schema-level exclusions `kxm.sync-event.v1` uses `additionalProperties: false` throughout its payload. It contains no generic `data`, `metadata`, `context`, `log`, `prompt`, `output`, free-form environment map, or `errorBody` escape hatch. Its `environment` field is a closed array of name/scope/status/source/version records and cannot carry values. Extensions require a new reviewed schema revision. The outbox stores only the already-derived sync object plus retry transport metadata. It does not retain the full local source payload for later redaction. A row sits in exactly one of three states: **pending** (retryable), **acked** (the hub holds it), or **refused** (the hub answered in a way that re-sending the same bytes cannot change). `refused_code` carries the hub's own code, and it is what keeps a permanently unacceptable row from being re-pushed on every tick — see Ordering and idempotency. ## Redaction Before transformation, the Runtime registers resolved secret values with an in-memory redactor. The transform also removes: - bearer/API/SSH credential shapes; - absolute Windows, UNC, POSIX, and home paths; - disallowed control characters; - excessive text and collection sizes; - fields not selected by the schema; - content that violates project classification policy. High-entropy detection is defense in depth, not the primary boundary. Secret values MUST NOT be persisted merely to support later redaction. ## Ordering and idempotency - The home Runtime assigns the run sequence. - The hub accepts an exact event once by `{projectId, runId, sequence}`. - Repeating identical bytes is idempotent. - Reusing a sequence with different content is a conflict and security alert. - The hub answers **one result per event, in request order**. The Runtime reads the answer at its row's index and verifies the echoed `runId`/`sequence` against the row it claims to describe; an answer that does not describe its row is treated as no answer at all, never as an acknowledgement. - The project a Runtime names on the wire MUST be the same identity its sync events carry — the `prj_*` id in `.kxm/project.yaml`. The hub pins a project id to the first hub project that claims it, so a second label on the same facts is refused forever after, not merely once. - A gap remains pending until filled or explicitly declared unavailable. - Hub projections never invent missing events. - A hub acknowledgement advances the outbox cursor; loss before acknowledgement causes a safe transport retry. - A **durable** refusal — `sync_sequence_reused`, `sync_project_mismatch`, `sync_home_runtime_mismatch`, `sync_event_invalid`, or a row too large to carry — moves that row out of the pending queue with the hub's code, because the next tick would raise the same alert on the same bytes. Nothing is deleted: the row, its code and its attempt count stay until an operator clears the refusal (`kxm runtime sync-retry`) after correcting the hub-side state. Only an operator decides that a refusal has become retryable. - A **transient** failure (unreachable hub, refused credential, a batch that isolation could not resolve) leaves every row pending, records the reason, and backs the next attempt off exponentially. Synchronization delay does not pause local execution except when the next action requires a shared-operation lease. It is never silent either: the supervisor keeps a per-project sync state (pending, acked and refused counts, the last failure and when the next attempt is due) at `GET /v1/sync/status`, surfaced by `kxm runtime status` and logged once per state change. ## Prompts and evidence on demand The hub normally displays the run title and bounded summary. When an authorized user requests more content: ```text hub/TUI request → home Runtime verifies project, actor, policy, and current availability → Runtime redacts and bounds the selected content → stream response → retain only if an explicit artifact policy permits it ``` The UI labels live content and retention. An offline Runtime simply makes the local body unavailable; the hub does not substitute a summary as if it were the body. ## Learning synchronization Candidates synchronize because the hub coordinates comparison and promotion. They remain evidence with explicit source references, confidence, and status. No candidate payload may contain tools, secret grants, approval state, or an automatic activation instruction. Offline runs may synchronize candidates after reconnecting. They cannot claim a new memory revision or promote one while offline. ## Environment and secrets A safe environment record may say: ```yaml name: DATABASE_URL scope: repository:api status: resolved source: secret-ref version: credential-version-7 ``` It cannot include a value, a reversible encoding, a value hash for a low-entropy secret, or a local credential-store path. ## Logs Structured lifecycle summaries synchronize. Raw harness, model, tool, build, and process logs stay in Runtime-local protected storage. An explicit reviewed policy may upload a redacted/encrypted log artifact, which remains an artifact transfer rather than an unrestricted event field. ## Policy changes `kxm.project.v1` expresses only the safe default event policy. Expanding prompt, evidence, diff, artifact, or log transfer requires a future versioned content- transfer policy and schema; it cannot be enabled through an unknown field. Such expansion is permission-increasing, requires trust review, and affects future runs only. The active run records the policy revision used for every outbox transform.