> Canonical guide: https://developers.foxlight.ai/build/sdk/reference/contracts/
> Contract snapshots: Skulk 2.0.0 (b0af39c79b6b7102b2478062904f1d7cc8619975); SDK 0.4.0 (31bb090b8f64689f87514e48385e22b6ad94a6c2). Check the installed runtime when versions differ.

# contracts API

SDK 0.4.0. This reference describes the supported authoring interface.

## `HostId`

```python
HostId = NewType('HostId', str)
```

## `TransportNodeId`

```python
TransportNodeId = NewType('TransportNodeId', str)
```

## `CapabilityNodeId`

```python
CapabilityNodeId = NewType('CapabilityNodeId', str)
```

## `BundleId`

```python
BundleId = NewType('BundleId', str)
```

## `CapabilityId`

```python
CapabilityId = NewType('CapabilityId', str)
```

## `CallId`

```python
CallId = NewType('CallId', str)
```

## `OperationId`

```python
OperationId = NewType('OperationId', str)
```

## `ProviderResourceId`

```python
ProviderResourceId = NewType('ProviderResourceId', str)
```

## `IdempotencyKey`

```python
IdempotencyKey = NewType('IdempotencyKey', str)
```

## `Identifier`

```python
Identifier = Annotated[str, Field(min_length=1, max_length=128, pattern='^[a-zA-Z0-9._:@-]+$')]
```

## `Digest`

```python
Digest = Annotated[str, Field(pattern='^[a-f0-9]{64}$')]
```

## `Revision`

```python
Revision = Annotated[str, Field(pattern='^[a-f0-9]{16}$')]
```

## `MAX_FRAME_BYTES`

```python
MAX_FRAME_BYTES = 65536
```

Largest single message on the local wire, in either direction.

## `MAX_MANIFEST_BYTES`

```python
MAX_MANIFEST_BYTES = 131072
```

Largest canonical manifest: the one ceiling on how much a capability declares.

A signed runtime release carries its manifest within the same bound, and the
publisher refuses a larger one before anything is signed, so an author meets
the limit at build time rather than as a capability that will not start.

## `MAX_STARTUP_BYTES`

```python
MAX_STARTUP_BYTES = MAX_MANIFEST_BYTES + MAX_FRAME_BYTES
```

Largest startup record: the manifest plus one frame of room for the
installation's identity, its settings and its credential path.

## `PROTOCOL`

```python
PROTOCOL = 4
```

The manifest and wire protocol this SDK speaks.

Protocol 4 adds isolated, bounded duplex media channels for managed streams.
Protocol 3 adds the host's serve address to the startup record
(`Startup.serve_host`), and its owners carry an installation's settings
across a schema change that still accepts them. Protocol 2 bounds the child's
startup record by the manifest's own limit and answers the hello with the
manifest's digest. Protocol 1, no longer accepted, read the record with one
wire frame and echoed every descriptor in the hello.

## `ACCEPTED_PROTOCOLS`

```python
ACCEPTED_PROTOCOLS: tuple[int, ...] = (3, 4)
```

Protocols a host or owner built from this SDK accepts.

The current protocol and, once there is one, the previous: a capability built
against the previous SDK keeps installing for one release cycle, and a
refusal names the exact mismatch rather than failing somewhere in a range.
The window never has more than two members, so both are always tested.

## `QUALIFIED_SKULK_VERSION`

```python
QUALIFIED_SKULK_VERSION = '2.0.0'
```

The Skulk release this SDK is qualified against, the version in
`skulk-baseline.json`. A manifest admits it by default, and the owner checks
legacy manifests against it because they carry no core-attested version.

## `accepted_protocol`

Refuse a protocol outside the window, naming what this SDK accepts.

```python
def accepted_protocol(value: int) -> int:
    ...
```

## `Protocol`

```python
Protocol = Annotated[int, AfterValidator(accepted_protocol), Field(json_schema_extra={'enum': list(ACCEPTED_PROTOCOLS)})]
```

A protocol number inside the accepted window.

## `Contract`

Strict immutable envelope; serialize at ownership boundaries.

## `Descriptor`

Public capability descriptor with four independently typed I/O modes.

### `Descriptor.chunk_schemas_match_mode`

Require precisely the chunk schemas consumed by the selected I/O mode.

```python
def chunk_schemas_match_mode(self) -> Self:
    ...
```

### `Descriptor.qualified_id`

Return the exact public negotiation key.

```python
def qualified_id(self) -> str:
    ...
```

### `Descriptor.revision`

Match the pinned public canonical descriptor revision algorithm.

```python
def revision(self) -> str:
    ...
```

### Fields

```python
id: Identifier
version: str
title: str
description: str
input_schema: dict[str, JsonValue]
output_schema: dict[str, JsonValue] | None
io_mode: Literal['unary', 'server_streaming', 'client_streaming', 'bidirectional']
input_chunk_schema: dict[str, JsonValue] | None
output_chunk_schema: dict[str, JsonValue] | None
annotations: dict[str, str] | None
```

| Field                 | Description                      | Declared defaults and constraints |
| --------------------- | -------------------------------- | --------------------------------- |
| `id`                  | Public capability identifier.    | —                                 |
| `version`             | Semantic version.                | pattern="^\\d+\\.\\d+\\.\\d+$"    |
| `title`               | Operator title.                  | min_length=1; max_length=128      |
| `description`         | Capability behavior.             | min_length=1; max_length=2048     |
| `input_schema`        | Public input JSON Schema.        | —                                 |
| `output_schema`       | Public final-result JSON Schema. | default=null                      |
| `io_mode`             | —                                | default="unary"                   |
| `input_chunk_schema`  | —                                | default=null                      |
| `output_chunk_schema` | —                                | default=null                      |
| `annotations`         | —                                | default=null                      |

## `Bounds`

Hard local admission, liveness, restart and output limits.

### Fields

```python
max_children: int
concurrency: Literal[1]
startup_seconds: float
health_seconds: float
call_seconds: float
stream_seconds: float
shutdown_seconds: float
restart_budget: int
backoff_seconds: float
log_bytes: Literal[0]
```

| Field              | Description                                                      | Declared defaults and constraints |
| ------------------ | ---------------------------------------------------------------- | --------------------------------- |
| `max_children`     | —                                                                | default=4; ge=1; le=16            |
| `concurrency`      | —                                                                | default=1                         |
| `startup_seconds`  | —                                                                | default=5.0; gt=0; le=30          |
| `health_seconds`   | —                                                                | default=1.0; gt=0; le=10          |
| `call_seconds`     | —                                                                | default=5.0; gt=0; le=30          |
| `stream_seconds`   | Single deadline ceiling for a streaming call, including cleanup. | default=300.0; gt=0; le=300       |
| `shutdown_seconds` | —                                                                | default=1.0; gt=0; le=5           |
| `restart_budget`   | —                                                                | default=3; ge=0; le=10            |
| `backoff_seconds`  | —                                                                | default=0.1; gt=0; le=10          |
| `log_bytes`        | No child log payload retention.                                  | default=0                         |

## `Surface`

One declared operator-facing web surface of a capability node.

Identity, kind and permissions are immutable manifest facts; readiness and
the volatile endpoint arrive in correlated health replies. `proxied`
surfaces are declared so the manifest is complete, but their endpoints are
withheld from every projection until the proxy contract is qualified.

### `Surface.kind_specific_fields`

Require proxied-only fields to stay at their defaults for links.

```python
def kind_specific_fields(self) -> Self:
    ...
```

### Fields

```python
surface_id: SurfaceId
title: str
kind: Literal['proxied', 'link']
entry: str
websocket: bool
```

| Field        | Description | Declared defaults and constraints |
| ------------ | ----------- | --------------------------------- |
| `surface_id` | —           | —                                 |
| `title`      | —           | min_length=1; max_length=64       |
| `kind`       | —           | —                                 |
| `entry`      | —           | default="/"; max_length=256       |
| `websocket`  | —           | default=false                     |

## `SurfaceReport`

Volatile readiness of one declared surface, reported with health.

A complete snapshot is expected: a declared surface absent from the reply
is not ready and any earlier endpoint is withdrawn.

### `SurfaceReport.link_urls_are_public`

Validate a link endpoint as soon as it is reported.

```python
def link_urls_are_public(self) -> Self:
    ...
```

### Fields

```python
surface_id: SurfaceId
ready: bool
port: int | None
url: str | None
```

| Field        | Description | Declared defaults and constraints |
| ------------ | ----------- | --------------------------------- |
| `surface_id` | —           | —                                 |
| `ready`      | —           | —                                 |
| `port`       | —           | default=null; ge=1024; le=65535   |
| `url`        | —           | default=null                      |

## `OperationBounds`

Limits for durable operations a node runs outside the invocation slot.

### Fields

```python
max_queued: int
max_runtime_seconds: int
stale_after_seconds: int
retain: int
log_bytes: int
reservations: int
```

| Field                 | Description                                                                                                             | Declared defaults and constraints |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| `max_queued`          | —                                                                                                                       | default=4; ge=1; le=64            |
| `max_runtime_seconds` | —                                                                                                                       | default=3600; ge=1; le=86400      |
| `stale_after_seconds` | —                                                                                                                       | default=30; ge=1; le=3600         |
| `retain`              | —                                                                                                                       | default=64; ge=1; le=1024         |
| `log_bytes`           | —                                                                                                                       | default=262144; ge=0              |
| `reservations`        | Replay-fence capacity. Reaching it refuses new starts rather than forgetting old ids, which would make them replayable. | default=65536; ge=64; le=1000000  |

## `StewardExposure`

Trusted private manifest classification; never inferred from tool text.

`read` exposures become direct steward tools; `lifecycle` and
`operation` exposures become inert proposals that need an approval. A
declaration is not evidence of enforcement: the owner still validates the
manifest and the child still checks authority at dispatch.

### Fields

```python
qualified_id: Identifier
behavior: Literal['read', 'lifecycle', 'operation']
risk: Literal['observation', 'billable', 'effect']
provider_selection: Literal['runpod-simulator', 'runpod-witness-experimental'] | None
summary: str | None
```

| Field                | Description                                                                                                    | Declared defaults and constraints |
| -------------------- | -------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| `qualified_id`       | —                                                                                                              | —                                 |
| `behavior`           | —                                                                                                              | —                                 |
| `risk`               | —                                                                                                              | —                                 |
| `provider_selection` | Governed provider for lifecycle exposures; None only for read and operation exposures that select no provider. | default="runpod-simulator"        |
| `summary`            | Model-facing tool description; omitted means the descriptor's.                                                 | default=null; max_length=256      |

## `CredentialInput`

One plugin-declared write-only credential; values are never manifest data.

### Fields

```python
credential_id: str
title: str
description: str
required: bool
```

| Field           | Description                                             | Declared defaults and constraints          |
| --------------- | ------------------------------------------------------- | ------------------------------------------ |
| `credential_id` | Stable node-local credential reference.                 | pattern="^[a-z0-9][a-z0-9_-]&#123;0,127}$" |
| `title`         | Owner-facing credential label.                          | min_length=1; max_length=128               |
| `description`   | What this credential authorizes and where to obtain it. | max_length=2048                            |
| `required`      | Whether readiness requires a usable value.              | default=true                               |

## `Manifest`

Owner-installed immutable bundle contract; no secrets or PATH discovery.

### `Manifest.unique_contracts`

Reject duplicate contracts and malformed compatibility requirements.

```python
def unique_contracts(self) -> Self:
    ...
```

### Fields

```python
protocol: Protocol
bundle_id: Identifier
bundle_version: str
skulk_requires: str
executable: str
executable_sha256: Digest
arguments: tuple[str, ...]
descriptors: tuple[Descriptor, ...]
bounds: Bounds
effect: Literal['read', 'governed']
steward: tuple[StewardExposure, ...]
title: str | None
surfaces: tuple[Surface, ...]
operations: OperationBounds | None
configuration_schema: dict[str, JsonValue] | None
credential_inputs: tuple[CredentialInput, ...]
preflight_profile: Literal['local', 'runpod-native'] | None
```

| Field                  | Description                                                    | Declared defaults and constraints         |
| ---------------------- | -------------------------------------------------------------- | ----------------------------------------- |
| `protocol`             | —                                                              | —                                         |
| `bundle_id`            | —                                                              | —                                         |
| `bundle_version`       | —                                                              | pattern="^\\d+\\.\\d+\\.\\d+$"            |
| `skulk_requires`       | —                                                              | max_length=128                            |
| `executable`           | Absolute approved executable.                                  | min_length=1; max_length=4096             |
| `executable_sha256`    | —                                                              | —                                         |
| `arguments`            | —                                                              | max_length=16                             |
| `descriptors`          | —                                                              | min_length=1; max_length=8                |
| `bounds`               | —                                                              | —                                         |
| `effect`               | —                                                              | default="read"                            |
| `steward`              | —                                                              | default=[]; max_length=8                  |
| `title`                | Operator-facing bundle title; the bundle id otherwise.         | default=null; min_length=1; max_length=64 |
| `surfaces`             | Declared web surfaces; readiness rides health replies.         | default=[]; max_length=4                  |
| `operations`           | Durable-operation limits; required by operation descriptors.   | default=null                              |
| `configuration_schema` | Ordinary-settings schema; configured bundles start disabled.   | default=null                              |
| `credential_inputs`    | Write-only inputs for the provisioned node credential store.   | default=[]; max_length=16                 |
| `preflight_profile`    | Signed owner preflight profile enforced at enable and restart. | default=null                              |

## `Message`

Every IPC message pins a protocol revision inside the accepted window.

### Fields

```python
protocol: Protocol
```

| Field      | Description | Declared defaults and constraints |
| ---------- | ----------- | --------------------------------- |
| `protocol` | —           | —                                 |

## `Hello`

Authenticated startup identity and exact manifest agreement.

The child answers with the digest of the manifest it read from its startup
record rather than echoing the manifest back: the check is the same, and
the message stays small however much a capability declares.

### Fields

```python
kind: Literal['hello']
host_id: Identifier
transport_node_id: Identifier
capability_node_id: Identifier
bundle_id: Identifier
bundle_version: str
token: str
manifest_sha256: Digest
```

| Field                | Description                                                                                                                     | Declared defaults and constraints |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| `kind`               | —                                                                                                                               | default="hello"                   |
| `host_id`            | —                                                                                                                               | —                                 |
| `transport_node_id`  | —                                                                                                                               | —                                 |
| `capability_node_id` | —                                                                                                                               | —                                 |
| `bundle_id`          | —                                                                                                                               | —                                 |
| `bundle_version`     | —                                                                                                                               | —                                 |
| `token`              | —                                                                                                                               | min_length=64; max_length=64      |
| `manifest_sha256`    | SHA-256 of the canonical manifest the child read from its startup record (manifest_digest); the owner compares it with its own. | —                                 |

## `Health`

Correlated challenge/response; readiness is not inferred from a PID.

### `Health.unique_surface_reports`

Refuse a reply that reports one surface twice.

```python
def unique_surface_reports(self) -> Self:
    ...
```

### Fields

```python
kind: Literal['health']
nonce: Identifier
ready: bool
surfaces: tuple[SurfaceReport, ...]
```

| Field      | Description                                           | Declared defaults and constraints |
| ---------- | ----------------------------------------------------- | --------------------------------- |
| `kind`     | —                                                     | default="health"                  |
| `nonce`    | —                                                     | —                                 |
| `ready`    | —                                                     | —                                 |
| `surfaces` | Complete readiness snapshot of the declared surfaces. | default=[]; max_length=4          |

## `Invoke`

One bounded unary invocation; execution time is a remaining local budget.

### Fields

```python
kind: Literal['invoke']
call_id: Identifier
capability_id: Identifier
version: str
descriptor_revision: Revision
remaining_seconds: float
payload: dict[str, JsonValue]
```

| Field                 | Description | Declared defaults and constraints |
| --------------------- | ----------- | --------------------------------- |
| `kind`                | —           | default="invoke"                  |
| `call_id`             | —           | —                                 |
| `capability_id`       | —           | —                                 |
| `version`             | —           | —                                 |
| `descriptor_revision` | —           | —                                 |
| `remaining_seconds`   | —           | gt=0; le=30                       |
| `payload`             | —           | —                                 |

## `StreamInvoke`

Admit one duplex invocation on its separately authenticated media channel.

### Fields

```python
kind: Literal['stream_invoke']
protocol: Protocol
call_id: Identifier
capability_id: Identifier
version: str
descriptor_revision: Revision
remaining_seconds: float
payload: dict[str, JsonValue]
```

| Field                 | Description | Declared defaults and constraints |
| --------------------- | ----------- | --------------------------------- |
| `kind`                | —           | default="stream_invoke"           |
| `protocol`            | —           | default=4; ge=4; le=4             |
| `call_id`             | —           | —                                 |
| `capability_id`       | —           | —                                 |
| `version`             | —           | —                                 |
| `descriptor_revision` | —           | —                                 |
| `remaining_seconds`   | —           | gt=0; le=300                      |
| `payload`             | —           | —                                 |

## `StreamHello`

Bind a fresh media connection to the installed child and admitted call.

### Fields

```python
kind: Literal['stream_hello']
protocol: Protocol
call_id: Identifier
token: str
capability_node_id: Identifier
manifest_sha256: Digest
```

| Field                | Description | Declared defaults and constraints |
| -------------------- | ----------- | --------------------------------- |
| `kind`               | —           | default="stream_hello"            |
| `protocol`           | —           | default=4; ge=4; le=4             |
| `call_id`            | —           | —                                 |
| `token`              | —           | min_length=64; max_length=64      |
| `capability_node_id` | —           | —                                 |
| `manifest_sha256`    | —           | —                                 |

## `Result`

Correlated success or bounded typed failure, with one terminal only.

### `Result.one_outcome`

Require exactly one terminal outcome.

```python
def one_outcome(self) -> Self:
    ...
```

### Fields

```python
kind: Literal['result']
call_id: Identifier
payload: dict[str, JsonValue] | None
error: Literal['invalid_payload', 'provider_error', 'timeout'] | None
```

| Field     | Description | Declared defaults and constraints |
| --------- | ----------- | --------------------------------- |
| `kind`    | —           | default="result"                  |
| `call_id` | —           | —                                 |
| `payload` | —           | —                                 |
| `error`   | —           | —                                 |

## `Shutdown`

Request graceful shutdown before the supervisor escalates.

### Fields

```python
kind: Literal['shutdown']
```

| Field  | Description | Declared defaults and constraints |
| ------ | ----------- | --------------------------------- |
| `kind` | —           | default="shutdown"                |

## `ApprovedEffect`

Exact signed effect bounds, optionally tied to a complete reviewed proposal.

### Fields

```python
authority_id: Identifier
key_id: Identifier
operator_id: Identifier
host_id: Identifier
transport_node_id: Identifier
capability_node_id: Identifier
capability_id: Identifier
capability_version: str
descriptor_revision: Revision
plan_digest: Digest
proposal_digest: Digest | None
idempotency_key: Identifier
campaign_policy_digest: Digest | None
effect: Literal['acquire', 'release']
provider_selection: Identifier
expires_at_unix_seconds: int
maximum_lifetime_seconds: int
maximum_total_cost_microunits: int
currency: Literal['USD']
policy_revision: Identifier
```

| Field                           | Description                                                                                                       | Declared defaults and constraints |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| `authority_id`                  | —                                                                                                                 | —                                 |
| `key_id`                        | —                                                                                                                 | —                                 |
| `operator_id`                   | —                                                                                                                 | —                                 |
| `host_id`                       | —                                                                                                                 | —                                 |
| `transport_node_id`             | —                                                                                                                 | —                                 |
| `capability_node_id`            | —                                                                                                                 | —                                 |
| `capability_id`                 | —                                                                                                                 | —                                 |
| `capability_version`            | —                                                                                                                 | pattern="^\\d+\\.\\d+\\.\\d+$"    |
| `descriptor_revision`           | —                                                                                                                 | —                                 |
| `plan_digest`                   | —                                                                                                                 | —                                 |
| `proposal_digest`               | Complete immutable proposal digest for owner-reviewed approvals; omitted only by the legacy direct approval path. | default=null                      |
| `idempotency_key`               | —                                                                                                                 | —                                 |
| `campaign_policy_digest`        | Complete reviewed campaign policy; absent for legacy proofs.                                                      | default=null                      |
| `effect`                        | —                                                                                                                 | —                                 |
| `provider_selection`            | —                                                                                                                 | —                                 |
| `expires_at_unix_seconds`       | —                                                                                                                 | gt=0                              |
| `maximum_lifetime_seconds`      | —                                                                                                                 | gt=0; le=86400                    |
| `maximum_total_cost_microunits` | —                                                                                                                 | ge=0                              |
| `currency`                      | —                                                                                                                 | default="USD"                     |
| `policy_revision`               | —                                                                                                                 | —                                 |

## `Denied`

A sanitized authorization, ownership or readiness refusal.

Part of the authoring surface: a capability raises it to refuse, and the
host reports it without the reason leaving the node.

## `Uncertain`

Observation is required; a provider mutation must not be assumed absent.

## `canonical`

Encode finite canonical JSON for wire and revision agreement.

```python
def canonical(value: BaseModel) -> bytes:
    ...
```

## `manifest_digest`

SHA-256 of the canonical manifest, what owner and child agree on at hello.

```python
def manifest_digest(manifest: 'Manifest') -> str:
    ...
```

## `serve_address`

Refuse a serve address that is not one routable unicast IP address.

```python
def serve_address(value: str) -> str:
    ...
```

## `Startup`

What a child reads from its inherited startup descriptor.

Private inherited descriptor contents, never command-line credentials. Part
of the authoring surface: a capability's child parses exactly this, with
`storage.read_startup`. The owner writes it canonically and refuses to
start a child whose record would exceed `MAX_STARTUP_BYTES`.

### `Startup.serve_host_matches_protocol`

Refuse a serve address in a record a protocol 2 child would reject.

```python
def serve_host_matches_protocol(self) -> Self:
    ...
```

### Fields

```python
socket: str
stream_socket: str | None
token: str
node_id: str
host_id: str
transport_id: str
manifest: Manifest
configuration: dict[str, JsonValue] | None
credential_config: str | None
serve_host: ServeAddress | None
```

| Field               | Description                                                                                                                                                                       | Declared defaults and constraints |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| `socket`            | —                                                                                                                                                                                 | —                                 |
| `stream_socket`     | Owner-provisioned media socket, unavailable to Fabric callers.                                                                                                                    | default=null                      |
| `token`             | —                                                                                                                                                                                 | —                                 |
| `node_id`           | —                                                                                                                                                                                 | —                                 |
| `host_id`           | —                                                                                                                                                                                 | —                                 |
| `transport_id`      | —                                                                                                                                                                                 | —                                 |
| `manifest`          | —                                                                                                                                                                                 | —                                 |
| `configuration`     | —                                                                                                                                                                                 | default=null                      |
| `credential_config` | Provisioned backend configuration; private startup IPC only.                                                                                                                      | default=null                      |
| `serve_host`        | Protocol 3: the address the host lets this child serve on besides loopback, the node's Tailscale address. Absent when the node has none, and always at protocol 2. See `serving`. | default=null                      |
