SkillAgentSearch skills...

Chainpad

Realtime Collaborative Editor Algorithm

Install / Use

/learn @cryptpad/Chainpad
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

ChainPad

XWiki labs logo

ChainPad Algorithm is a Realtime Collaborative Editor algorithm based on Nakamoto Blockchains. This implementation is designed to run with a dumb broadcasting server but with minimal effort, the algorithm could be ported to full peer-to-peer. Because the ChainPad server need not be aware of the content which is being edited, different types of editors can exist in harmony on the same system.

This library is currently licensed as LGPL-2.1. Previous versions of this library (v5.2.7 and below) were licensed as AGPL-3.0.

Getting Started

To embed ChainPad in your web application, it is recommended that you use the contained node.js websocket server. You may examine test.html to see how to bind the editor to a simple textarea.

Building

To compile the code into chainpad.js run the following:

npm install
node make

This will run the tests and concatenate the js files into the resulting chainpad.js output file.

The API

var chainpad = ChainPad.create(config);

// The bindings are not included in the engine, see below.
bindToDataTransport(chainpad);
bindToUserInterface(chainpad);

chainpad.start();

Configuration Parameters

Config is an optional object parameter which may have one or more of the following contents. NOTE: it's critical that every ChainPad instance in the session has the same values for these parameters.

  • initialState (string) content to start off the pad with, default is empty-string.
  • checkpointInterval (number) the number of patches which should be allowed to go across the wire before sending a checkpoint. A small number will result in lots of sending of checkpoints which are necessarily large because they send the whole document in the message. A large number will result in more patches to download for a new person joining the pad.
  • avgSyncMilliseconds (number) the number of milliseconds to wait before sending to the server if there is anything to be sent. Making this number smaller will cause lots of patches to be sent (however the number will be limited by the RTT to the server because ChainPad will only keep one unacknowledged message on the wire at a time).
  • validateContent (function) if specified, this function will be called during each patch and receive the content of the document after the patch, if the document has semantic requirements then this function can validate them if they are broken then the patch will be rejected.
  • strictCheckpointValidation (boolean) if true then we will fail any checkpoint which comes at an interval which is not in agreement with checkpointInterval. Default: false.
  • patchTransformer (function) if specified, this function will be used for Operational Transformation. You have 3 options which are packaged with ChainPad or you can implement your own.
    • ChainPad.TextTransformer (this is default so you need not pass anything) if you're using ChainPad on plain text, you probably want to use this.
    • ChainPad.SmartJSONTransformer if you are using ChainPad to patch JSON data, you probably want this.
    • ChainPad.NaiveJSONTransformer this is effectively just TextTransformer with a validation step to make sure the result is JSON, using this is not recommended.
  • operationSimplify (function) This is an optional function which will override the function ChainPad.Operation.simplify in case you want to use a different one. Simplify is used for "fixing" operations which remove content and then put back the same content. The default simplify will not create patches containing strings with single characters from surrogate pairs.
  • logLevel (number) If this is zero, none of the normal logs will be printed.
  • userName (string) This is a string which will appear at the beginning of all logs in the console, if multiple ChainPad instances are running at the same time, this will help differentiate them.
  • noPrune (boolean) If this is true, history will not be pruned when a checkpoint is encountered. Caution: this can end up occupying a lot of memory!
  • diffFunction (function) This is a function which takes 2 strings and outputs and array of Operations. If unspecified, ChainPad will use the ChainPad.Diff which is a smart diff algorithm based on the one used by Fossel. The default diff function will not create patches containing strings with single characters from surrogate pairs.
  • diffBlockSize (number) This is an optional number which will inform the default diff function ChainPad.Diff how big the rolling window should be. Smaller numbers imply more resource usage but common areas within a pair of documents which are smaller than this number will not be seen. The default is 8.
  • transformFunction (function) This parameter has been removed, if you attempt to pass this argument ChainPad will fail to start and throw an error.

Binding the ChainPad Session to the Data Transport

To bind the session to a data transport such as a websocket, you'll need to use the message() and onMessage() methods of the ChainPad session object as follows:

  • message: Function which takes a String and signals the ChainPad engine of an incoming message.
  • onMessage: Function which takes a function taking a String, called by the ChainPad engine when a message is to be sent.
var socket = new WebSocket("ws://your.server:port/");
socket.onopen = function(evt) {
    socket.onmessage = function (evt) { chainpad.message(evt.data); };
    chainpad.onMessage(function (message, cb) {
        socket.send(message);
        // Really the callback should only be called after you are sure the server has the patch.
        cb();
    });
});

Binding the ChainPad Session to the User Interface

  • Register a function to handle changes to the document, a change comprises an offset, a number of characters to be removed and a number of characters to be inserted. This is the easiest way to interact with ChainPad.
var myContent = '';
chainpad.onChange(function (offset, toRemove, toInsert) {
    myContent = myContent.substring(0, offset) + toInsert + myContent.substring(offset + toRemove);
});
  • Signal to chainpad engine that the user has inserted and/or removed content with the change() function.
var chainpad = ChainPad.create();
chainpad.change(0, 0, "Hello world");
console.log(chainpad.getUserDoc()); // -> "Hello world"
chainpad.change(0, 5, "Goodbye cruel");
console.log(chainpad.getUserDoc()); // -> "Goodbye cruel world"
  • Register a function to handle a patch to the document, a patch is a series of insertions and deletions which may must be applied atomically. When applying, the operations in the patch must be applied in decending order, from highest index to zero. For more information about Patch, see chainpad.Patch.
chainpad.onPatch(function(patch) {});
  • Signal the chainpad engine that the user has inserted and/or removed content to/from the document. The Patch object can be constructed using Patch.create and Operations can be added to the patch using Operation.create and Patch.addOperation(). See ChainPad Internals for more information.
chainpad.patch(patch);

Block Object

A block object is an internal representation of a message sent on the wire, each block contains a Patch which itself contains one or more Operations. You can access Blocks using chainpad.getAuthBlock() or chainpad.getBlockForHash().

Fields/Functions

  • hashOf: Calculated SHA256 of the on-wire representation of this Block (as a Message).
  • lastMsgHash: SHA256 of previous/parent Block in the chain. If this is all zeros then this Block is the initial block.
  • isCheckpoint: True if this Block represents a checkpoint. A checkpoint always removes all of the content from the document and then adds it back, leaving the document as it was.
  • getParent() -> Block: Get the parent block of this block, this is fast because the blocks are already in the chain in memory.
  • getContent() -> string: Get the content of the Authoritative Document at the point in the history represented by this block. This takes time because it requires replaying part of the chain.
  • getPatch() -> Patch: Get a clone of the Patch which is contained in this block.
  • getInversePatch() -> Patch: Get a clone of the inverse Patch (the Patch which would undo the Patch provided by getPatch). This is calculated when the Message comes in to ChainPad.
  • equals(Block) -> Boolean: Find out if another Block is representing the same underlying structure, since Blocks are created whenever one is requested, using triple-equals is not ok.

Control Functions

chainpad.start()

Start the engine, this will cause the engine to setup a setInterval to sync back the changes reported. Before start() is called, you can still inform chainpad of changes from the network.

chainpad.abort()

Stop the engine, no more messages will be sent, even if there is Uncommitted Work.

chainpad.sync()

Flush the Uncommitted Work back to the server, there is no guarantee that the work is actually committed, just that it has attempted to send it to the server.

chainpad.getAuthDoc()

Access the Authoritative Document, this is the content which everybody has agreed upon and has been entered into the chain.

chainpad.getAuthBlock()

Access the blockchain block which is at the head of the cha

Related Skills

View on GitHub
GitHub Stars407
CategoryDevelopment
Updated1mo ago
Forks29

Languages

JavaScript

Security Score

100/100

Audited on Feb 20, 2026

No findings