feat: Docker support, TOTP auth, image rendering, ion-sync fixes, favicon, open-in-new-window#9
Open
s39n wants to merge 43 commits into
Open
feat: Docker support, TOTP auth, image rendering, ion-sync fixes, favicon, open-in-new-window#9s39n wants to merge 43 commits into
s39n wants to merge 43 commits into
Conversation
- Dockerfile: multi-stage build (downloads obsidian + obsidian-mobile renderer bundles in stage 1, installs server deps in stage 2) - docker-compose.yml: base setup, vault persisted via volume - docker-compose.auth-key.yml: API-key auth overlay (AUTH_KEY env var) - docker-compose.auth-basic.yml: nginx HTTP Basic Auth overlay - nginx.conf: nginx reverse proxy with WebSocket support for /api/watch - .dockerignore: excludes vendor/, node_modules/, .git/, etc. - src/server/middleware/auth.js: Express middleware for API-key auth (login page, cookie session, Bearer token support) - src/server/index.js: load auth middleware when AUTH_KEY is set
Both auth options are now in one file — easier for Dockhand and other Docker Compose users to work with a single file: - Option 1 (API Key): uncomment AUTH_KEY in the env section, or set it in Dockhand's stack environment editor - Option 2 (Basic Auth): docker compose --profile auth-nginx up (requires nginx.htpasswd generated with htpasswd) Removes docker-compose.auth-key.yml and docker-compose.auth-basic.yml.
…error) Docker build environments have no internet access, so downloading Obsidian renderer files at build time (EAI_AGAIN / DNS failure) doesn't work. Instead: - entrypoint.sh checks for vendor/obsidian/app.js on startup and runs the download scripts only if missing (~30s on first start) - obsidian_vendor Docker volume caches the result — subsequent starts are instant - Dockerfile becomes a single stage with no network dependency at build time
The update scripts extract to /app/.tmp/ then rename() into /app/vendor/. rename() fails with EXDEV when src and dst are on different filesystems — which they are in Docker (.tmp = container layer, vendor = named volume). Fix: at startup, mkdir /app/vendor/.tmp and symlink /app/.tmp -> /app/vendor/.tmp so both paths live on the same volume filesystem.
docker-compose.yml: - commented examples for mounting your own vault folder and plugins dir - AUTH_KEY now loaded from .env file (gitignored) or Dockhand env editor - env_file with required:false so .env is optional auth: - .env.example shows how to configure AUTH_KEY locally - .gitignore: add .env so real keys are never committed mobile: - index.html: add env(safe-area-inset-top) padding on html element so content is not hidden under the status bar / notch on iOS and Android
Split into two variables: - APP_PORT (default 3000): host port for direct access to obsidian-web - PORT (default 3000): public port nginx listens on When running the auth-nginx profile, set APP_PORT=3001 so nginx can take port 3000 without both services fighting over the same host port.
docker-compose: AUTH_KEY was commented out in environment so it never reached the Node process even when set in Dockhand's env editor or .env. Now explicitly passed as AUTH_KEY: \ so an empty value is the default (auth disabled) and any non-empty value enables the login page. mobile: add env(safe-area-inset-bottom) so the bottom tab bar clears the home indicator on iOS/Android — previously only top was handled.
- Rewrite src/server/middleware/auth.js with otplib + qrcode - Login page: 6-digit code entry with auto-advance and auto-submit - Setup page at /__totp-setup?token=TOTP_SECRET shows QR code + raw secret - Session cookie derived from TOTP_SECRET (consistent across restarts) - ±30s clock drift tolerance (window: 1) - Replace AUTH_KEY with TOTP_SECRET in docker-compose.yml and .env.example - Update index.js log message and comments
…Buffer-safe token
… HTTP Add importKey, deriveKey (PBKDF2 proxied to server), encrypt, decrypt. - PBKDF2 with 100k iterations offloaded to POST /api/pbkdf2 (Node native crypto.pbkdf2) to avoid freezing the browser for ~10 s in pure JS. - AES-256 forward cipher and AES-GCM (GHASH + CTR) in pure JS, verified byte-for-byte against Node's native AES-256-GCM output. - digest (SHA-256) polyfill kept from prior commit. Fixes: crypto.subtle.importKey is not a function at deriveKey (ion-sync:43)
…p - Add Obsidian-style crystal SVG favicon to browser tab (index.html, starter.html)- Handle open-window / move-to-window / new-window IPC channels by opening the same vault in a new browser tab (electron.js shim)- Handle open-vault-manager IPC channels by opening /starter in a new tab - Fix sendSync('starter') server endpoint to return '/starter' so Obsidian's vault picker can navigate to the vault management page
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Repeat visits now hit a CacheStorage entry instead of waiting for the server to return /api/bootstrap. The SW fires a background revalidation in parallel, keeping the cache fresh for the NEXT visit. - src/client/sw.js: new SW; intercepts GET /api/bootstrap (not /status), serves from cache immediately then updates in background; skipWaiting + clients.claim() for immediate takeover on install. - src/server/index.js: add GET /sw.js route with Service-Worker-Allowed: / and Cache-Control: no-cache so the SW can control the full origin and always receives updates. - src/client/boot.js: register /sw.js at the top of the IIFE, before the bootstrap fetch, so the SW is in place for subsequent navigations.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds Docker/NAS deployment support and fixes several compatibility issues for self-hosted use, particularly around the ion-sync community plugin and plain-HTTP (non-HTTPS) deployments.
Docker & Deployment
Dockerfileanddocker-compose.ymlwith nginx reverse proxy optionUSER_DATAandVAULT_PATHenv vars for flexible vault mounting.tmpinto the vendor volume.env.exampleand deployment docs in READMEAuthentication
AUTH_KEYwith TOTP (time-based one-time password) authenticationTOTP_SECRETenv var; auth disabled when unset/__totp-setupQR code endpoint for easy authenticator app registrationCrypto / ion-sync (plain HTTP support)
Browsers restrict
crypto.subtleto HTTPS/localhost. These polyfills allow ion-sync to work on plain HTTP LAN deployments:crypto.subtle.digestcrypto.subtle.encrypt/decrypt/api/pbkdf2(Node native crypto — avoids ~10s browser freeze for 100k iterations)crypto.randomUUIDpolyfill viacrypto.getRandomValuessafeStorageshim — stores plugin secrets server-side via/api/keytar(Electron'ssafeStorageunavailable in browser)Image Rendering
vault.getResourcePathinboot.jsto return/api/fs/read?path=...HTTP URLs instead ofapp://local/...Electron URLs that browsers can't loadfile.pathdirectly (clean vault-relative path) rather than parsing the Electron URL, avoiding issues with Obsidian's?timestampcache-buster suffixapp://orfile://img src attributes that slip through/api/fs/readnow returns correct MIME types for images, video, audio, and PDFImage Paste (Clipboard)
pasteevents inelectron.jsand cache the image dataremote.clipboard.readImage(),availableFormats(),hasImage()so Obsidian's paste handler can write pasted images to the vaultIPC / Electron shim fixes
open-window/move-to-window/new-windowIPC channels by opening the vault in a new browser tab instead of crashing (V0Electron check)open-vault-manager/manage-vaultschannels by navigating to/startersendSync('starter')to return'/starter'so vault picker navigation workstrashendpoint: strip virtual/vault/prefix before resolving path (was causing ENOENT on ion-sync deletes)file-urlendpoint: return/api/fs/read?path=...instead ofapp://local/...shell.showItemInFolder— opens the file in a new tab via/api/fs/readContent-Type: application/octet-streamsobody-parserparses the bodyOther fixes
mkdirRepair): unlinks stale files blocking directory creationfavicon.ico,favicon.svg,apple-touch-icon.png)