jsonot is a Go library for JSON Operational Transformation (OT). It helps you apply JSON OT operations, transform concurrent edits, build collaboration backends, and generate diff / revert operations for versioned JSON documents.
The project is inspired by ylgrgyq/json0-rs.
Use jsonot when you need a Go-native JSON OT engine for scenarios such as:
- collaborative editing in Go
- WebSocket collaborative editor backends
- ShareDB-style backends in Go
- JSON diff / revert and version restore flows
- server-authoritative merge pipelines for rich text or structured JSON documents
If you are searching for a Go OT library, JSON OT for Go, or a ShareDB alternative in Go, this repository is the main entry point.
jsonot is a good fit when you need:
- a backend OT core for collaborative editors
- server-side rebasing with
Transform - document restore and rollback with
Diff - a small Go package instead of a full collaboration platform
- a Go package that you can embed inside your own WebSocket or HTTP services
Choose the entry point that matches your goal:
- I want to use the library directly → start with Quick start and What problems does jsonot solve?
- I want a collaborative editing demo → see WebSocket demo and BlockNote demo
- I want a ShareDB-style backend → see
jsonot/sharedb
jsonot provides the OT core needed to rebase concurrent operations and keep a server-authoritative document consistent.
Diff can generate JSON OT that transforms one version of a document into another, which is useful for rollback, restore, comparison, and audit flows.
The sharedb package shows how to layer snapshots, versions, submit, and subscribe APIs on top of the core OT engine.
- Apply JSON OT operations to objects, arrays, numbers, and text values
- Transform concurrent operations with
Transform - Compose multiple operations into a single operation
- Generate diff / revert operations from two JSON documents with
Diff - Build operations with the provided builders
- Switch between the default Sonic backend and the AJSON backend
- Use the lightweight
sharedbpackage for ShareDB-style backend flows - Explore runnable collaboration demos for WebSocket text editing and BlockNote documents
| Capability | jsonot | Where to start |
|---|---|---|
| Apply JSON OT to documents | Yes | Apply, Quick start |
| Rebase concurrent operations | Yes | Transform, Transforming concurrent operations |
| Compose multiple ops | Yes | Compose |
| Generate diff / revert operations | Yes | Generating revert operations from two versions |
| ShareDB-style backend abstraction | Yes | jsonot/sharedb |
| WebSocket collaboration demo | Yes | examples/websocket |
| Rich text collaboration demo | Yes | examples/blocknote-collab |
| Scenario | Recommended starting point |
|---|---|
| Build a collaborative editor backend in Go | WebSocket demo |
| Build a rich text collaboration backend | BlockNote demo |
| Build a ShareDB-like backend in Go | jsonot/sharedb |
| Restore an older JSON version | Diff |
| Rebase concurrent client operations on the server | Transform |
package main
import (
"context"
"fmt"
"github.com/edocevol/jsonot"
)
func main() {
ot := jsonot.NewJSONOperationTransformer()
doc, _ := jsonot.UnmarshalValue([]byte(`{
"name": "json0",
"hobbies": ["reading", "coding", "music"],
"info": {"email": "example@mail.qq.com"}
}`))
rawOps, _ := jsonot.UnmarshalValue([]byte(`[
{"p": ["hobbies", "2"], "ld": "music"},
{"p": ["hobbies", "3"], "li": "movie"},
{"p": ["info", "email"], "od": "example@mail.qq.com"}
]`))
components := ot.OperationComponentsFromValue(rawOps)
op := jsonot.NewOperation(components.MustGet())
result := ot.Apply(context.Background(), doc, op)
fmt.Println(string(result.MustGet().RawMessage()))
}Operation components are represented as JSON objects. The p field is the target path.
li: list insertld: list deletelm: list moveoi: object insertod: object deletena: number add subtypet+o: custom subtype name and operand
Examples:
{"p": ["todos", "1"], "li": {"title": "review"}}
{"p": ["profile", "name"], "oi": "jsonot"}
{"p": ["counter"], "na": 1}
{"p": ["content"], "t": "text", "o": {"p": 5, "i": " world"}}ot := jsonot.NewJSONOperationTransformer()
factory := ot.OperationFactory()
component := factory.ObjectOperationBuilder(
jsonot.NewPathFromKeys([]string{"profile", "name"}),
).Insert(jsonot.ValueFromPrimitive("jsonot")).Build()
op := jsonot.NewOperation([]*jsonot.OperationComponent{component.MustGet()})Use Transform when two operations were created from the same base document and need to be rebased against each other.
leftPrime, rightPrime, err := ot.Transform(context.Background(), leftOp, rightOp)After that, apply leftPrime after rightOp, or rightPrime after leftOp.
Use Diff to generate JSON OT that transforms one document into another.
current, _ := jsonot.UnmarshalValue([]byte(`{"version":2,"items":[1,2,3]}`))
previous, _ := jsonot.UnmarshalValue([]byte(`{"version":1,"items":[1,3]}`))
revertOp := ot.Diff(context.Background(), current, previous)
restored := ot.Apply(context.Background(), current, revertOp.MustGet())This is useful for version comparison and restore flows. When a structure changes too much, Diff falls back to replacing that subtree, including the root document when needed.
The library uses Sonic by default.
jsonot.UseSonic()
jsonot.UseAJSON()Switch the backend before creating or parsing values so all Value instances come from the same implementation.
- What is jsonot?
- How to build collaborative editing in Go with JSON OT
- How to use jsonot for JSON diff / revert
- How to build a ShareDB-style backend in Go
- WebSocket collaboration demo
- BlockNote collaboration demo
jsonot/sharedb
If you prefer a server-authoritative merge flow and explicit rebasing with Transform, jsonot gives you an OT-centric path instead of a CRDT data model.
ShareDB is a full collaboration system. jsonot focuses on a Go-native OT engine plus lightweight backend primitives that you can embed in your own services.
Plain diff can tell you what changed. jsonot also helps you transform and rebase concurrent operations before applying them.
Yes. The core package handles OT transforms and applies operations, while the examples show how to build collaborative editing servers in Go.
Yes, when you want Go-native OT primitives and lightweight backend abstractions instead of the full ShareDB stack. Start with jsonot/sharedb.
Yes. Use Diff to derive an operation that transforms the current document into an earlier version.
Yes. The BlockNote demo shows a rich text collaboration backend that sends snapshots and merges them with Diff, Transform, and Apply.
go get github.com/edocevol/jsonot- copy the quick-start example from this README
- run
go test ./... - open one of the demos when you need an end-to-end collaboration flow
- CI runs
go test ./...in PR Check - tag pushes validate tests before publishing a GitHub release in Release Tag
- the repository includes runnable demos and a dedicated
sharedbpackage
go test ./...