# Pack protocol

> Pack protocol v2 — the messages a runtime exchanges with a pack service to run pack code (tool handlers and guardrail checks).

`@kindgi/specs/pack-protocol.schema.json`, schema version 2.5.0.

Pack protocol v2 — the messages a runtime exchanges with a pack service to run pack code (tool handlers and guardrail checks). One request, one response. Transport (HTTP): `POST /v1/invoke` takes a request and answers 200 with a response; `GET /v1/info` (token) answers `info`; `GET /healthz` (no token) answers 200 while the process is up; `GET /readyz` (no token) answers 200 once every module the index names has loaded and the service isn't draining. Headers: `kindgi-pack-token` (required on `/v1/invoke` and `/v1/info`; `Authorization` is deliberately not used, since platforms like Cloud Run put their own identity token there), `kindgi-timeout-ms` (relative deadline; default 120000), `kindgi-run-id`, `kindgi-request-id`, `kindgi-protocol`, `traceparent` (W3C Trace Context: the run's trace, so a pack service's records can carry its trace id; optional, protocol 2.4.1), `kindgi-tenant-id` (the tenant the call is for, set by the runtime from the deployment it routed the call to, on `/v1/invoke` and `/v1/info`; optional, protocol 2.7.0); the answer to an invoke carries `kindgi-duration-ms` and `kindgi-artifact-version`. Every outcome of running pack code is a response message with status 200 — a validation failure, a throw, an unknown tool, `deadline-exceeded`, `cancelled` (the caller closed the connection). Non-2xx statuses are transport-level only: 401 (bad token), 404, 405, 413 (body over 10 MiB), 415 (not `application/json`), 421 with `{"code":"pack-tenant-mismatch"}` (a service pinned to one tenant by `KINDGI_PACK_TENANT_ID` refused a call to `/v1/invoke` or `/v1/info` whose `kindgi-tenant-id` header is missing or names another tenant, or a tool call whose context names another: the call didn't run, and isn't retried; protocol 2.7.0), 503 with `Retry-After` (not ready, draining, or at the concurrency cap — the call provably did not run), 500 (the service itself failed). Process contract: a pack service reads `--index`, `--module-root` and `--host` arguments, `KINDGI_PACK_SERVICE_TOKEN`, `PORT` (`0` picks a free port), `KINDGI_PACK_SERVICE_MAX_CONCURRENCY` and, optionally, `KINDGI_PACK_TENANT_ID` (protocol 2.7.0); it removes `KINDGI_PACK_SERVICE_TOKEN` from its process environment before it loads pack code, which never sees it; it loads every module before listening and exits 1 with a `{"kind":"boot-failed","problems":[…]}` JSON line on stderr when one fails; once listening it writes `{"kind":"listening","port":N,…}` on stderr; SIGTERM drains in-flight calls (up to 8 s; `/readyz` and new invokes answer 503 meanwhile) and exits 0.

Type: [request](https://docs.kindgi.com/v0.1.8/reference/schemas/pack-protocol.md#request) or [response](https://docs.kindgi.com/v0.1.8/reference/schemas/pack-protocol.md#response) or [info](https://docs.kindgi.com/v0.1.8/reference/schemas/pack-protocol.md#info)

## Definitions

### version

Protocol version of every message.

Type: `2`

### request

Type: [toolInvoke](https://docs.kindgi.com/v0.1.8/reference/schemas/pack-protocol.md#toolinvoke) or [checkInvoke](https://docs.kindgi.com/v0.1.8/reference/schemas/pack-protocol.md#checkinvoke)

### response

Type: [toolResult](https://docs.kindgi.com/v0.1.8/reference/schemas/pack-protocol.md#toolresult) or [checkResult](https://docs.kindgi.com/v0.1.8/reference/schemas/pack-protocol.md#checkresult) or [error](https://docs.kindgi.com/v0.1.8/reference/schemas/pack-protocol.md#error)

### callContext

Per-call context a handler receives. Serializable only: a pack service adds its own cancellation (an abort signal, a cancellation token) on top.

- `tenantId` (string, required): Tenant the call runs for.
- `runId` (string, required): The kernel run the call belongs to.
- `projectId` (string): The project of the run the call belongs to. Set by the runtime from the run, never from its input or a model's arguments. Absent from an older runtime.
- `orgId` (string): The org of that project, when the project has one. Set by the runtime from the project, never from input. Absent when the project has no org, and from an older runtime.
- `requestId` (string): The individual call — e.g. the model's tool-call id.
- `idempotencyKey` (string (uuid)): The call's idempotency key (protocol 2.6.0): the same every time this call runs (a resume after a wait, a retry after a failure, a re-run after a crash), different for every other call. A tool that changes something passes it to the system it writes to, or looks for it there first. A version 5 UUID (RFC 9562, SHA-1) under the namespace a70d5bff-e713-4322-b2ea-9d5c4926a861, over the UTF-8 bytes of the JSON array \[runId, stepScope, toolId] (with the model's call id appended for a call a model asked for) as JSON.stringify writes it: no whitespace, and characters beyond ASCII as themselves, never as \u escapes (Python: json.dumps(parts, separators=(',', ':'), ensure_ascii=False)); stepScope is the step's own (its node id, a loop body's step with its iteration, a fanout branch). Absent from a runtime that can't name its steps.
- `env` (map of string): The env values the tool declares (`needsSpec.env`), by name, resolved for this call: the project's value, else its org's, else the tenant's, in the env the runtime serves; a schema `default` when no scope sets one (protocol 2.5.0). The runtime records them with the call, so the call re-run after a wait or a retry sends the same values. Absent when the tool declares none, and from an older runtime.
- `secrets` (object): Resolved secrets for the call (open).
- `config` (object): Reserved: no runtime sends it yet (open).
- `settings` (map of object): The settings blocks the calling agent version pins: each block's values, by block id (protocol 2.4.0). Absent when it pins none, and from an older runtime.

### toolInvoke

Run a tool handler. The pack service validates `input` against the indexed input schema, runs the handler, validates its return value against the indexed output schema.

- `v` ([version](https://docs.kindgi.com/v0.1.8/reference/schemas/pack-protocol.md#version), required)

- `kind` (`"invoke"`, required)

- `tool` (object, required)

  - `id` (string, required): Tool id, resolved from the pack service's own index.
  - `version` (string): When set, the pack service refuses a different indexed version (`tool-version-mismatch`).

- `input` (object, required): The tool's input (any JSON).

- `ctx` ([callContext](https://docs.kindgi.com/v0.1.8/reference/schemas/pack-protocol.md#callcontext), required)

### checkInvoke

Run a guardrail check: `evaluate(config, trace)`.

- `v` ([version](https://docs.kindgi.com/v0.1.8/reference/schemas/pack-protocol.md#version), required)
- `kind` (`"check-invoke"`, required)
- `check` (object, required)
  - `id` (string, required): Check id (the indexed guardrail's `checkId`, else its `id`).
- `config` (object, required): The guardrail's config (open).
- `trace` (object, required): The run trace the check evaluates (`RunTrace` in `@kindgi/guardrails`).

### toolResult

- `v` ([version](https://docs.kindgi.com/v0.1.8/reference/schemas/pack-protocol.md#version), required)
- `kind` (`"result"`, required)
- `output` (object, required): The handler's return value, valid against the tool's output schema.

### checkResult

- `v` ([version](https://docs.kindgi.com/v0.1.8/reference/schemas/pack-protocol.md#version), required)

- `kind` (`"check-result"`, required)

- `result` (object, required): `CheckResult` — open beyond the listed fields.

  - `passed` (boolean, required)
  - `reason` (string)
  - `judgeResponse` (string)
  - `attributes` (object)

### errorCode

Why running pack code didn't produce a result.

Type: `"input-validation-failed"` | `"output-validation-failed"` | `"handler-import-failed"` | `"handler-shape-invalid"` | `"handler-throw"` | `"check-shape-invalid"` | `"malformed-message"` | `"unknown-protocol-version"` | `"unexpected-message-kind"` | `"tool-not-in-pack"` | `"check-not-in-pack"` | `"tool-version-mismatch"` | `"deadline-exceeded"` | `"cancelled"`

### issue

One validation issue, Ajv-shaped. `keyword` names the failing JSON Schema keyword; it is absent when the failure came from a model's own validation (a Zod refinement, a pydantic validator) rather than a schema keyword. Open: an implementation may add fields.

- `instancePath` (string, required): JSON Pointer to the failing value (`""` for the root, `/items/0/name`).
- `schemaPath` (string): URI fragment of the failing schema keyword (`#/properties/name/type`).
- `keyword` (string): The failing keyword (`type`, `required`, `additionalProperties`, …).
- `params` (object): Keyword parameters, as Ajv reports them: `required` → `{ missingProperty }`, `additionalProperties` → `{ additionalProperty }`, `type` → `{ type }`, `enum` → `{ allowedValues }`, limits → `{ limit }`, `pattern` → `{ pattern }`, `format` → `{ format }`.
- `message` (string, required): Human-readable message (`must have required property 'name'`).

### error

- `v` ([version](https://docs.kindgi.com/v0.1.8/reference/schemas/pack-protocol.md#version), required)
- `kind` (`"error"`, required)
- `code` ([errorCode](https://docs.kindgi.com/v0.1.8/reference/schemas/pack-protocol.md#errorcode), required)
- `message` (string, required)
- `toolId` (string): Present when the error is about a tool.
- `checkId` (string): Present when the error is about a check.
- `cause` (object): The serialized throw for `handler-throw` / `handler-import-failed` (`{ name, message, stack }` for an error object).
- `issues` (array of [issue](https://docs.kindgi.com/v0.1.8/reference/schemas/pack-protocol.md#issue)): Present for `input-validation-failed` / `output-validation-failed`.

### info

The answer to `GET /v1/info`.

- `protocol` ([version](https://docs.kindgi.com/v0.1.8/reference/schemas/pack-protocol.md#version), required)

- `packId` (string, required)

- `packVersion` (string, required)

- `artifactVersion` (string, required)

- `indexHash` (string): `sha256:` and the hex SHA-256 of the service's index file, byte for byte: the `indexHash` a deployment signs. Lets the runtime check that an endpoint serves the deployment it was set for (protocol 2.7.0). Absent from an older pack service.

- `tools` (array of object, required)

  - `id` (string, required)
  - `version` (string)

- `checks` (array of string, required)

- `missingEnv` (array of string): The index's required env names this process lacks (unset or empty), sorted. Empty when none is missing. Under the `strict` env check the service isn't ready while any is listed.
