Connector author contract¶
Kata external-root connectors are executables that implement the versioned
kata.connector.v1 JSON protocol. Provider-specific credentials and APIs stay
inside the connector.
This page is the contract for connector authors: the wire protocol, the public
Go SDK, and the conformance kit. Operators configure connector instances with
[[connector]] tables in <KATA_HOME>/config.toml (see the
configuration reference) and manage bindings with the
kata connector and kata bridge commands (see the
CLI reference).
Process and framing¶
Kata starts one fresh connector process for each RPC. It writes exactly one JSON request to standard input, closes the request stream, and expects exactly one JSON response on standard output. Extra JSON, malformed responses, mismatched request IDs, an unsupported protocol version, or output beyond the bounded response limit fail the call. Diagnostic output is not part of the protocol.
Every request contains:
{
"protocol": "kata.connector.v1",
"id": "opaque-request-id",
"method": "read_root",
"instance": "notes",
"settings": {"workspace": "example-workspace"},
"params": {"root_key": "opaque-root-key"}
}
The response repeats protocol and id, then returns exactly one of result
or error:
{
"protocol": "kata.connector.v1",
"id": "opaque-request-id",
"error": {
"code": "root_unavailable",
"message": "The external root is temporarily unavailable"
}
}
Errors must be safe, bounded operator text: a lowercase structured code and a
message without credentials, absolute paths, command lines, stack traces,
standard-output/error dumps, or control characters. The process client redacts
values supplied through the instance's explicit connector.env mapping. The
connector remains responsible for returning safe errors.
Methods and capabilities¶
All handlers implement these methods:
| Method | Purpose |
|---|---|
describe |
Return stable connector/account identity, protocol, capabilities, and optional settings schema. |
resolve_root |
Turn an operator-provided locator into a canonical root. |
read_root |
Read title, body, lifecycle state, revision, timestamps, actor, and optional fields. |
list_comments |
Return the root's canonical comment observations. |
complete_root |
Complete the external root and return verified readback. |
publish_comment |
Publish one comment and return its provider identity and timestamps. |
list_fields |
Discover canonical external field descriptors. |
read_fields |
Read selected canonical field values. |
write_fields |
Write selected fields, optionally with compare-and-set preconditions, then return canonical readback. |
publish_comment is advertised with the publish_comment capability. The
field methods are advertised with fields. Compare-and-set field writes are
advertised separately with conditional_fields. Kata rejects a conditional
write when the connector does not advertise that capability; it does not retry
the write without its preconditions. A connector may return a structured
unsupported error for optional methods whose capability it does not advertise.
Root keys, account identities, connector IDs, actor IDs, comment IDs, field IDs, and schema revisions are opaque stable identities. Do not encode Kata issue UIDs, refs, project IDs, binding IDs, branches, or other Kata-owned identity inside provider metadata. Field descriptor IDs and schema revisions must already be canonical: surrounding whitespace is invalid.
Root and comment revisions are opaque change tokens. A revision advances when
the canonical root or comment changes and remains stable while that observation
is unchanged; clients compare revisions for equality rather than ordering.
Timestamps use RFC 3339.
Field values use the portable date, local_datetime, instant, string,
string_set, and null kinds accepted by the conformance transcripts; a descriptor lists
the exact kinds it accepts and whether it is nullable and writable.
Protocol v1 write_fields requests always contain fields. Connectors that
advertise conditional_fields may also receive expected; when present, the
two objects have identical field-ID keysets. The connector writes only when
every current value equals its canonical expected value. Otherwise it changes
nothing and returns a field_conflict error. A request without expected is
an unconditional write.
Text and set values¶
string uses the existing scalar value member and preserves its exact text,
including an empty string. string_set uses values, an array of unique strings
sorted in byte order. Set elements may be empty strings. Strings and set elements
must be valid UTF-8 without NUL. Neither text kind carries a timezone; sets
cannot carry a scalar value. Date/time/null values cannot carry values.
{"kind": "string", "value": "123"}
{"kind": "string_set", "values": ["12", "45"]}
{"kind": "string_set"}
{"kind": "null"}
An omitted or empty values array represents an empty set, which is distinct
from null. Wire validation rejects duplicate or unsorted elements. Go authors
can call connector.CanonicalizeFieldValue before sending a value; it returns
a validated copy with sorted, deduplicated set elements without changing the
caller's slice. connector.ValidateFieldValue checks already canonical values.
These wire kinds extend protocol v1 without changing existing date requests.
They let adapters expose tags, assignees, or provider selections, but do not
register new native Kata mapping fields. Kata still maps only scheduled_on
and deadline_on. Each future mapping needs its own native ownership and
validation contract.
The built-in Forgejo connector exposes
text/set fields but does not advertise conditional_fields: its provider API
has no atomic compare-and-set field operation. A read followed by a write does
not meet the conditional-write contract. Kata therefore refuses automatic
mappings for it. Unconditional batches may require several provider calls and
partially apply before failure; callers must read current values before recovery.
Go SDK¶
Go connectors can implement pkg/connector.Handler and serve one request with
connector.ServeOne:
func main() {
if err := connector.ServeOne(context.Background(), os.Stdin, os.Stdout, handler); err != nil {
os.Exit(1)
}
}
connector.InvocationFromContext returns the configured instance ID and a copy
of its raw settings for the current call. Treat settings as non-secret operator
configuration; read credentials from the explicitly mapped child environment.
Conformance¶
The public pkg/connector/conformance package runs the same behavior against a
disposable provider root through the connector's real protocol boundary. A
fixture implements Exchange, Invocation, RootLocator, Reset, ExternalState,
MutateComment, and InjectFault, then
calls:
Invocation returns the configured instance ID and raw JSON settings used for
every transcript request. This lets a fixture exercise settings required by its
declared configuration schema through the real protocol boundary.
A fixture with provider-specific value restrictions may additionally implement
conformance.FieldSampleFixture, whose FieldSamples(fieldID string) returns
canonical []connector.FieldValue samples. Returning nil uses the portable
transcript defaults. An override must include a sample for every accepted kind;
nullable clears are checked separately. This lets a milestone field use real
numeric provider IDs while the generic fixture still exercises arbitrary and
empty text values. Conditional-write cases run only for connectors advertising
conditional_fields.
The committed transcripts under
pkg/connector/conformance/testdata/protocol-v1/ are language-neutral. Other
languages should drive those JSON cases against their executable and preserve
the same identity, mutation, error-safety, ordering, replay, and readback
invariants. The publication case injects a process exit after the provider
mutation but before the response, retries the same operation_id, and requires
provider state to change only once. The required-methods case edits an existing
provider comment and requires its stable ID to keep a changed revision across
subsequent unchanged reads.
Current boundaries¶
- The external root owns title and body while a binding is active.
- Kata planning-field mappings are limited to
scheduled_onanddeadline_onin protocol v1. - Kata uses one process per RPC for isolation and bounded cleanup.
- The browser UI has no bridge indicator yet.