The connector kit for GameCult minds: the contract every model turn goes through, and connector daemons.
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Hands 51982b7f63 lease readouts: a pool of their own, a refusal over it, the arm's promises pinned, the request type named for the lease
Readouts no longer take a lease's connection slot: the accept loop admits
max_connections + READOUT_CONNECTIONS, a lease holds one of max_connections
after it is read, a readout one of READOUT_CONNECTIONS, and a readout over its
limit is refused Busy rather than dropped. Socket tests hold the service lock
and a metered cell across readouts (no asking, no week wait, no lock), flood
the readout pool beside leases, and pin the refusal. The new public request
type is LeaseUsageReadoutRequest so it no longer shares a name with the kit's
EnvelopeKind::UsageReadoutRequest; wire bytes and fixtures are unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-06 23:31:24 +01:00
crates lease readouts: a pool of their own, a refusal over it, the arm's promises pinned, the request type named for the lease 2026-11-06 23:31:24 +01:00
schemas connector-usage-only: regenerate v4 goldens and schemas, format, fix tests 2026-10-31 04:51:05 +01:00
.gitattributes connector-usage-only: delete the dollar authorities (WIP, does not yet build) 2026-10-31 04:43:18 +01:00
.gitignore connector-kit-lift: the provider-neutral connector kit, lifted from CodexConnector fd7cec4 (squash of eureka-body/connector-kit-lift 1cf879f..ba1f7eb) 2026-10-28 07:12:42 +01:00
Cargo.lock quota meter: the weekly reading comes from the CLI's own rate-limit event; while a lease is live and the reading is stale the connector makes the smallest real call. The usage poll and its HTTP client go: the setup token can never reach that endpoint (squash of eureka-body/quota-meter-stream 7d4c1bf..c298d9d; Soul s4-s5) 2026-11-03 13:12:31 +01:00
Cargo.toml connector-kit-lift: the provider-neutral connector kit, lifted from CodexConnector fd7cec4 (squash of eureka-body/connector-kit-lift 1cf879f..ba1f7eb) 2026-10-28 07:12:42 +01:00
LICENSE Initial commit: LICENSE and README front door 2026-10-28 03:49:49 +01:00
README.md lease socket: a usage readout from the one QuotaCell, no lease, no caller check, no token, no meter wake 2026-11-06 22:55:06 +01:00

ModelConnector

The connector kit for GameCult minds: one typed, encrypted, replay-safe contract between a caller that needs a model turn and a daemon that holds the provider credential, so no project wires its own provider calls.

The connector meters nothing in dollars. It holds the subscription token, lends it, and publishes the plan's usage windows (usage_readout, claude_usage_readout on the lease socket, and plan_quota on every lease grant) for the minds to read and decide their own burn. The one usage-derived refusal is the provider's own (ProviderRejected), repeated until its reset.

Status: the model-connector crate and the claude-connector backend crate are live. No connector binary exists yet (the Claude connector binary is first, then an OpenAI-compatible one).

Entry points

Crate crates/model-connector:

  • src/contract.rs - the v4 wire contract (gamecult.model.*.v4): ProviderRequest, TransportInvocation, TransportResult, TransportReceipt, the typed StopReason, FailureKind and Refusal, InputItem::ReasoningState (a provider's opaque reasoning block, replayed by callers verbatim via TransportResult::transcript_items; the service refuses another provider's state in the in-flight tool exchange and drops it from earlier turns), and the canonical byte and digest functions that bind a result to its request. Every document is named MessagePack with unknown fields refused.
  • src/envelope.rs - the AES-256-GCM TransportEnvelope (binary ciphertext), its identity-binding AAD, the length-prefixed framing, and the CultNet carriage: each frame is a cultnet.operation_request.v0 / operation_response.v0 for service gamecult.model.connector, operation invoke (or usage_readout), its message_id the request id and its payload the base64 of the sealed envelope. usage_readout answers a UsageReadout (gamecult.model.usage_readout.v1): the backend's plan quota windows, each with utilization, reset, status and age; ConnectorClient::usage_readout asks.
  • schemas/ - one JSON Schema per v4 document, generated from the types.
  • src/client.rs - ConnectorClient, the one-request caller side.
  • src/service.rs (feature daemon) - TransportService: caller admission, durable replay (a retried request is answered byte-identically and the provider runs once), in-flight and capacity refusals.
  • src/serve.rs (feature daemon) - serve(), the loopback accept loop, over the ProviderBackend trait. A connector binary implements ProviderBackend for its provider and calls serve; the kit names no provider.

Crate crates/claude-connector (library only; the binary that wires it to serve is not written):

  • ClaudeCodeBackend implements ProviderBackend for provider claude-code by driving the unmodified claude binary, one child per call, in print stream-json mode with no tools, no MCP servers, no settings sources and the system prompt replaced. Tool-less turns only: a request with tools or a final tool result is refused before any spawn. A request's output schema goes to the CLI as --json-schema and the result's structured output is the one assistant text.
  • Reasoning state is a session handle (ClaudeCodeHandle) authenticated with a connector secret and bound to the caller it was minted for; a handle that cannot be proven to match the request and its caller re-seeds from the caller's transcript, and every resume forks.
  • ProcessPort is the one OS seam (StdProcessPort, unix, kills the child's whole process group); SessionStore and StderrSink are the other ports. ClaudeCodeConfig is what the binary hands in; the credential is read from a systemd credential and reaches the child on a pipe (CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR), never in an environment. QuotaCell keeps the plan windows the CLI's rate_limit_events report, for the usage readout and the lease grant.

Frame arithmetic is public in src/envelope.rs, measured on the carriage frame: frame_bytes_for_payload(n) is the frame a caller payload bound of n needs, payload_bytes_for_frame(frame) is the largest payload bound a frame allows, and MAX_IDENTITY_BYTES (256) bounds caller ids, request ids and model names. serve refuses a frame/payload pair that violates them (ServeError::FrameBound states the frame needed); the smallest legal frame is frame_bytes_for_payload(MIN_PAYLOAD_BYTES). A request id past the bound is refused on the wire as Refusal::Malformed, the reply echoing the id (the connection is dropped only if that reply cannot fit the frame); ConnectorClient refuses such an id locally with RequestIdTooLong.

The default feature set compiles contract, envelope and client only. The kit speaks v4 only: a v2 frame gets no answer.

Verify

cargo test --locked --workspace
cargo test --locked --workspace --features model-connector/daemon

crates/model-connector/tests/wire_golden_v4.rs pins the v4 documents and one sealed carriage frame to the fixtures in crates/model-connector/tests/fixtures/wire_v4 (regenerate with WIRE_V4_REGENERATE=1; see the fixtures' README), and crates/model-connector/tests/published_schemas.rs fails when schemas/ differs from the types (MODEL_CONNECTOR_REGENERATE_SCHEMAS=1 rewrites it).

Provenance and license

AGPL-3.0-only. See LICENSE.

The contract, envelope, client, replay service and serve loop were lifted from CodexConnector at fd7cec4, formerly PolyForm Noncommercial. Its sole copyright holder relicensed the lifted material as AGPL-3.0-only for this repository.