Bridge service for self-hosted Livesync workflows in the m0sh1.cc tooling stack.
  • TypeScript 93.3%
  • JavaScript 3.5%
  • Shell 2%
  • Dockerfile 1.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Yael fd35d1e2fe
All checks were successful
Create gated fork release tag / validate (push) Successful in 36s
Create gated fork release tag / release (push) Successful in 3s
ci/woodpecker/tag/build Pipeline was successful
Merge pull request 'chore(deps): update pnpm to v12.8.1' (#243) from renovate/pnpm-12.x into main
Reviewed-on: #243
2026-09-30 19:55:44 +00:00
.ci ci: publish private LiveSync releases through Forgejo 2026-09-25 17:27:26 +02:00
.forgejo/workflows chore(deps): update actions/setup-node action to v7 2026-09-25 16:51:11 +00:00
.github ci: publish private LiveSync releases through Forgejo 2026-09-25 17:27:26 +02:00
.woodpecker chore(deps): update weekly container digest refresh (#244) 2026-09-30 19:55:40 +00:00
dat Update submodule and minor refactors 2026-02-26 09:31:43 +01:00
lib@72fbbc0ec3 fix: use encoded CouchDB document IDs in LiveSync 2026-09-25 19:07:48 +02:00
runtime Fix durable LiveSync replay handling 2026-07-23 14:26:23 +02:00
script Add processor. 2023-12-01 04:24:37 +00:00
scripts feat: migrate livesync bridge to node 26 2026-06-21 22:26:56 +02:00
stubs feat: migrate livesync bridge to node 26 2026-06-21 22:26:56 +02:00
test fix: use encoded CouchDB document IDs in LiveSync 2026-09-25 19:07:48 +02:00
types feat: migrate livesync bridge to node 26 2026-06-21 22:26:56 +02:00
.dockerignore feat: migrate livesync bridge to node 26 2026-06-21 22:26:56 +02:00
.gitignore Harden LiveSync replay and publication 2026-07-23 13:57:33 +02:00
.gitmodules chore: update dependencies and repo refs 2026-07-23 12:43:21 +02:00
.mise.toml Keep mise pnpm aligned with package manager upgrade 2026-09-30 21:54:46 +02:00
.prettierignore feat: migrate livesync bridge to node 26 2026-06-21 22:26:56 +02:00
docker-compose.yml Add a basic dockerfile and docker-compose.yml 2024-01-15 23:05:27 +01:00
Dockerfile chore(deps): update weekly container digest refresh (#244) 2026-09-30 19:55:40 +00:00
eslint.config.js feat: migrate livesync bridge to node 26 2026-06-21 22:26:56 +02:00
Hub.ts Fix durable LiveSync replay handling 2026-07-23 14:26:23 +02:00
main.ts Fix durable LiveSync replay handling 2026-07-23 14:26:23 +02:00
package.json Merge remote-tracking branch 'origin/main' into health/livesync-pnpm 2026-09-30 21:53:57 +02:00
Peer.ts Fix LiveSync retry cache semantics 2026-07-23 14:43:47 +02:00
PeerCouchDB.ts test: isolate CouchDB peer fixtures 2026-08-10 18:08:23 +02:00
PeerStorage.ts Fix LiveSync retry cache semantics 2026-07-23 14:43:47 +02:00
pnpm-lock.yaml Merge remote-tracking branch 'origin/main' into health/livesync-pnpm 2026-09-30 21:53:57 +02:00
pnpm-workspace.yaml chore(deps): update dependency uuid to v14.0.2 2026-08-20 18:37:42 +00:00
readme.md fix: align mise with the package pnpm version 2026-09-25 17:29:10 +02:00
tsconfig.json chore(deps): upgrade prerelease toolchain 2026-06-22 01:12:47 +02:00
types.ts fix: harden livesync bridge runtime 2026-05-15 17:32:05 +02:00
util.ts feat: add Woodpecker CI pipeline with Deno lint/fmt/check 2026-02-22 19:03:00 +01:00
vitest.config.ts feat: migrate livesync bridge to node 26 2026-06-21 22:26:56 +02:00

LiveSync Bridge

m0sh1.cc fork

This Forgejo-first fork is maintained for the m0sh1.cc self-hosted LiveSync tooling stack. The canonical repository is https://git.m0sh1.cc/isityael/livesync-bridge; GitHub is maintained as a public push mirror at https://github.com/isityael/livesync-bridge.

screenshot

What is this?

This is a custom replicator between Self-hosted LiveSync remote vaults and storage. The Unified Version of filesystem-livesync and livesync-classroom.

A Vault or storage can be synchronised with vaults or storage. You can even combine them. Of course, different passphrases for each vault could be used. And, you can synchronize documents under the specified folder on the vault, to another vault's specified one.

Of course, it is multi-directional!

How to use

Prerequisites

  • Node.js 26 and PNPM 12.6.0 via Corepack are required.

Simply run

  1. Clone the GitHub Repository
git clone --recursive https://github.com/isityael/livesync-bridge
  1. Open the config file dat/config.sample.json, edit and save to dat/config.json. (You do not have to worry, the sample is in the following section).
  2. Simply run like this.
$ corepack enable
$ pnpm install --frozen-lockfile
$ pnpm run build
$ pnpm start

Note: If you want to scan all storage and databases from the beginning, please run with --reset.

Docker Instructions

  1. Clone the GitHub Repository
git clone --recursive https://github.com/isityael/livesync-bridge
  1. Open the config file dat/config.sample.json, edit and save to dat/config.json. (The storage folder has to start with "data/" to be in the volume)

  2. Simply run like this.

docker compose up -d

New container releases are published privately to git.m0sh1.cc/m0sh1-internal/livesync-bridge by Woodpecker. Tag builds validate once, build a candidate with provenance and an SBOM, block HIGH/CRITICAL vulnerabilities with fixes available, and sign the candidate before promoting release, SHA and latest tags. The GitHub mirror does not publish images. Existing GHCR images are retained for recovery; they are not refreshed.

The publisher requires Woodpecker secrets forgejo_package_username and forgejo_package_token with package write access to the private m0sh1-internal organization. Existing DHI and Cosign secrets remain required. Consumers must authenticate to Forgejo with package read access.

Health and recovery

GET /healthz listens on port 8080 by default. Set LSB_HEALTH_PORT to change the port. Kubernetes startup, readiness, and liveness probes may use the same path: only healthy returns HTTP 200; startup, stale, and unhealthy return HTTP 503. The response includes phase, checkpoint and remote-activity times, conflict count, and replay state, but never credentials or note content.

Persist LSB_STATE_DIR across restarts. Malformed state is quarantined and forces a full remote replay before local tombstones can be sent. Replay and watch callbacks are processed durably in sequence; a failed destination write does not advance the checkpoint. Each CouchDB destination has its own confirmed baseline and per-checkpoint tombstone budget. Additional controls are:

  • LSB_MAX_CONSECUTIVE_FAILURES (default 3)
  • LSB_RETRY_DELAY_MS (default 10000)
  • LSB_STALE_AFTER_MS (default 300000)
  • LSB_MAX_TOMBSTONES_PER_CHECKPOINT (default 10)

Configuration

The configuration file consists of the following structure.

{
  "peers": [
    {
      "type": "couchdb", // Type should be `couchdb or storage`
      "name": "test1", // Should be unique
      "group": "main", // we can omit this.
      "database": "test",
      "username": "admin",
      "password": "password",
      "url": "http://localhost:5984",
      "customChunkSize": 100,
      "minimumChunkSize": 20,
      "passphrase": "passphrase", // E2EE passphrase, if you do not enabled, leave it blank.
      "obfuscatePassphrase": "passphrase", // Path obfuscation passphrase, if you do not enabled, leave it blank. if enabled, set the same value of passphrase.
      "baseDir": "blog/", // Sharing folder
      "useRemoteTweaks": true, // Overwrite customChunkSize or minimumChunkSize, and check configuration matches
    },
    {
      "type": "couchdb",
      "name": "test2", // We can even synchronise the same databases as long as they have different names in here.
      "group": "main", // we can omit this.
      "database": "test2",
      "username": "admin",
      "passphrase": "passphrase",
      "password": "password",
      "url": "http://localhost:5984",
      "customChunkSize": 100,
      "minimumChunkSize": 20,
      "obfuscatePassphrase": "passphrase",
      "baseDir": "xxxx/",
    },
    {
      "type": "storage",
      "name": "storage-test1",
      "group": "main", // we can omit this.
      "baseDir": "./vault/", // The folder which have been synchronised.
      "processor": {
        // The processor configuration. You can omit this.
        "cmd": "script/test.sh", // The programme which run at file modification or deletion.
        "args": ["$filename", "$mode"],
        // The modified file is set to $filename. The mode is set to `deleted` or `modified`.
        // $filename and $mode have been set also in environment variables.
      },
      "scanOfflineChanges": true,
      "useChokidar": true, // Node runtime uses chokidar for filesystem watching.
    },
  ],
}

Realistic example

name database_uri / path CouchDB username CouchDB password vault E2EE passphrase baseDir
private vault of Cornbread http://localhost:5984/classroom_cornbread cornbread tackle glucose shared/
shared vault http://localhost:5984/classroom_shared common_user resu_nommoc cocoa
private vault of Vanilla http://localhost:5984/classroom_vanilla vanilla liberty smock kyouyuu/
storage ./vault/

Cornbread's every document under "shared" is synchronized with the top of the shared vault:

Cornbread shared
document1 Not transferred
document2 Not transferred
shared/shared_doc1 shared_doc1
shared/sub/sub_doc sub/sub_doc

Vanilla's every document under "kyouyuu" is synchronized with the top of the shared vault:

Vanilla shared
documentA Not transferred
documentB Not transferred
kyouyuu/some_doc some_doc
kyouyuu/sub/some_sub_doc sub/some_sub_doc

Totally, all files are synchronized like this:

Cornbread shared Vanilla
document1 Not transferred
document2 Not transferred
Not transferred documentA
Not transferred documentB
shared/shared_doc1 shared_doc1 kyouyuu/shared_doc1
shared/some_doc some_doc kyouyuu/some_doc
shared/sub/some_sub_doc sub/some_sub_doc kyouyuu/sub/some_sub_doc
shared/sub/sub_doc sub/sub_doc kyouyuu/sub/sub_doc

... with the configuration below:

{
  "peers": [
    {
      "type": "couchdb", // Type should be `couchdb or storage`
      "name": "cornbread", // Should be unique
      "url": "http://localhost:5984",
      "database": "classroom_cornbread",
      "username": "cornbread",
      "password": "tackle",
      "passphrase": "glucose", // E2EE passphrase, if you do not enabled, leave it blank.
      "obfuscatePassphrase": "glucose", // Path obfuscation passphrase, if you do not enabled, leave it blank. if enabled, set the same value of passphrase.
      "customChunkSize": 100,
      "minimumChunkSize": 20,
      "baseDir": "shared/", // Sharing folder
    },
    {
      "type": "couchdb", // Type should be `couchdb or storage`
      "name": "shared", // Should be unique
      "url": "http://localhost:5984",
      "database": "classroom_shared",
      "username": "common_user",
      "password": "resu_nommoc",
      "passphrase": "cocoa", // E2EE passphrase, if you do not enabled, leave it blank.
      "obfuscatePassphrase": "cocoa", // Path obfuscation passphrase, if you do not enabled, leave it blank. if enabled, set the same value of passphrase.
      "customChunkSize": 100,
      "minimumChunkSize": 20,
      "baseDir": "", // Sharing folder
    },
    {
      "type": "couchdb",
      "name": "vanilla", // We can even synchronise the same databases as long as they have different names in here.
      "url": "http://localhost:5984",
      "database": "classroom_vanilla",
      "username": "vanilla",
      "password": "liberty",
      "passphrase": "smock",
      "obfuscatePassphrase": "smock",
      "customChunkSize": 100,
      "minimumChunkSize": 20,
      "baseDir": "kyouyuu/",
    },
    {
      "type": "storage",
      "name": "storage-test1",
      "baseDir": "./vault/", // The folder which have been synchronised.
    },
  ],
}