Skip to content

In development · Early Access

Live CRUD over one WebSocket.

CrudWS is an open protocol for create, read, update and delete over a single persistent connection. Clients subscribe to the records they show, and the server pushes every change to all of them the moment it is stored. Any language, any framework, any server.

wss://api.example.com/crud crudws.v0
→ {"op":"hello","ref":"1","token":"eyJhbGciOi..."}← {"op":"welcome","ref":"1","session":"s_8f2a"}→ {"op":"subscribe","ref":"2","resource":"tasks"}← {"op":"snapshot","ref":"2","items":[{"id":"t_7","version":4}],"cursor":"c_190"}→ {"op":"update","ref":"3","resource":"tasks","id":"t_7","version":4,"data":{"done":true}}← {"op":"ack","ref":"3","id":"t_7","version":5}← {"op":"change","resource":"tasks","kind":"updated","id":"t_7","version":5}

how it works

From one socket to every screen in four steps

One connection carries everything: authentication, reads, writes and the changes other people make. Pick a step to highlight its frames in the diagram.

CrudWS sequence: handshake, subscribe, change, broadcast Client A and client B each open a WebSocket to the server with hello and receive welcome. Both subscribe to tasks and receive a snapshot. Client A sends update for task t_7 with version 4; the server stores version 5 and answers ack. The server then pushes a change frame with version 5 to every subscriber, client A and client B. client A server client B handshake hello hello welcome welcome subscribe subscribe tasks subscribe tasks snapshot snapshot change v4 → v5 update t_7 v4 ack v5 broadcast change t_7 v5 change t_7 v5
Protocol v0 (draft). Frames shortened.

features

What the protocol defines, so you do not have to

Most teams rebuild these pieces on top of a raw WebSocket in every project. CrudWS writes them down once.

  • One connection for everything

    Reads, writes and pushed changes share one persistent WebSocket. No second channel for updates, no request per poll.

  • Push to every subscriber

    Change frames go to all clients subscribed to a resource, filtered for each client by what it may read.

  • Versions and conflicts

    Every record has a version. Stale writes come back as a conflict frame with the current record, so optimistic updates stay safe.

  • Auth per connection

    The client authenticates once in the handshake. The server binds the identity to the socket and ends the session when the token expires.

  • Permissions per resource

    The server checks every subscribe, read and write against the identity, per resource and optionally per record.

  • Resume after reconnect

    Each change carries a cursor. After a dropped connection the client subscribes again with its last cursor and receives what it missed.

protocol

A protocol you can read in one sitting

JSON text frames over WebSocket. Every frame has an op. Requests carry a ref that the server echoes in its answer, so answers may arrive in any order.

Message types
op purpose
helloclient → server Open the session: protocol version and credentials
subscribeclient → server Receive changes for a resource, optionally filtered
unsubscribeclient → server Stop a subscription
createclient → server Insert a record
readclient → server Fetch one record by id or a filtered list
updateclient → server Change fields, with the version the client last saw
deleteclient → server Remove a record, with its version
pingclient → server Keep the connection alive
welcomeserver → client Session accepted: id, heartbeat, granted permissions
snapshotserver → client Current records and a cursor, answer to subscribe or read
ackserver → client Write accepted, with the new version
changeserver → client Pushed to subscribers: created, updated or deleted
conflictserver → client Write rejected: stale version, current record attached
errorserver → client Request failed: code and message
pongserver → client Answer to ping
update and its possible answersjson
// client to server→ {"op":"update","ref":"3","resource":"tasks","id":"t_7","version":4,"data":{"done":true}} // accepted← {"op":"ack","ref":"3","id":"t_7","version":5} // or: someone else saved first← {"op":"conflict","ref":"3","id":"t_7","version":6,"data":{"done":false,"title":"Ship v0"}}
Envelope
op, ref, resource, id, version, data and where. Short field names, nothing else required.
Ordering
Changes to one resource arrive in commit order on each connection.
Error codes
unauthorized, forbidden, not_found, invalid, rate_limited, unsupported, internal.

Draft v0, subject to change. Early Access members receive the full draft and help shape v1.

any stack

Works with any stack

CrudWS is a wire protocol, not a framework. Any language that can open a WebSocket and parse JSON can speak it, on the client and on the server. Here is the same session in four languages.

  • JavaScript / TypeScript
  • Python
  • Go
  • PHP
  • Rust
  • Java / Kotlin
  • C# / .NET
  • Ruby
  • Elixir
  • Swift
  • Node.js
  • Django
  • Rails
  • Laravel
  • Phoenix
  • ASP.NET
  • Embedded / IoT

client.jsillustration, not an SDK

// Browser or Node.js 22+, built-in WebSocketconst ws = new WebSocket("wss://api.example.com/crud", "crudws.v0");const send = (frame) => ws.send(JSON.stringify(frame)); ws.onopen = () => {  send({ op: "hello", ref: "1", token });  send({ op: "subscribe", ref: "2", resource: "tasks" });}; ws.onmessage = ({ data }) => {  const frame = JSON.parse(data);  if (frame.op === "change") {    render(frame.id, frame.data, frame.version);  }}; // later: mark task t_7 as done, based on version 4send({  op: "update", ref: "3", resource: "tasks",  id: "t_7", version: 4, data: { done: true },});

client.pyillustration, not an SDK

# pip install websocketsimport asyncio, json, websockets async def main():    async with websockets.connect("wss://api.example.com/crud",                                  subprotocols=["crudws.v0"]) as ws:        await ws.send(json.dumps({"op": "hello", "ref": "1", "token": TOKEN}))        await ws.send(json.dumps({"op": "subscribe", "ref": "2", "resource": "tasks"}))         async for message in ws:            frame = json.loads(message)            if frame["op"] == "change":                print(frame["kind"], frame["id"], frame["version"]) asyncio.run(main())

main.goillustration, not an SDK

// go get github.com/coder/websocketconn, _, err := websocket.Dial(ctx, "wss://api.example.com/crud",    &websocket.DialOptions{Subprotocols: []string{"crudws.v0"}})if err != nil {    log.Fatal(err)}defer conn.CloseNow() wsjson.Write(ctx, conn, map[string]any{"op": "hello", "ref": "1", "token": token})wsjson.Write(ctx, conn, map[string]any{"op": "subscribe", "ref": "2", "resource": "tasks"}) for {    var frame map[string]any    if err := wsjson.Read(ctx, conn, &frame); err != nil {        break    }    if frame["op"] == "change" {        fmt.Println(frame["kind"], frame["id"], frame["version"])    }}

client.phpillustration, not an SDK

// composer require phrity/websocket$client = new WebSocket\Client("wss://api.example.com/crud");$client->addHeader("Sec-WebSocket-Protocol", "crudws.v0"); $client->text(json_encode(["op" => "hello", "ref" => "1", "token" => $token]));$client->text(json_encode(["op" => "subscribe", "ref" => "2", "resource" => "tasks"])); while ($message = $client->receive()) {    $frame = json_decode($message->getContent(), true);    if ($frame["op"] === "change") {        echo "{$frame["kind"]} {$frame["id"]} v{$frame["version"]}\n";    }}

Illustrations of the raw protocol with a common WebSocket library per language. They are not official SDKs; there are none yet.

use cases

For screens that must never show stale data

  • Live dashboards

    Orders, tickets, deployments, stock levels. Subscribe once and the numbers update themselves, without polling the API every few seconds.

    ← {"op":"change","resource":"orders","kind":"created","id":"o_1042","version":1}
  • Collaborative admin panels

    Two people open the same customer record. Versions turn the second save into a visible conflict instead of a silent overwrite.

    ← {"op":"conflict","ref":"9","resource":"customers","id":"c_311","version":8}
  • Live lists and feeds

    Kanban boards, activity feeds, queue views: rows appear, change and disappear on every open screen at the same time.

    ← {"op":"change","resource":"cards","kind":"deleted","id":"k_77","version":12}
  • IoT and device state

    Devices write their state as small updates, dashboards and other devices subscribe. Permissions per resource keep each device to its own records.

    → {"op":"update","ref":"41","resource":"devices","id":"pump-3","version":220,"data":{"rpm":1450}}

compare

How it compares

The alternatives are mature and widely used. CrudWS is a draft that does one thing: CRUD-shaped data with instant push, as an open protocol you can implement anywhere.

  • REST polling

    What it is
    HTTP API, asked again and again
    Writes
    Separate HTTP requests
    Changes reach clients
    On the next poll
    Concurrent edits
    Up to you (ETag and If-Match are possible)
    Backend
    Any
    Maturity
    Universal
  • GraphQL subscriptions

    What it is
    GraphQL operation type, streamed over WebSocket or SSE
    Writes
    Mutations, usually over HTTP
    Changes reach clients
    Per subscription defined in the schema
    Concurrent edits
    Up to you
    Backend
    Any GraphQL server
    Maturity
    Mature and widely deployed
  • Firebase / Supabase realtime

    What it is
    Managed backend platforms with realtime features
    Writes
    Through the vendor SDK or API
    Changes reach clients
    Pushed, built in
    Concurrent edits
    Transactions, otherwise last write wins (Firestore); your Postgres logic (Supabase)
    Backend
    Vendor platform (Supabase can be self-hosted)
    Maturity
    Production-grade, managed
  • CrudWS

    What it is
    Open protocol for CRUD and push over one WebSocket
    Writes
    Same socket, answered by request id
    Changes reach clients
    Pushed to every subscriber of the resource
    Concurrent edits
    Version on every write, conflict frame on mismatch
    Backend
    Any server that implements the spec
    Maturity
    In development, Early Access

Based on the commonly documented behaviour of each approach; details vary by implementation and configuration. If you need flexible queries across related data, GraphQL is the better fit.

faq

Questions

What is CrudWS?

An open protocol for real-time CRUD over WebSockets. It defines the JSON frames a client and a server exchange to create, read, update and delete records over one connection, and how the server pushes changes to every subscribed client.

Is it a library, a framework or a hosted service?

None of them. CrudWS is a protocol specification. You implement it on your own server, in your language, on your database. A reference implementation may follow; nothing is released yet.

Which languages and frameworks does it work with?

Any that can open a WebSocket and read JSON: JavaScript and TypeScript, Python, Go, PHP, Java, C#, Ruby, Elixir, Rust and others. Frameworks such as Django, Rails, Laravel or Phoenix are examples of servers that could implement it, not requirements.

How are conflicting edits handled?

Every record has a version. update and delete must carry the version the client last saw. If the record changed in the meantime, the server rejects the write with a conflict frame that contains the current record, and the client decides whether to merge, retry or show the conflict.

How is it different from GraphQL subscriptions?

GraphQL subscriptions stream the results of operations defined in a schema, and writes usually go over HTTP as mutations. CrudWS is narrower: records, versions and changes over one socket, without a query language. If you need flexible queries, GraphQL is the better fit.

Is CrudWS production ready?

No. The protocol is in development and the specification is a draft (v0). Please do not build production systems on it yet. Early Access is for teams who want to shape it.

What does Early Access include?

The full draft specification as soon as it is ready for review, a direct line to the people writing it, and early news about a reference implementation. Joining is free and commits you to nothing.

Who is behind CrudWS?

CrudWS is developed by MARCODES GmbH, a software company from Grünwald near Munich, Germany.

early access

Join Early Access

Get the draft specification first and help shape v1. We only write when there is something to review.

  • The full v0 draft as soon as it is ready for review
  • Your use case on the table while the protocol is still open
  • Early news about a reference implementation

Stack, data, number of clients: anything that helps us shape the spec.