Collaboration for Quoin, operated for you.

Real-time editing, presence and comments on a server you do not have to run, scale, back up or wake up for. The editor and the wire protocol are open. Operating it is the part worth paying for.

Free for 25 people a month and 100 documents. No card.

The bargain, stated plainly

Yours either way

The editor is MIT and on npm. Your documents are plain Yjs state. The wire is y-websocket's protocol, so a client you already have talks to this server, and this server is not the only one that can serve your documents.

What you are paying for

Sockets that survive a deploy. Documents kept across restarts and across instances. Presence that still works when your traffic lands on two machines instead of one. Usage you can read. Somebody for whom three in the morning is their problem rather than yours.

Nothing is held back

No feature of the hosted server is missing from the one you can run. The moment the dashboard is the only way to do something, the open half becomes a demo and this whole argument stops being true, so it does not happen.

The demo, not a video

Running on this service, right now, served from this application rather than a deployment of its own: the editor, presence, comments and the AI assistant. Open it in two tabs and type in both.

The demo, live

The same editor and the same server this page is selling. Open it in two tabs and watch them agree.

Open in a new tab

Three steps, and the third is nothing

  1. Your application decides who the reader is

    Your users never reach us to prove who they are, and we never learn anything about them. Your server mints a short-lived token naming the room, and, if you would rather count people than connections, an opaque id for the reader. The format is an HMAC over base64url JSON, which is this much code.

    import { createHmac } from 'node:crypto';
    
    const roomToken = (claims, secret) => {
      const body = Buffer.from(JSON.stringify(claims)).toString('base64url');
      return `${body}.${createHmac('sha256', secret).update(body).digest('base64url')}`;
    };
    
    // In your own route, once you already know who is asking.
    roomToken(
      {
        room: 'design-notes',
        exp: Math.floor(Date.now() / 1000) + 300,
        project: process.env.QUOIN_PROJECT_ID,
        subject: reader.id,
      },
      process.env.QUOIN_SIGNING_SECRET,
    );
  2. The browser opens a provider with that token

    Any y-websocket client will do, because the protocol is y-websocket's. There is nothing of ours to install in the browser unless you want the smaller purpose-built client.

    import * as Y from 'yjs';
    import { WebsocketProvider } from 'y-websocket';
    
    const doc = new Y.Doc();
    const token = await fetch('/api/room-token/design-notes').then((r) => r.text());
    
    const provider = new WebsocketProvider(
      'wss://quoincloud.com/api/collab',
      'design-notes',
      doc,
      { params: { token } },
    );
  3. There is no third step

    Hand the Yjs document to the editor's collaboration extension and you are finished. That part is documented on the editor's own site, which is where everything about the editor lives.

How you leave

Infrastructure that is hard to leave is infrastructure you have to keep, which is not a relationship worth having. Every document exports from the dashboard as the Yjs state a server would hold, and loads back into a plain Yjs document in one call. The wire is y-websocket's, so moving is a change of URL. Both of those are checked rather than asserted: an unmodified y-websocket client syncs through this server, carries presence through it and passes its token the same way, and an exported document opens in a bare Yjs document with nothing of ours involved.

Start on the free tier

25 people a month, 100 documents, 2 projects, no card. At a limit nothing is deleted and nothing already there stops working; what is refused is a document nobody has made yet.