{"id":13412528,"url":"https://github.com/dotchain/dot","last_synced_at":"2026-01-17T01:05:48.983Z","repository":{"id":50582674,"uuid":"114581200","full_name":"dotchain/dot","owner":"dotchain","description":"distributed data sync with operational transformation/transforms ","archived":false,"fork":false,"pushed_at":"2019-09-30T00:29:15.000Z","size":2539,"stargazers_count":84,"open_issues_count":0,"forks_count":8,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-04-13T18:49:23.231Z","etag":null,"topics":["distributed-systems","dot","golang","operational","ot","persistent","reactive","stream","sync","transformation","transformations","transforms","versioned","versioning"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dotchain.png","metadata":{"files":{"readme":"README.md","changelog":"changes/assorted_test.go","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-12-18T01:08:12.000Z","updated_at":"2024-03-15T06:52:29.000Z","dependencies_parsed_at":"2022-08-31T21:34:06.228Z","dependency_job_id":null,"html_url":"https://github.com/dotchain/dot","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dotchain%2Fdot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dotchain%2Fdot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dotchain%2Fdot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dotchain%2Fdot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dotchain","download_url":"https://codeload.github.com/dotchain/dot/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243625148,"owners_count":20321241,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["distributed-systems","dot","golang","operational","ot","persistent","reactive","stream","sync","transformation","transformations","transforms","versioned","versioning"],"created_at":"2024-07-30T20:01:25.748Z","updated_at":"2026-01-17T01:05:48.928Z","avatar_url":"https://github.com/dotchain.png","language":"Go","readme":"# DOT\n\n[![Status](https://travis-ci.com/dotchain/dot.svg?branch=master)](https://travis-ci.com/dotchain/dot?branch=master)\n[![GoDoc](https://godoc.org/github.com/dotchain/dot?status.svg)](https://godoc.org/github.com/dotchain/dot)\n[![codecov](https://codecov.io/gh/dotchain/dot/branch/master/graph/badge.svg)](https://codecov.io/gh/dotchain/dot)\n[![Go Report Card](https://goreportcard.com/badge/github.com/dotchain/dot)](https://goreportcard.com/report/github.com/dotchain/dot)\n\nThe DOT project is a blend of [operational\ntransformation](https://en.wikipedia.org/wiki/Operational_transformation),\n[CmRDT](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type#Operation-based_CRDTs),\n[persistent/immutable\ndatastructures](https://en.wikipedia.org/wiki/Persistent_data_structure)\nand [reactive](https://en.wikipedia.org/wiki/Reactive_programming)\nstream processing.\n\nThis is an implementation of distributed data synchronization of rich\ncustom data structures with conflict-free merging.\n\n## Status\n\nThis is very close to v1 release.  The [ES6](https://github.com/dotchain/dotjs) version\ninteroperates well right now but outstanding short-term issues have\nmore to do with consistency of the API surface than features:\n\n* ~The ES6 version has a simpler polling-based Network API that seems worth adopting here.~  ** Adopted **\n* ~The ES6 branch/undo integration also feels a lot simpler.~ ** Adopted **\n* The ES6 version prefers `replace()` instead of `update()`.\n* Nullable value types (i.e typed Nil values vs change.Nil vs nil) seems confusing.\n\n## Features\n\n1. Small, well tested mutations and immutable persistent values\n2. Support for rich user-defined types, not just collaborative text\n3. Streams and **Git-like** branching, merging support\n4. Simple network support (Gob serialization) and storage support\n5. Strong references support that are automatically updated with changes\n6. Rich builtin undo support for any type and mutation\n7. Folding (committed changes on top of uncommitted changes)\n8. Support for CmRDT types (see [crdt](https://github.com/dotchain/dot/tree/master/changes/crdt))\n\nAn interoperable ES6 version is available on [dotchain/dotjs](https://github.com/dotchain/dotjs) with a TODO MVC demo of it [here](https://github.com/dotchain/demos)\n\n\n## Contents\n1. [Status](#status)\n2. [Features](#features)\n3. [CRDTs](#crdts)\n4. [TODO Example](#todo-example)\n    1. [Server](#server)\n    2. [Types](#types)\n    3. [Type registration](#type-registration)\n    4. [Code generation](#code-generation)\n    5. [Toggling Complete](#toggling-complete)\n    6. [Changing description](#changing-description)\n    7. [Adding Todos](#adding-todos)\n    8. [Client connection](#client-connection)\n    9. [Running the demo](#running-the-demo)\n    10. [In browser demo](#in-browser-demo)\n5. [How it all works](#how-it-all-works)\n    1. [Applying changes](#applying-changes)\n    2. [Applying changes with streams](#applying-changes-with-streams)\n    3. [Composition of changes](#composition-of-changes)\n    4. [Convergence](#convergence)\n    5. [Convergence using streams](#convergence-using-streams)\n    6. [Revert and undo](#revert-and-undo)\n    7. [Folding](#folding)\n    8. [Branching of streams](#branching-of-streams)\n    9. [References](#references)\n    10. [Network synchronization and server](#network-synchronization-and-server)\n6. [Broad Issues](#broad-issues)\n7. [Contributing](#contributing)\n\n## CRDTs\n\nMuch of the framework can support operation-based CRDT changes which\nsimply appear as commutative operations (and so the merge operation is\ntrivial).  A set of types built this way is available in the\n[crdt](https://github.com/dotchain/dot/tree/master/changes/crdt)\nfolder.\n\n## TODO Example\n\nThe standard TODO-MVC example demonstrates the features of\ncollaborative (eventually consistent) distributed data structures.\n\n### Server\n\nThe DOT backend is essentially a simple log store.  All mutations to\nthe application state are represented as a **sequence of operations**\nand written in append-only fashion onto the log.  The following\nsnippet shows how to start a web server (though it does not include\nauthentication or CORs for example).\n\n```go example.global\n\nfunc Server() {\n\t// import net/http\n\t// import github.com/dotchain/dot\n\n        // uses a local-file backed bolt DB backend\n\thttp.Handle(\"/dot/\", dot.BoltServer(\"file.bolt\"))\n        http.ListenAndServe(\":8080\", nil)\n}\n```\n\nThe example above uses the\n[Bolt](http://godoc.org/github.com/dotchain/dot/ops/bolt)\nbackend for the actual storage of the operations.  There is also a\n[Postgres](http://godoc.org/github.com/dotchain/dot/ops/pg) backend\navailable.\n\nNote that the server above has no real reference to any application\nlogic: it simply accepts operations and writes them out in a\nguaranteed order broadcasting these to all the clients.\n\n### Types\n\nA TODO MVC app consists of only two core types: `Todo` and `TodoList`:\n\n```go example.global\n\n// Todo tracks a single todo item\ntype Todo struct {\n\tComplete bool\n        Description string\n}\n\n// TodoList tracks a collection of todo items\ntype TodoList []Todo\n\n```\n\n### Type registration\n\nTo use the types across the network, they have to be registered with\nthe codec (which will be\n[sjson](http://godoc.org/github.com/dotchain/dot/ops/sjson)\nin this example)\n\n```go example.global\n// import github.com/dotchain/dot/ops/nw\n\nfunc init() {\n\tnw.Register(Todo{})\n        nw.Register(TodoList{})\n}\n```\n\n### Code generation\n\nFor use with **DOT**, these types need to be augmented with standard\nmethods of the [Value](https://godoc.org/github.com/dotchain/dot/changes#Value)\ninterface (or in the case of lists like `TodoList`, also implement the\n[Collection](https://godoc.org/github.com/dotchain/dot/changes#Collection)\ninterface).\n\nThese interfaces are essentially the ability to take changes of the\nform **replace a sub field** or **replace items in the array** and\ncalculate the result of applying them.  They are mostly boilerplate\nand so can be autogenerated easily via the\n[dotc](https://godoc.org/github.com/dotchain/dot/x/dotc) package. See\n[code generation](codegen.md) for augmenting the above type\ninformation.\n\nThe code generation not only implements these two interfaces, it also\nproduces a new **Stream** type for **Todo** and **TodoList**.  A\nstream type is like a linked list with the `Value` field being the\nunderlying value and **Next()** returning the next entry in the stream\n(in case the value was modified).  And **Latest** returns the\nlast entry in the stream at that point.  Also, each stream type\nimplements mutation methods to easily modify the value associated with\na stream.\n\nWhat makes the streams interesting is that two different modifications\nfrom the same state cause the **Latest** of both to be the same with\nthe effect of both *merged*.  (This is done using the magic of\noperational transformations)\n\n### Toggling Complete\n\nThe code to toggle the `Complete` field of a particular todo item\nlooks like the following:\n\n```go example.global\nfunc Toggle(t *TodoListStream, index int) {\n\t// TodoListStream.Item() is generated code. It returns\n        // a stream of the n'th element of the slice so that\n        // particular stream can be modified. When that stream is\n        // modified, the effect is automatically merged into the\n        // parent (and available via .Next of the parent stream)\n\ttodoStream := t.Item(index) \n\n\t// TodoStream.Complete is generated code. It returns a stream\n        // for the Todo.Complete field so that it can be modified. As\n        // with slices above, mutations on the field's stream are\n        // reflected on the struct stream (via .Next or .Latest())\n        completeStream := todoStream.Complete()\n\n\t// completeStream is of type streams.Bool. All streams\n        // implement the simple Update(newValue) method that replaces\n        // the current value with a new value.\n        completeStream.Update(!completeStream.Value)\n}\n```\n\nNote that the function does not return any value here but the updates\ncan be fetched by calling `.Latest()` on any of the corresponding\nstreams. If a single stream instance has multiple edits, the\n`Latest()` value is the merged value of all those edits. \n\n### Changing description\n\nThe code for changing the `Description` field is similar.  The string\n`Description` field in `Todo` maps to a `streams.S16` stream. This\nimplements an `Update()` method like all streams.\n\nBut to make things interesting, lets look at **splicing** rather\nthan replacing the whole string. Splicing is taking a subsequence of\nthe string at a particular position and replacing it with the provided\nvalue.  It captures insert,  delete and replace in one operation.\n\nThis probably better mimics what text editors do and a benefit of such\nhigh granularity edits is that when two users edit the same text, the\nedits will merge quite cleanly so\nlong as they don't directly touch the same characters.\n\n```go example.global\nfunc SpliceDescription(t *TodoListStream, index, offset, count int, replacement string) {\n\t// TodoListStream.Item() is generated code. It returns\n        // a stream of the n'th element of the slice so that\n        // particular stream can be modified. When that stream is\n        // modified, the effect is automatically merged into the\n        // parent (and available via .Next of the parent stream)\n\ttodoStream := t.Item(index) \n\n\t// TodoStream.Description is generated code. It returns a\n        // stream for the Todo.Description field so that it can be\n        // modified. As with slices above, mutations on the field's\n        // stream are reflected on the struct stream (via .Next or\n        // .Latest()) \n\t// TodoStream.Description() returns streams.S16 type\n        descStream := todoStream.Description()\n\n\t// streams.S16 implements Splice(offset, removeCount, replacement)\n        descStream.Splice(offset, count, replacement)\n}\n```\n\n### Adding Todos\n\nAdding a Todo is relatively simple as well:\n\n```go example.global\nfunc AddTodo(t *TodoListStream, todo Todo) {\n\t// All slice streams implement Splice(offset, removeCount, replacement)\n\tt.Splice(len(t.Value), 0, todo)\n}\n```\n\nThe use of `Splice` in this example should hint that (just like\nstrings) collections support insertion/deletion at arbitrary points within\nvia the Splice method. In addition to supporting this, collections also\nsupport the `Move(offset, count, distance)` method to move some items\naround within the collection\n\n### Client connection\n\nSetting up the client requires connecting to the URL where the server\nis hosted.  In addition, the code below illustrates how sessions\ncould be saved and restarted if needed.\n\n```go example.global\n// import time\n// import sync\n// import github.com/dotchain/dot\n\nvar Lock sync.Mutex\nfunc Client(stop chan struct{}, render func(*TodoListStream)) {\n\turl := \"http://localhost:8080/dot/\"\n        session, todos := SavedSession()\n\ts, store := session.NonBlockingStream(url, nil)\n        defer store.Close()\n\n\ttodosStream := \u0026TodoListStream{Stream: s, Value: todos}\n\n        ticker := time.NewTicker(500*time.Millisecond)\n        changed := true\n\tfor {\n        \tif changed {\n\t\t\trender(todosStream)\n                }\n        \tselect {\n                case \u003c- stop:\n                \treturn\n                case \u003c- ticker.C:\n                }\n\n                Lock.Lock()\n\t\ts.Push()\n                s.Pull()\n                next := todosStream.Latest()\n                changed = next != todosStream\n                todosStream, s = next, next.Stream\n                Lock.Unlock()\n        }\n\n       \tSaveSession(session, todosStream.Value)\n}\n\n\nfunc SaveSession(s *dot.Session, todos TodoList) {\n\t// this is not yet implemented. if it were, then\n        // this value should be persisted locally and returned\n        // by the call to savedSession\n}\n\nfunc SavedSession() (s *dot.Session, todos TodoList) {\n\t// this is not yet implemented. return default values\n        return dot.NewSession(), nil\n}\n\n```\n\n### Running the demo\n\nThe TODO MVC demo is in the\n[example](https://github.com/dotchain/dot/tree/master/example)\nfolder.\n\nThe snippets in this markdown file can be used to generate the\n**todo.go** file and then auto-generate the \"generated.go\" file:\n\n```sh\n$ go get github.com/tvastar/test/cmd/testmd\n$ testmd -pkg example -o examples/todo.go README.md\n$ testmd -pkg main codegen.md \u003e examples/generated.go\n```\n\nThe server can then be started by:\n\n```sh\n$ go run server.go\n```\n\nThe client can then be started by:\n\n```sh\n$ go run client.go\n```\n\nThe provide client.go stub file simply appends a task every 10\nseconds.\n\n### In browser demo\n\nThe [fuss](https://github.com/dotchain/fuss) project has demos of a\nTODO-MVC app built on top of this framework using\n[gopherjs](https://github.com/gopherjs/gopherjs).  In particular, the\n[collab](https://github.com/dotchain/fuss/tree/master/todo/collab)\nfolder illustrates how simple the code is to make something work\ncollaboratively (the rest of the code base is not even aware of\nwhether things are collaborative).\n\n## How it all works\n\nThere are values, changes and streams.\n\n1. **Values** implement the\n[Value](https://godoc.org/github.com/dotchain/dot/changes#Value)\ninterface. If the value represents a collection, it also implements\nthe\n[Collection](https://godoc.org/github.com/dotchain/dot/changes#Collection)\ninterface.\n2. **Changes** represent mutations to values that can be *merged*. If\ntwo independent changes are made to the same value, they can be merged\nso that the `A + merged(B) = B + merged(A)`.  This is represented by\nthe [Change](https://godoc.org/github.com/dotchain/dot/changes#Change)\ninterface. The\n[changes](https://godoc.org/github.com/dotchain/dot/changes) package\nimplements the core changes with composition that allow richer changes\nto be implemented.\n3. **Streams** represent a sequence of changes to a value, except it\nis **convergent** -- if multiple writers modify a value, they each get\na separate stream instance that only reflects their local change but\nfollowing the *Next* chain will guarantee that all versions end up with\nthe same final value.\n\n### Applying changes\n\nThe following example illustrates how to edit a string with values and\nchanges\n\n```golang dot_test.Example_applyingChanges\n\t// import fmt\n        // import github.com/dotchain/dot/changes\n        // import github.com/dotchain/dot/changes/types\n\n\t// S8 is DOT-compatible string type with UTF8 string indices\n\tinitial := types.S8(\"hello\")\n\n        append := changes.Splice{\n        \tOffset: len(\"hello\"), // end of \"hello\"\n                Before: types.S8(\"\"), // nothing to remove\n                After: types.S8(\" world\"), // insert \" world\"\n        }\n\n        // apply the change\n        updated := initial.Apply(nil, append)\n\n\tfmt.Println(updated)\n        // Output: hello world\n```\n\n### Applying changes with streams\n\nA less verbose *stream* based version (preferred) would look like so:\n\n```golang dot_test.Example_applyingChangesUsingStreams\n\t// import fmt\n        // import github.com/dotchain/dot/streams\n\n        initial := \u0026streams.S8{Stream: streams.New(), Value: \"hello\"}\n        updated := initial.Splice(5, 0, \" world\")\n\n\tfmt.Println(updated.Value)\n        // Output: hello world\n```\n\nThe [changes](https://godoc.org/github.com/dotchain/dot/changes)\npackage implements the core changes: **Splice**, **Move** and\n**Replace**.  The logical model for these changes is to treat all\nvalues as either being like *arrays* or like *maps*.  The actual\nunderlying datatype can be different as long as the array/map\nsemantics is implemented.\n\n### Composition of changes\n\nChanges can be *composed* together. A simple form of composition is\njust a set of changes:\n\n```golang dot_test.Example_changesetComposition\n\t// import fmt\n        // import github.com/dotchain/dot/changes\n        // import github.com/dotchain/dot/changes/types\n\n\tinitial := types.S8(\"hello\")\n\n        // append \" world\" =\u003e \"hello world\"\n        append1 := changes.Splice{\n        \tOffset: len(\"hello\"),\n                Before: types.S8(\"\"),\n                After: types.S8(\" world\"),\n        }\n\n        // append \".\" =\u003e \"hello world.\"\n        append2 := changes.Splice{\n        \tOffset: len(\"hello world\"),\n                Before: types.S8(\"\"),\n                After: types.S8(\".\"),\n        }\n        \n        // now combine the two appends and apply\n        both := changes.ChangeSet{append1, append2}\n        updated := initial.Apply(nil, both)\n        fmt.Println(updated)\n\n\t// Output: hello world.\n```\n\nAnother form of composition is modifying a sub-element such as an\narray element or a dictionary path:\n\n```golang dot_test.Example_pathComposition\n\t// import fmt\n        // import github.com/dotchain/dot/changes\n        // import github.com/dotchain/dot/changes/types\n\n        // types.A is a generic array type and types.M is a map type\n        initial := types.A{types.M{\"hello\": types.S8(\"world\")}}\n\n        // replace \"world\" with \"world!\"\n        replace := changes.Replace{Before: types.S8(\"world\"), After: types.S8(\"world!\")}\n\n        // replace \"world\" with \"world!\" of initial[0][\"hello\"]\n        path := []interface{}{0, \"hello\"}\n        c := changes.PathChange{Path: path, Change: replace}\n        updated := initial.Apply(nil, c)\n        fmt.Println(updated)\n\n\t// Output: [map[hello:world!]]        \n```\n\n### Convergence\n\nThe core property of all changes is the ability to guarantee\n*convergence* when two mutations are attempted on the same state:\n\n```golang dot_test.Example_convergence\n\t// import fmt\n        // import github.com/dotchain/dot/changes\n        // import github.com/dotchain/dot/changes/types\n\n\tinitial := types.S8(\"hello\")\n\n\t// two changes: append \" world\" and delete \"lo\"\n\tinsert := changes.Splice{Offset: 5, Before: types.S8(\"\"), After: types.S8(\" world\")}\n\tremove := changes.Splice{Offset: 3, Before: types.S8(\"lo\"), After: types.S8(\"\")}\n\n\t// two versions derived from initial\n        inserted := initial.Apply(nil, insert)\n        removed := initial.Apply(nil, remove)\n\n        // merge the changes\n        removex, insertx := insert.Merge(remove)\n\n        // converge by applying the above\n        final1 := inserted.Apply(nil, removex)\n        final2 := removed.Apply(nil, insertx)\n\n        fmt.Println(final1, final1 == final2)\n        // Output: hel world true\n```\n\n### Convergence using streams\n\nThe same convergence example is a lot easier to read with streams:\n\n```golang dot_test.Example_convergenceUsingStreams\n\t// import fmt\n        // import github.com/dotchain/dot/streams\n\n\tinitial := streams.S8{Stream:  streams.New(), Value: \"hello\"}\n\n\t// two changes: append \" world\" and delete \"lo\"\n        s1 := initial.Splice(5, 0, \" world\")\n\ts2 := initial.Splice(3, len(\"lo\"), \"\")\n\n\t// streams automatically merge because they are both\n        // based on initial\n        s1 = s1.Latest()\n        s2 = s2.Latest()\n\n        fmt.Println(s1.Value, s1.Value == s2.Value)\n        // Output: hel world true\n```\n\nThe ability to *merge* two independent changes done to the same\ninitial state is the basis for the eventual convergence of the data\nstructures.  The\n[changes](http://godoc.org/github.com/dotchain/dot/changes) package \nhas fairly intensive tests to cover the change types defined there,\nboth individually and in composition. \n\n### Revert and undo\n\nAll the predefined types of changes in DOT (see\n[changes](https://godoc.org/github.com/dotchain/dot/changes)) are\ncarefully designed so that every change can be inverted easily without\nreference to the underlying value.  For example,\n[changes.Replace](https://godoc.org/github.com/dotchain/dot/changes#Replace)\nhas both the **Before** and **After** fields instead of just keeping\nthe **After**.  This allows the reverse to be computed quite easily by\nswapping the two fields.  This does generally incur additional storage\nexpenses but the tradeoff is that code gets much simpler  to work\nwith.\n\nIn particular, it is possible to build generic\n[undo](https://godoc.org/github.com/dotchain/dot/streams/undo) support\nquite easily and naturally.  The following example shows both **Undo**\nand **Redo** being invoked from an undo stack.\n\n```go dot_test.Example_undoStreams\n\t// import fmt\n        // import github.com/dotchain/dot/streams\n        // import github.com/dotchain/dot/changes\n        // import github.com/dotchain/dot/changes/types\n        // import github.com/dotchain/dot/streams/undo\n\n\t// create master, undoable child and the undo stack itself\n\tmaster := \u0026streams.S16{Stream: streams.New(), Value: \"hello\"}\n        s := undo.New(master.Stream)\n        undoableChild := \u0026streams.S16{Stream: s, Value: master.Value}\n\n\t// change hello =\u003e Hello\n\tundoableChild = undoableChild.Splice(0, len(\"h\"), \"H\")\n\tfmt.Println(undoableChild.Value)\n\n\t// for kicks, update master hello =\u003e hello$ as if it came\n        // from the server\n        master.Splice(len(\"hello\"), 0, \"$\")\n\n\t// now undo this via the stack\n        s.Undo()\n\n\t// now undoableChild should be hello$\n        undoableChild = undoableChild.Latest()\n        fmt.Println(undoableChild.Value)\n\n\t// now redo the last operation to get Hello$\n        s.Redo()\n        undoableChild = undoableChild.Latest()\n        fmt.Println(undoableChild.Value)\n        \n\t// Output:\n        // Hello\n        // hello$\n        // Hello$\n```\n\n### Folding\n\nIn the case of editors, folding refers to a piece of text that has\nbeen hidden away. The difficulty with implementing this in a\ncollaborative setting is that as external edits come in, the fold has\nto be maintained.\n\nThe design of DOT allows for an elegant way to achieve this: consider\nthe \"folding\" as a local change (replacing the folded region with\nsay \"...\"). This local change is never meant to be sent out.  All\nchanges to the unfolded and folded versions can be proxied quite\nnicely without much app involvement:\n\n```go dot_test.Example_folding\n\t// import fmt\n        // import github.com/dotchain/dot/streams\n        // import github.com/dotchain/dot/changes\n        // import github.com/dotchain/dot/changes/types\n        // import github.com/dotchain/dot/x/fold\n\n\t// create master, folded child and the folding itself\n\tmaster := \u0026streams.S16{Stream: streams.New(), Value: \"hello world!\"}\n        foldChange := changes.Splice{\n        \tOffset: len(\"hello\"),\n                Before: types.S16(\" world\"),\n                After: types.S16(\"...\"),\n        }\n        foldedStream := fold.New(foldChange, master.Stream)\n        folded := \u0026streams.S16{Stream: foldedStream, Value :\"hello...!\"}\n\n        // folded:  hello...! =\u003e Hello...!!!\n\tfolded = folded.Splice(0, len(\"h\"), \"H\")\n        folded = folded.Splice(len(\"Hello...!\"), 0, \"!!\")\n        fmt.Println(folded.Value)\n\n\t// master: hello world =\u003e hullo world\n\tmaster = master.Splice(len(\"h\"), len(\"e\"), \"u\")\n        fmt.Println(master.Value)\n\n        // now folded = Hullo...!!!\n        fmt.Println(folded.Latest().Value)\n\n        // master = Hullo world!!!\n        fmt.Println(master.Latest().Value)\n\n\t// Output:\n        // Hello...!!!\n        // hullo world!\n        // Hullo...!!!\n        // Hullo world!!!\n```\n\n### Branching of streams\n\nStreams in DOT can also be branched *a la* Git. Changes made in\nbranches do not affect the master or vice-versa -- until one of Pull\nor Push are called.\n\n```golang dot_test.Example_branching\n\t// import fmt\n        // import github.com/dotchain/dot/streams\n        // import github.com/dotchain/dot/changes\n        // import github.com/dotchain/dot/changes/types        \n        \n        // local is a branch of master\n        master := \u0026streams.S16{Stream: streams.New(), Value: \"hello\"}\n        local := \u0026streams.S16{Stream: streams.Branch(master.Stream), Value: master.Value}\n\n\t// edit locally: hello =\u003e hallo\n\tlocal.Splice(len(\"h\"), len(\"e\"), \"a\")\n\n\t// changes will not be reflected on master yet\n        fmt.Println(master.Latest().Value)\n\n\t// push local changes up to master now\n        local.Stream.Push()\n\n\t// now master = hallo\n\tfmt.Println(master.Latest().Value)\n\n        // Output:\n        // hello\n        // hallo\n```\n\nThere are other neat benefits to the branching model: it provides a\nfine grained control for pulling changes from the network on demand\nand suspending it as well as providing a way for making local\nchanges.\n\n### References\n\nThere are two broad cases where a JSON-like structure is not quite\nenough.\n\n1. Editors often need to track the cursor or selection which can be\nthought of as offsets in the editor text.  When changes happen to the\ntext, for example, the offset would need to be updated.\n2. Objects often need to refer to other parts of the JSON-tree. For\nexample, one can represent a graph using the array, map primitives\nwith the addition of references. When changes happen, these too would\nneed to be updated.\n\nThe [refs](https://godoc.org/github.com/dotchain/dot/refs) package\nimplements a set of types that help work with these.  In particular,\nit defines a\n[Container](https://godoc.org/github.com/dotchain/dot/refs#Container)\nvalue that allows elements within to refer to other elements.\n\n\n### Network synchronization and server\n\nDOT uses a fairly simple backend\n[Store](https://godoc.org/github.com/dotchain/dot/ops#Store)\ninterface: an append-only dumb log. The\n[Bolt](https://godoc.org/github.com/dotchain/dot/ops/bolt) and\n[Postgres](https://godoc.org/github.com/dotchain/dot/ops/pg)\nimplementations are quite simple and other data backends can be\neasily added.\n\nSee [Server](#server) and [Client connection](#client-connection) for\nsample server and client applications.  Note that the journal approach\nused implies that the journal size only increases and so clients will\neventually take a while to rebuild their state from the journal. The\nclient API allows snapshotting state to make the rebuilds faster.\nThere is no server support for snapshots though it is possible to\nbuild one rather easily\n\n## Broad Issues\n\n1. changes.Context/changes.Meta are not fully integrated\n2. ~gob-encoding makes it harder to deal with other languages but JSON\nencodindg wont work with interfaces.~\n   * Added `sjson encoding` as a portable (if verbose) format.\n   * The [ES6 dotjs](https://github.com/dotchain/dotjs) package uses this as the native format.\n3. Cross-object merging and persisted branches need more platform support\n   * Snapshots are somewhat related to this as well.\n4. Full rich-text support with collaborative cursors still needs work\nwith references and reference containers.\n5. Code generation can infer types from regular go declarations\n6. Snapshots and transient states need some sugar.\n\n## Contributing\n\nPlease see [CONTRIBUTING.md](CONTRIBUTING.md).\n\n","funding_links":[],"categories":["Distributed Systems"],"sub_categories":["Advanced Console UIs"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdotchain%2Fdot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdotchain%2Fdot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdotchain%2Fdot/lists"}