Skip to main content

statewire

The Statewire protocol for Python, on top of pinned.

Statewire is a PinnedAPI (one live instance per id cluster-wide) that replicates one JSON object — any State — over envelope streams:

  • GET /stream (SSE) and /ws (WebSocket twin) — every message is an envelope {"ops"?, "res"?, "ack"?, "syn"?, "fin"?}. The first envelope on attach is a full snapshot (ops: [{"op": "replace", "path": [], "value": <state>}]) with the syn handshake {"lastSeq", "lease"?}: the client's last admitted command seq (-1 unknown client) and the attach's writer-lease token. Sessions are single-writer per client id: attaching takes the lease, and any other live stream of that client id ends with fin {"reason": "superseded"}.
  • POST /commands — one command at a time, a method call {"method": <name>, "params": [...]} routed to the @command handler registered under that name. Commands carry Statewire-Client-Id, a monotonic Statewire-Command-Seq, and the current Statewire-Lease. The HTTP response is receipt only (200 {} | 400 malformed | 409 seq gap | 412 unknown client | 423 stale lease); the protocol statuses carry a discriminator body ({"error": "seq-gap" | "unknown-client" | "stale-lease", "message"?}) so clients can tell statewire's verdict from a middleware-minted bare status; verdicts ride the stream. Over WS the same commands arrive as {"method": <name>, "params": [...], "seq": <int>} frames — no lease header, holding the connection is the lease.

ops are Immer-style deltas with array paths (object keys as strings, list indices as ints): replace sets a value, add splices — its final int segment indexes into the parent, so a list parent gains an element and a string parent gains text at that offset — remove deletes, and event is transient (never in snapshots). ack is the cumulative watermark for the issuing client, emitted after the command's effect ops. res carries responses ({"seq", "type": "accepted" | "rejected" | "pending" | "crashed" | "result-unavailable", "message"?, "payload"?}): the envelope that first covers a seq states the command's fate — a terminal response, a pending response (result follows in a later envelope), or nothing = void. fin ({"reason": "evicted" | "error" | "gone" | "superseded", "message"?}) is always the last envelope.

Handler outcomes: the return value becomes the terminal accepted payload; raise StatewireReject becomes rejected; returning a CommandExecution keeps the command open past the handler's return — its ack() flushes the covering ack with a pending response, its resolve/reject produce the late terminal response (post-ack failures are terminal rejected, not stream faults). Duplicate seqs never re-run: they are answered from a ~30s result cache / in-flight registry — in the POST body over HTTP (200 {"res": ...}), as a res on the stream over WS.

The protocol is generic: it says nothing about messages, queues, or agents — it only replicates whatever self.state dict you assign and dispatches whatever commands you declare. Domain-specific layers (see the harness-sdk package) sit on top.

from statewire import Statewire, command


class Thread(Statewire):
    async def lifespan(self):
        self.state = {"messages": []}  # yielding without setting self.state throws
        yield

    @command
    async def addMessage(self, message_id: str, content: str):
        self.state["messages"].append({"id": message_id, "content": content})
        self.create_task(self.run())  # long work outside the inbox; returning here => ack

@command registers the handler under the method's own name; @command("name") registers it under an explicit wire name. Params are positional. An unknown method or a params/signature mismatch is a rejected response on the stream, not an HTTP error.

self.state is a change-tracking proxy: mutate it plainly and the ops replicate to every attached stream. += on a string becomes an end-offset text-insert add op of the suffix; other mutations become narrow replace / add / remove ops. Mutations within one synchronous segment coalesce into a single envelope.

Extra routes

Need an endpoint beyond the protocol trio (a health check, a file upload)? Decorate a method with pinned's route escape hatch:

from pinned import route
from statewire import Statewire


class Thread(Statewire):
    @route.get("/health")
    async def health(self, request):
        return {"ok": True}

The reserved protocol paths /statewire, /stream, /commands, and /ws are Statewire's own; a subclass that decorates a @route onto any of them raises at class-definition time rather than silently shadowing the protocol.

GET /statewire is the meta endpoint: {"protocol": 1, "commands": [<names>]}. Setting envelope_ts = True on the subclass stamps every non-empty envelope (snapshot included) with "ts" (epoch ms) for debuggability.

Layering

pinned        one live instance per id, with an HTTP surface
  └─ statewire   the Statewire protocol: /stream + /ws (envelopes) + /commands
       └─ harness-sdk   HarnessState types, queueing, deepagents

Develop

uv sync
uv run pytest

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

statewire-0.1.0.tar.gz (55.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

statewire-0.1.0-py3-none-any.whl (35.3 kB view details)

Uploaded Python 3

File details

Details for the file statewire-0.1.0.tar.gz.

File metadata

  • Download URL: statewire-0.1.0.tar.gz
  • Upload date:
  • Size: 55.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for statewire-0.1.0.tar.gz
Algorithm Hash digest
SHA256 8b6579ba973f0c4ca3b47b35c8dc7c93a54f53a6ef46c9144940ef42bf0394f6
MD5 28e89ab52aaf806833cad6aad48deb75
BLAKE2b-256 f46f8cd9ca0e979de962578f13fb9c9e047ea4985c3e2b04d3db70639d0eb91e

See more details on using hashes here.

File details

Details for the file statewire-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: statewire-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 35.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for statewire-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d770cf06a38160bc2b20e8e3a06f5eb8a8bccb6bc79a3b704e039eaef2b06927
MD5 893cae2c103183bbe22e6b38764c211e
BLAKE2b-256 8202aaaf24731566c3de6e593d5d9c88560028ccc439c69018f3733c583b5257

See more details on using hashes here.

Release history Release notifications | RSS feed

0.16.1

2 files

0.16.0

2 files

0.15.0

2 files

0.14.0

2 files

0.13.0

2 files

0.12.5

2 files

0.12.4

2 files

0.12.3

2 files

0.12.2

2 files

0.12.1

2 files

0.12.0

2 files

0.11.1

2 files

0.11.0

2 files

0.10.0

2 files

0.9.2

2 files

0.9.1

2 files

0.9.0

2 files

0.8.7

2 files

0.8.6

2 files

0.8.5

2 files

0.8.4

2 files

0.8.3

2 files

0.8.2

2 files

0.8.1

2 files

0.8.0

2 files

0.7.2

2 files

0.7.1

2 files

0.7.0

2 files

0.6.2

2 files

0.6.1

2 files

0.6.0

2 files

0.5.0

2 files

0.4.7

2 files

0.4.6

2 files

0.4.5

2 files

0.4.4

2 files

0.4.3

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.3

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

0.2.0

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page