Skip to main content

3 posts tagged with "Yjs"

Yjs real-time collaboration framework

View All Tags

Peer-to-Peer Chelys Tool Sharing

· 8 min read
Fares Abawi
TeXlyre author & maintainer
Part of the NGI0 Core roadmap

This post reports on Task 3 of TeXlyre's NGI0 Commons grant. For the full project roadmap, see TeXlyre joins NGI0 Commons.

Chelys tools can now be shared with collaborators over WebRTC. A service running on one user's machine can be exposed to another TeXlyre instance in the same collaborative project while the tool remains local to its host. TeXlyre can therefore interact with both local and remote providers through the same editor-side route, whereas Chelys handles the relay between a remote connection and the recipe's local endpoint, bridging WebRTC with WebSocket-connected recipes.

Background

In Task 2, we introduced support for connecting TeXlyre to local language servers over LSP through WebSocket endpoints. This is functional when TeXlyre and Chelys are running on the same machine or when the WebSocket endpoint is made public. However, the local endpoint cannot be accessed directly by other collaborators in the project when neither condition is fulfilled.

TeXlyre already uses Yjs over WebRTC for peer-to-peer project collaboration. External tools, however, follow a different communication pattern from document synchronization. Requests need to reach a specific provider, after which responses must return to the originating client. Additionally, larger messages may need to be split and reconstructed during transport. In this task, we therefore add a separate service for external tool transport. This involves the creation of a shared WebRTC and WebSocket wrapper for discovery, framing, and bidirectional communication specific to such tools, while the existing collaboration infrastructure for peer coordination remains unchanged.

The work spans two repositories. Each link below shows the full diff for that repository's contribution to this task.

  • chelys: the native WebRTC service bridge, recipe relay, and remote provider lifecycle.
  • texlyre: service framing, WebRTC and WebSocket transports, provider discovery, and project-level tool sharing.

Milestone 3a: Service serialization and transport

Local and shared external tool providers use the same service transport abstraction. Messages can contain text or binary data and can exceed the size suitable for a single transport message, as is also the case with Yjs updates. TeXlyre therefore splits larger payloads into bounded frames, transmits them in sequence, and reconstructs the original message at the receiving end. Integrations above this layer observe only the resulting message stream and remain agnostic to the transport.

The transport itself differs depending on where the provider is located. Local providers communicate over WebSocket, whereas remote providers use a WebRTC data channel. On the WebRTC side, the service channel uses SCTP over DTLS for ordered and reliable message delivery, while TeXlyre's service framing operates above this transport layer to handle application-level payload sizes and reconstruction, splitting data messages into 16 KiB chunks.

Service serialization and transport between a requesting TeXlyre instance and a shared provider host

Yjs and awareness carry only discovery and peer coordination, while framed tool payloads travel over a separate ordered WebRTC data channel and are reassembled at the receiving end.

Yjs and awareness states are used separately to announce available services and coordinate the peers involved in establishing a connection. Tool payloads, including files, commands, and responses, do not pass through the Yjs document synchronization stream. This keeps service communication independent from project synchronization while allowing both to use the same peer-to-peer collaboration infrastructure.

Milestone 3b: WebRTC relay for remote tool access

In Milestone 3a, we built the transport structure needed to reach a remote provider, while in Milestone 3b, we make an existing Chelys service (backend tool) available through that transport. The recipe continues to expose the same local WebSocket endpoint used when TeXlyre and Chelys run on the same machine. For remote access, however, Chelys exposes that service through WebRTC and relays messages between the remote connection and the recipe's local WebSocket endpoint.

Chelys relaying a WebRTC service connection to a recipe's local WebSocket endpoint

The remote instance connects over WebRTC to Chelys on the host machine, which forwards service messages to the recipe's unchanged localhost WebSocket endpoint.

The collaboration state announces which services, together with their providers, a host makes possible to share. A remote TeXlyre instance can then register a compatible shared provider alongside its local providers, while the corresponding tool remains attached only to Chelys on the host machine. Provider availability follows the host service: if the recipe is stopped or the host leaves the collaboration session, the shared provider is removed and its corresponding service connection is closed.

For example, Alice can run ltex-ls-plus through Chelys and expose it to a collaborative project. Bob's TeXlyre instance can then register and use that provider as if it were locally available, while Chelys transparently relays its requests to Alice's local ltex-ls-plus endpoint. If Alice keeps her Chelys instance running while closing TeXlyre, Bob can still connect to language server and typesetter services provided by Alice. Alice and Bob only need to be simultaneously present when the provider configuration is initially announced through the collaboration state.

WebRTC connection establishment still relies on the configured signaling infrastructure. However, this is only used to establish the peer connection and does not require the corresponding tool's WebSocket connection to be forwarded or made publicly available across the network in order to be reachable.

The same relay mechanism is also used by the external typesetters introduced in Task 5. Shared SILE or TeX Live providers can therefore transfer synchronized project files, compile requests, logs, and generated output through the same service connection without modifying the typesetter protocol.

Walkthrough: sharing a local typesetter with a collaborator

The following steps walk through exposing a Chelys typesetter to a collaborator, using the SILE recipe as the example provider. Alice hosts the typesetter and Bob compiles against it from another TeXlyre instance.

  1. On Alice's machine, install Chelys and pair it with her TeXlyre identity, following the walkthrough in the Task 2 report.

  2. In Chelys, open Browse recipes, add the SILE recipe, and install it as a Docker container (click the dropdown and choose ghcr.io... to download a pre-built Docker image instead for a quicker install).

    Chelys recipe browser with the SILE typesetter recipe added and installing in Docker mode

  3. Before starting the service, switch its transport from the local WebSocket endpoint to a WebRTC room. The transport is resolved when the service starts, so a running service has to be stopped and started again for a change to take effect.

    Chelys share options for the SILE recipe with the transport set to a WebRTC room instead of the local WebSocket endpoint

  4. Click Run. Chelys starts the SILE backend, joins the configured room, and relays the room's service messages to the recipe's local endpoint.

  5. In TeXlyre, Alice opens a SILE project (download example and upload it to TeXlyre SILE project), selects the shared services to offer in it, and sends the collaboration link to Bob.

    TeXlyre project sharing panel listing Alice's running Chelys services with per-project selection

  6. Bob opens the shared project. SILE appears in his compiler list next to the built-in browser compilers, marked as hosted by Alice. Clicking use in the approval toast allows the tools shared by Alice to be used for current and other projects where applicable.

    TeXlyre on Bob's instance showing the 'Use' toast to begin using Alice's SILE typestting compiler

  7. Bob compiles parshape.sil. The project files are synchronized to Alice's machine over the service channel, SILE runs there, and the PDF and log are returned to Bob's preview pane.

    TeXlyre on Bob's instance showing the PDF produced by Alice's SILE backend alongside the returned compiler log

  8. If Alice closes the TeXlyre window and keeps Chelys running, the provider remains available to Bob who can still compile using SILE.

  9. Shared tools can be reviewed and revoked from two places. Collab → Tools opens the per-project Shared Tools dialog, showing tools offered by collaborators with a Using/Ignore toggle and, under Shared by me, the option to share the tools used in the current project.

    TeXlyre Shared Tools dialog reached through the Collab menu, listing Alice's SILE typesetter and the per-project sharing option

    Settings → External Tools manages the same providers account-wide, where a shared entry such as SILE can be disabled (TeXlyre account retains recipe but becomes inactive), edited, removed, or offered to all collaborators regardless of project.

    TeXlyre External Tools settings showing the SILE typesetter shared by Alice alongside the account-wide sharing option

Acknowledgements

This work was funded by NLnet Foundation as part of the TeXlyre project.

Chelys Local LSP Bridge and Plugin System

· 8 min read
Fares Abawi
TeXlyre author & maintainer
Part of the NGI0 Core roadmap

This post reports on Task 2 and the first milestone of Task 4 of TeXlyre's NGI0 Commons grant. For the full project roadmap, see TeXlyre joins NGI0 Commons.

TeXlyre can now use language servers that run on your own machine. Chelys is a local companion application that installs and runs a language server as a background tool and relays its messages to TeXlyre over a local WebSocket connection. Pairing uses your existing TeXlyre identity through a WebAuthn/PRF passkey, which also lets TeXlyre exchange its local storage with other paired instances without Chelys running. A plugin system manages provider containers, so adding a language server means selecting a recipe instead of configuring software by hand. Recipes are available for Tinymist, ltex-ls-plus, harper-ls, and JabRef.

Background

A browser cannot launch and supervise local processes such as language servers or typesetting engines. TeXlyre already supported the Language Server Protocol on the editor side, but it had no way to install or run a server on the user's machine. Chelys provides that capability as a local companion application. It runs the plugins and exposes them over a local endpoint, while the account pairing is set up to keep the necessary state synchronized across the user's devices.

The work covers two repositories. Each link below shows the full diff for that repository's contribution to this task.

  • chelys: the companion application, covering passkey login, the WebRTC sync layer, and LSP plugin support.
  • texlyre: passkey login on the application side.

Milestone 2a: Local LSP relay over WebSocket

Chelys runs a language server as a local background process and bridges it to TeXlyre. The browser connects to a local WebSocket endpoint that Chelys exposes, and Language Server Protocol requests and responses are relayed in both directions between TeXlyre's editor and the local provider. Since the transport is a plain WebSocket address, the relay can also be used on its own, meaning a language server managed manually can be reached by pointing TeXlyre directly at its WebSocket address, following Using an LSP with TeXlyre, without Chelys.

TeXlyre LSP autocomplete

TeXlyre LSP info on hover

TeXlyre LSP info on hover

A request originating in the TeXlyre editor travels over the local WebSocket to Chelys, which forwards it to the language server process and returns the response along the same path.

Milestone 2b: Standalone passkey login and cross-instance local storage sync

TeXlyre already synchronized project data between instances over WebRTC, scoped by project links. Logging in with the Chelys passkey extends this to user-level data. The passkey's PRF output derives a room identifier, and two TeXlyre instances signed in with the same passkey exchange their local storage (settings, properties, records, and secrets) over that room. This runs in the browser over WebRTC and does not require Chelys. The Chelys app remains responsible for installing and running the plugins.

Signaling requires connectivity

The storage exchange relies on WebRTC for peer connections, which by default uses the signaling server at ywebrtc.texlyre.org. Paired instances therefore cannot exchange data while offline. To run fully offline, host a local y-webrtc signaling server (see texlyre-infrastructure) and set its endpoint in both TeXlyre and Chelys.

The room is derived from the user's username, password, and the WebAuthn/PRF passkey, and credentials are stored in the operating system's native keychain. Synchronization uses Yjs CRDTs over WebRTC with no central server holding the data, and a presence indicator shows which devices are connected.

Paired TeXlyre instances exchanging local storage

Once paired, instances share an encrypted account room and exchange local storage over WebRTC; the presence indicator reflects the devices currently connected.

Milestone 4a: Provider plugin system

Providers are packaged as recipes: declarative descriptions of a background tool and how to run it. The plugin system installs, updates, and removes provider containers and resolves their dependencies, so adding a language server does not require manual software setup. A recipe runs either as a native system process or inside a Docker container; Docker mode requires a Docker installation available on the system. Ready-made recipes are published at chelys-recipes, covering Tinymist for Typst, ltex-ls-plus and harper-ls for prose and grammar checking, and JabRef for BibTeX.

Bridging a TCP-only server: JabRef

Most recipes run a language server that communicates through LSP over standard input and output. JabRef's language server, However, jabls only transports over a TCP socket. The recipe runs jabls as a local TCP server and bridges it to a WebSocket that TeXlyre can reach, using socat to connect the TCP socket to standard streams and lsp-ws-proxy to expose those streams as a WebSocket:

TeXlyre ⇄ WebSocket ⇄ lsp-ws-proxy ⇄ stdio ⇄ socat ⇄ TCP ⇄ jabls -p 2087

jabls is the same component that supports JabRef's official VS Code extension, run standalone here and bridged to TeXlyre. It provides BibTeX and BibLaTeX integrity diagnostics for .bib files, reporting consistency, citation-key, and formatting problems as the file is edited. The diagnostics are read-only integrity checks and the server does not modify the .bib file.

The recipe exposes a single user-editable variable, wsPort (default 7021), the port the WebSocket proxy listens on. The internal jabls TCP port is fixed at 2087 inside the container and is not exposed. Changing wsPort updates the run command, the Docker port mapping, and the transport URL that Chelys injects into TeXlyre. The recipe supports three modes: Docker, which builds a self-contained image bundling Java 21, JBang, socat, and the proxy; System, which runs the same bridge from a host toolchain; and Connect, which attaches to a bridge already listening on wsPort. All modes transmit the LSP json recipe for that plugin to TeXlyre over WebRTC.

Walkthrough: from pairing to live diagnostics

The following steps walk through the install to live diagnostics from a local language server, using JabRef as the example provider.

  1. Download and install Chelys for your platform from the v0.1.0 release.

  2. In TeXlyre, register a passkey for Chelys at texlyre.org/texlyre and copy the resulting PRF key.

    Registering a Chelys passkey in TeXlyre

    Copying PRF in TeXlyre

    TeXlyre generates a WebAuthn/PRF passkey for Chelys; the PRF key shown here is copied into Chelys to derive the shared account room.

  3. In Chelys, log in with the same username and password and paste the copied PRF key.

    Logging in to Chelys with the TeXlyre credentials and PRF key

    Chelys pairs with the existing TeXlyre identity; credentials are stored in the system keychain.

  4. In Chelys, open Browse recipes and add one of the provided recipes.

    Browsing a recipe in Chelys

    The recipe browser lists available providers; adding one prepares it to be installed as either a system process or a Docker container.

  5. With the recipe added, click Install and choose Docker container.

    Installing the JabRef recipe as a Docker container

    Installing a recipe in Docker mode; the plugin system builds the image and resolves its dependencies.

  6. Once installation succeeds, click Run.

  7. Back in TeXlyre, refresh texlyre.org/texlyre and open a document matched to the running language server, for example a .bib document when running JabRef.

  8. Edit a BibTeX entry and introduce an integrity problem, such as a duplicate citation key or a malformed fields:

    @article{knuth1984,
    author = {Donald E. Knuth},
    title = {Literate Programming},
    journal = {The Computer Journal},
    year = {1984},
    volume = {27},
    number = {2},
    pages = {97--111}
    }

    @article{knuth1984,
    author = {Leslie Lamport},
    title = {LaTeX: A Document Preparation System},
    year = {1986}
    }

    @book{goossens1993
    author = {Michel Goossens and Frank Mittelbach and Alexander Samarin},
    title = {The LaTeX Companion},
    publisher = {Addison-Wesley},
    year = {1993},
    }

    @article{lamport1994,
    author = {Leslie Lamport}
    title = {LaTeX: A Document Preparation System},
    journal = {Addison-Wesley},
    year = {nineteen ninety-four},
    }

    @inproceedings{,
    author = {Jane Doe},
    title = {An Untitled Contribution},
    booktitle = {Proceedings of Nowhere},
    year = {2020}
    }

    @misc{oren2021,
    author = {Oren Patashnik},
    title = {BibTeXing},
    Year = {1988},
    url = {https://example.org/bibtex}
    }

    With jabls running, the issue is underlined as a diagnostic, and hovering or clicking it shows the reported problem.

    Live BibTeX diagnostics from jabls in the TeXlyre editor

    Diagnostics produced by the local jabls server, relayed through Chelys, appear inline in the TeXlyre editor.

Acknowledgements

This work was funded by NLnet Foundation as part of the TeXlyre project.

Introducing TeXlyre - Local-First Real-Time LaTeX Collaboration

· 7 min read
Fares Abawi
TeXlyre author & maintainer

TeXlyre is a local-first real-time LaTeX collaboration platform that enables researchers, academics, and teams to work together on scientific documents. Built with cutting-edge technologies including React, TypeScript, and Yjs, TeXlyre enables collaborative document editing with complete offline capabilities.