Find a file
igor 1e1c6cdbaa
Some checks are pending
Build and push Joplock image / build-and-push (push) Waiting to run
Fix table row-insert scramble and round-trip stability
Bug: inserting a row on top of a 4-column table in rendered mode, then
switching notes and coming back, corrupted the table (header row
duplicated). Root cause: the turndown table rule always used all
<tbody> <tr> elements as body rows even when the first was already
used as the header row due to the absence of <thead>.

Fix: when <thead> is absent and <tbody> is present, skip the first
<tr> (used as headerRow) from bodyRows. Harden the fallback for
single-row tables.

Tests: 20 new unit tests (html-to-markdown conversion, full round-trip,
edge cases) and 2 new Playwright E2E tests covering the exact bug
scenario.
2026-07-30 15:39:59 +12:00
.github/workflows Publish multi-arch Docker images 2026-05-07 14:30:50 +12:00
app feat: notebook/folder sharing 2026-07-24 11:15:11 +12:00
cm-build add expander-based AI autocomplete triggers 2026-06-07 14:56:30 +12:00
hljs-build Add BASIC language syntax highlighting support 2026-05-18 12:17:14 +12:00
playwright-tests Fix table row-insert scramble and round-trip stability 2026-07-30 15:39:59 +12:00
public Fix table row-insert scramble and round-trip stability 2026-07-30 15:39:59 +12:00
scripts Add single-file HTML export, image/link inlining for exports, and visible table gridlines for PDF/DOCX 2026-07-15 14:34:11 +12:00
tests Fix table row-insert scramble and round-trip stability 2026-07-30 15:39:59 +12:00
vendor/turndown-lib fix mobile resume startup and editor targeting 2026-04-25 20:51:37 +12:00
.dockerignore fix mobile resume startup and editor targeting 2026-04-25 20:51:37 +12:00
.gitignore Add vault/notebook encryption: client-side AES-256-GCM E2EE with SVG lock icons, mobile vault support, startup resume gate, and re-entry guard 2026-04-28 22:25:46 +12:00
AGENT_GUIDE.md docs: update sharing model in AGENT_GUIDE, add share- prefix to teardown 2026-07-24 14:45:20 +12:00
AGENTS.md Document shell-mode cache + readonly and TinyMCE fast/debug onEdit split; add AGENTS.md pointer 2026-07-14 20:48:40 +12:00
docker-compose.example-full-build.yml feat(editor): dual-mode editor — CodeMirror 6 markdown + TinyMCE 8 rendered 2026-07-06 17:00:57 +12:00
docker-compose.example-full.yml feat(editor): dual-mode editor — CodeMirror 6 markdown + TinyMCE 8 rendered 2026-07-06 17:00:57 +12:00
docker-compose.yml feat(editor): dual-mode editor — CodeMirror 6 markdown + TinyMCE 8 rendered 2026-07-06 17:00:57 +12:00
Dockerfile Add single-file HTML export, image/link inlining for exports, and visible table gridlines for PDF/DOCX 2026-07-15 14:34:11 +12:00
package-lock.json feat(editor): dual-mode editor — CodeMirror 6 markdown + TinyMCE 8 rendered 2026-07-06 17:00:57 +12:00
package.json feat(editor): dual-mode editor — CodeMirror 6 markdown + TinyMCE 8 rendered 2026-07-06 17:00:57 +12:00
playwright.config.js chore: capture Playwright screenshots on every run 2026-07-20 15:58:53 +12:00
README.md docs: update AGENT_GUIDE and README with vault identity guard, restore, and feature list 2026-07-16 10:01:07 +12:00
server.js Make login cookie persistent to survive browser process eviction on suspend 2026-06-19 22:50:45 +12:00

Joplock

A secure, fast web client for Joplin Server.

Joplock runs as a sidecar alongside an unmodified Joplin Server instance, sharing the same Postgres database, sessions, notes, folders, and resources. It gives you a lightweight browser-based interface to your Joplin notes without modifying Joplin Server itself. Keep using the other Joplin clients too, this won't interfere.

Key Features

  • Full Joplin compatibility -- desktop, mobile, CLI, and Joplock all work with the same account and data simultaneously
  • Low Resource usage -- minimal memory usage on the client, fast and responsive
  • Security-first design -- no private data stored on the client; sessions are cleaned up on logout; per-user settings and admin controls for user management
  • Client-side encrypted vaults -- turn any notebook into a vault; notes inside are AES-GCM encrypted in the browser with a PBKDF2-derived key. The server never sees vault passwords. Ciphertext is bound to a specific vault and note id, and the server rejects any write that would land a vault-encrypted body in the wrong note or the wrong vault
  • Dual-mode editor -- CodeMirror 6 for markdown editing and TinyMCE 8 for rich rendered mode, switchable per note; mobile opens in a read-only rendered view with an edit toggle
  • Tables, code blocks, and lightbox -- full-screen code editor modal with language picker, TinyMCE tables with markdown round-trip, and a lightbox for images and attachments
  • Note export -- export any note as Markdown, single-file HTML (images and links inlined), DOCX (server-side via pandoc with reference styles), or PDF, with visible table gridlines in PDF/DOCX output
  • AI autocomplete -- optional prose completion via user-configured provider profiles (OpenRouter and others), triggered by Ctrl/Cmd-Space or configurable Expander triggers; per-user text expanders for snippets
  • Concurrent-edit detection -- detects when another client (or another browser tab) has updated the same note and offers Overwrite / Create copy so edits are never silently lost
  • Multiple themes -- Grey, Fruit, Dark Fruit, Earth, Swamp Thing, Fireball, and more, all as CSS custom-property sets; per-user theme picker in the status bar and settings page
  • User creation from Joplock UI -- create and modify users directly from Joplock settings page
  • Full database backup and restore -- create and restore complete Postgres backups for both Joplin and Joplock data
  • Multi-factor authentication -- optional TOTP-based MFA on top of standard Joplin sessions
  • Fast search -- searches titles and note bodies directly in Postgres; optional live-as-you-type search
  • Near-instant autosave -- debounced saves with conflict detection, hash-based deduplication, and an undo ring buffer with full note history snapshots. Restoring a snapshot updates the open editor immediately without a page refresh
  • PWA support -- installable as a home screen app on mobile and desktop with splash screens, offline indicator, and service worker shell
  • Server-side rendering -- SSR with htmx for minimal client-side JavaScript

Runtime Model

Joplock:

  • reads Joplin data directly from the shared Postgres database
  • validates the same sessionId cookie used by Joplin Server
  • writes notes, folders, and resources through stock Joplin Server APIs

That keeps desktop, mobile, CLI, and Joplock compatible with the same account and data.

Requirements

  • docker
  • an existng Joplin Server instance, or run the fullstack option

Environment

All configuration is done directly in the compose files via inline environment variables with comments. No .env file is needed -- just edit the values in docker-compose.yml or docker-compose.example-full.yml before starting.

Backup and recovery configuration:

  • JOPLOCK_BACKUP_DIR enables server-side full database backups
  • JOPLOCK_BACKUP_COMPRESSION controls the pg_dump compression method, for example zstd:19 or gzip:9
  • JOPLOCK_BACKUP_COMPRESSION_LEVEL=0-9 controls pg_dump compression for backup files
  • JOPLOCK_RECOVERY_ENABLED=true enables the break-glass recovery page at /recovery
  • JOPLOCK_RECOVERY_PASSWORD protects that recovery page

Important:

  • Backups are only durable if JOPLOCK_BACKUP_DIR is mounted to persistent storage.
  • Default backup compression is zstd:19, which is usually smaller than gzip:9.
  • JOPLOCK_BACKUP_COMPRESSION takes precedence over JOPLOCK_BACKUP_COMPRESSION_LEVEL.
  • Higher compression produces smaller backup files but may take longer to create.
  • Recovery mode is for backup and restore only, not regular note usage.
  • Restore replaces the entire shared Postgres database, including Joplock-owned tables.

Docker

Published container image:

  • ghcr.io/abort-retry-ignore/joplock:latest

Sidecar Install

Use this when you already have Joplin Server and Postgres running elsewhere. Edit the environment values in docker-compose.yml to point at your existing setup, or copy into your existing compose. Then:

docker compose up -d

This pulls the pre-built image from GitHub Container Registry. To build from source instead:

docker compose -f docker-compose-build.yml up -d --build

On Linux, the compose files map host.docker.internal to the host gateway so Joplock can reach host services by default.

Backup And Restore

Normal workflow:

  1. Sign in as the configured Joplock admin.
  2. Open Settings -> Admin -> Backup & Restore.
  3. Create a backup or restore an existing server-side backup.

Break-glass workflow when normal Joplin login is unavailable:

  1. Enable JOPLOCK_RECOVERY_ENABLED=true and set JOPLOCK_RECOVERY_PASSWORD.
  2. Open /recovery.
  3. Sign in with the recovery password.
  4. Create or restore full database backups from there.

Before restoring:

  1. Stop or quiesce Joplin Server if possible.
  2. Stop active sync clients.
  3. Expect the entire shared Postgres database to be replaced.

Full Example Stack

Use this as a reference/demo stack with Postgres, Joplin Server, and Joplock together. Edit the values in docker-compose.example-full.yml as needed, then:

docker compose -f docker-compose.example-full.yml up -d

The full example uses the public joplin/server:latest image. Joplock is exposed on http://localhost:5444 by default. Joplin Server is internal-only unless you add a port mapping.

The full example is meant as a working reference compose file. Adjust it for your real deployment.