Find a file
igor 72e2ff98ce docs: split the agent guide into a short AGENTS.md and on-demand topic files
AGENT_GUIDE.md was ~33k tokens, loaded in full by every agent that
followed AGENTS.md's 'read it first', whatever the task. A third of it
was lookups an agent can get from the code (file map, route table, env
var table, settings key list, static asset list) and had already gone
stale in several places.

- AGENTS.md (~1.8k tokens, always loaded): what the project is, a
  'before you touch X, read Y' table, the rules that always apply,
  workflow (including that a push to master publishes the Docker
  image), and the traps that have each broken real data or a real
  phone. It keeps the tool-managed graft block as is.
- docs/agents/*.md (11 files, read on demand): the hard-won 'why' and
  'do not regress' contracts by topic, sliced from the old guide with
  nothing hand-rewritten. The derivable lookups are dropped; the few
  non-derivable facts from them (git-ignored dev compose, built
  bundles, reference.docx) moved to testing-and-dev.md.
- pointer comments at the top of the contract-heavy source files, so
  the rule surfaces at the point of editing
- tests/agentDocs.test.js keeps the docs honest: AGENTS.md has a size
  budget, every topic file is linked and says when to read it, and any
  file, function or npm script a doc names must still exist
- README and two code comments no longer point at AGENT_GUIDE.md
2026-10-14 08:37:26 +13:00
.github/workflows fix(rendered): checkbox Enter always adds item; rendered-mode auto-title 2026-07-31 16:12:28 +12:00
app docs: split the agent guide into a short AGENTS.md and on-demand topic files 2026-10-14 08:37:26 +13:00
cm-build feat(editor): rich markdown mode (inline images, chips, checkboxes, tables, ...) 2026-10-06 17:11:10 +13:00
docs docs: split the agent guide into a short AGENTS.md and on-demand topic files 2026-10-14 08:37:26 +13:00
hljs-build Add BASIC language syntax highlighting support 2026-05-18 12:17:14 +12:00
integration-tests test(joplin): harness removes raw-uploaded and client-trashed items through the sync API 2026-10-09 15:16:32 +13:00
playwright-tests fix(mobile): phones in landscape keep the mobile layout and clear the notch 2026-10-13 16:02:56 +13:00
prism-build feat(editor): prism code highlighting, plaintext save identity guards, vault sync safety 2026-09-03 12:10:55 +12:00
public docs: split the agent guide into a short AGENTS.md and on-demand topic files 2026-10-14 08:37:26 +13:00
scripts fix(mobile): keep the header and toolbar on screen while the iOS keyboard is up 2026-10-13 14:18:42 +13:00
tests docs: split the agent guide into a short AGENTS.md and on-demand topic files 2026-10-14 08:37:26 +13: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 fix(mobile): keep the header and toolbar on screen while the iOS keyboard is up 2026-10-13 14:18:42 +13:00
.ignore feat(editor): markdown as default open mode; fix blank editor on vault unlock 2026-09-21 10:26:00 +12:00
AGENTS.md docs: split the agent guide into a short AGENTS.md and on-demand topic files 2026-10-14 08:37:26 +13: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): prism code highlighting, plaintext save identity guards, vault sync safety 2026-09-03 12:10:55 +12:00
Dockerfile fix(rendered): checkbox Enter always adds item; rendered-mode auto-title 2026-07-31 16:12:28 +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(joplin): real-client compatibility harness; fix shared-note unsharing, stale share ids and subtree revoke 2026-10-09 15:13:22 +13:00
playwright.config.js chore: capture Playwright screenshots on every run 2026-07-20 15:58:53 +12:00
README.md docs: split the agent guide into a short AGENTS.md and on-demand topic files 2026-10-14 08:37:26 +13:00
server.js fix(rendered): checkbox Enter always adds item; rendered-mode auto-title 2026-07-31 16:12:28 +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
  • Nested notebooks -- Joplin's notebook hierarchy as a tree on desktop and mobile: create sub-notebooks, move notebooks anywhere (the server refuses moves into their own descendants), and deleting a notebook promotes its sub-notebooks and notes instead of removing them
  • Notebook sharing -- share a notebook (with all of its sub-notebooks) by email with per-recipient read/write access; the sharer keeps ownership, and vault notebooks cannot be shared
  • 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 (the default) and TinyMCE 8 for rich rendered mode, switchable per note; on the mobile layout (phones and narrow windows) rendered mode opens read-only with a pencil toggle to edit
  • Rich markdown mode -- inline image previews with drag-to-resize, attachment and note-link chips with hover info, clickable task checkboxes, code-block Copy buttons, Tab-aligned pipe tables, paste-as-markdown (web pages, Word, Docs) and URL-over-selection links, heading folding, a word-count status bar with a heading outline (desktop layout only), and an optional live-preview mode that hides formatting marks off the line you are editing (each switchable under Settings → Markdown editor)
  • Tables, code blocks, and lightbox -- full-screen code editor modal with language picker, TinyMCE tables with markdown round-trip, a lightbox for images and attachments, and a per-image download button
  • Note export -- export any note as Markdown, single-file HTML (images and links inlined), DOCX (server-side via pandoc with reference styles), or PDF (pandoc + weasyprint), 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; a /ask <question> slash command for direct Q&A; 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 -- 21 themes (Grey, Fruit, Dark Fruit, Earth, Swamp Thing, Fireball, Dracula, Nord, 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, a mobile layout that follows the phone in both orientations (display-cutout safe), and compressed, cached static assets. Notes are never stored offline: with no connection you get an offline page, not a cached note
  • 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.

Vault Safety With Sync Clients

Vault enforcement for desktop, mobile, and CLI writes only runs when their sync traffic goes through Joplock's Joplin proxy. A direct public Joplin Server URL bypasses that guard. Do not expose a direct Joplin Server endpoint to clients that can access vault notebooks.

Use one public Joplock URL and proxy sync under a prefix, for example:

JOPLOCK_PUBLIC_BASE_URL=https://notes.example.com
JOPLIN_PUBLIC_BASE_PATH=/joplin
JOPLIN_SERVER_PUBLIC_URL=https://notes.example.com/joplin

Set Joplin Server's APP_BASE_URL to that same public /joplin URL, route clients to https://notes.example.com/joplin, and keep Joplin Server internal. Joplock then rejects sync writes that would move a vault note, replace it with plaintext, or put its ciphertext in a different note/vault.

Requirements

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

The published image already contains everything the app shells out to: postgresql18-client (backups), pandoc (DOCX/PDF export), and weasyprint (PDF export).

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, use the full-stack build variant (Postgres + Joplin Server + Joplock, Joplock built locally):

docker compose -f docker-compose.example-full-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.

Development

AGENTS.md holds the repo rules, and docs/agents/ the reasons and "do not regress" contracts by topic (it is written for coding agents, short and on-demand). docs/joplock-db-schema.html documents the four sidecar tables Joplock adds to the shared database.

For local work, keep a git-ignored docker-compose.dev.yml (Postgres + Joplin Server + Joplock from source) and rebuild the app container after source edits:

./scripts/rebuild-dev.sh

The Docker image copies app/, public/, and server.js at build time, so docker compose restart joplock does not pick up source changes.