Skip to main content

contracts API

SDK 0.4.0. This reference describes the supported authoring interface.

HostId​

HostId = NewType('HostId', str)

TransportNodeId​

TransportNodeId = NewType('TransportNodeId', str)

CapabilityNodeId​

CapabilityNodeId = NewType('CapabilityNodeId', str)

BundleId​

BundleId = NewType('BundleId', str)

CapabilityId​

CapabilityId = NewType('CapabilityId', str)

CallId​

CallId = NewType('CallId', str)

OperationId​

OperationId = NewType('OperationId', str)

ProviderResourceId​

ProviderResourceId = NewType('ProviderResourceId', str)

IdempotencyKey​

IdempotencyKey = NewType('IdempotencyKey', str)

Identifier​

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

Digest​

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

Revision​

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

MAX_FRAME_BYTES​

MAX_FRAME_BYTES = 65536

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

MAX_MANIFEST_BYTES​

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​

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​

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​

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​

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.

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

Protocol​

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.

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

Descriptor.qualified_id​

Return the exact public negotiation key.

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

Descriptor.revision​

Match the pinned public canonical descriptor revision algorithm.

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

Fields​

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
FieldDescriptionDeclared defaults and constraints
idPublic capability identifier.—
versionSemantic version.pattern="^\d+\.\d+\.\d+$"
titleOperator title.min_length=1; max_length=128
descriptionCapability behavior.min_length=1; max_length=2048
input_schemaPublic input JSON Schema.—
output_schemaPublic 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​

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]
FieldDescriptionDeclared 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_secondsSingle 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_bytesNo 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.

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

Fields​

surface_id: SurfaceId
title: str
kind: Literal['proxied', 'link']
entry: str
websocket: bool
FieldDescriptionDeclared 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.

Validate a link endpoint as soon as it is reported.

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

Fields​

surface_id: SurfaceId
ready: bool
port: int | None
url: str | None
FieldDescriptionDeclared 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​

max_queued: int
max_runtime_seconds: int
stale_after_seconds: int
retain: int
log_bytes: int
reservations: int
FieldDescriptionDeclared 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
reservationsReplay-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​

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
FieldDescriptionDeclared defaults and constraints
qualified_id——
behavior——
risk——
provider_selectionGoverned provider for lifecycle exposures; None only for read and operation exposures that select no provider.default="runpod-simulator"
summaryModel-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​

credential_id: str
title: str
description: str
required: bool
FieldDescriptionDeclared defaults and constraints
credential_idStable node-local credential reference.pattern="^[a-z0-9][a-z0-9_-]{0,127}$"
titleOwner-facing credential label.min_length=1; max_length=128
descriptionWhat this credential authorizes and where to obtain it.max_length=2048
requiredWhether 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.

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

Fields​

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
FieldDescriptionDeclared defaults and constraints
protocol——
bundle_id——
bundle_version—pattern="^\d+\.\d+\.\d+$"
skulk_requires—max_length=128
executableAbsolute 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
titleOperator-facing bundle title; the bundle id otherwise.default=null; min_length=1; max_length=64
surfacesDeclared web surfaces; readiness rides health replies.default=[]; max_length=4
operationsDurable-operation limits; required by operation descriptors.default=null
configuration_schemaOrdinary-settings schema; configured bundles start disabled.default=null
credential_inputsWrite-only inputs for the provisioned node credential store.default=[]; max_length=16
preflight_profileSigned owner preflight profile enforced at enable and restart.default=null

Message​

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

Fields​

protocol: Protocol
FieldDescriptionDeclared 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​

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
FieldDescriptionDeclared 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_sha256SHA-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.

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

Fields​

kind: Literal['health']
nonce: Identifier
ready: bool
surfaces: tuple[SurfaceReport, ...]
FieldDescriptionDeclared defaults and constraints
kind—default="health"
nonce——
ready——
surfacesComplete readiness snapshot of the declared surfaces.default=[]; max_length=4

Invoke​

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

Fields​

kind: Literal['invoke']
call_id: Identifier
capability_id: Identifier
version: str
descriptor_revision: Revision
remaining_seconds: float
payload: dict[str, JsonValue]
FieldDescriptionDeclared 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​

kind: Literal['stream_invoke']
protocol: Protocol
call_id: Identifier
capability_id: Identifier
version: str
descriptor_revision: Revision
remaining_seconds: float
payload: dict[str, JsonValue]
FieldDescriptionDeclared 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​

kind: Literal['stream_hello']
protocol: Protocol
call_id: Identifier
token: str
capability_node_id: Identifier
manifest_sha256: Digest
FieldDescriptionDeclared 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.

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

Fields​

kind: Literal['result']
call_id: Identifier
payload: dict[str, JsonValue] | None
error: Literal['invalid_payload', 'provider_error', 'timeout'] | None
FieldDescriptionDeclared defaults and constraints
kind—default="result"
call_id——
payload——
error——

Shutdown​

Request graceful shutdown before the supervisor escalates.

Fields​

kind: Literal['shutdown']
FieldDescriptionDeclared defaults and constraints
kind—default="shutdown"

ApprovedEffect​

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

Fields​

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
FieldDescriptionDeclared 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_digestComplete immutable proposal digest for owner-reviewed approvals; omitted only by the legacy direct approval path.default=null
idempotency_key——
campaign_policy_digestComplete 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.

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

manifest_digest​

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

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

serve_address​

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

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.

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

Fields​

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
FieldDescriptionDeclared defaults and constraints
socket——
stream_socketOwner-provisioned media socket, unavailable to Fabric callers.default=null
token——
node_id——
host_id——
transport_id——
manifest——
configuration—default=null
credential_configProvisioned backend configuration; private startup IPC only.default=null
serve_hostProtocol 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