Skip to content

fix(obsidian): enable obsidian-git auto-backup on headless server - #1463

Merged
shunkakinoki merged 1 commit into
mainfrom
fix/obsidian-git-headless-backup
Apr 13, 2026
Merged

fix(obsidian): enable obsidian-git auto-backup on headless server#1463
shunkakinoki merged 1 commit into
mainfrom
fix/obsidian-git-headless-backup

Conversation

@shunkakinoki

@shunkakinoki shunkakinoki commented Apr 12, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fix obsidian-git plugin not auto-backing up on headless kyber server
  • Add CDP-based systemd timer to trigger the plugin's backup every 3 minutes
  • Fix Chrome 140 Fontations font indexer crash with --disable-features=FontationsFontIndexer
  • Install fonts (dejavu_fonts, fontconfig) for headless Electron rendering

Problem

Three issues prevented obsidian-git from working headlessly:

  1. Chrome 140 Fontations crash -- NOTREACHED in remote_font_face_source.cc:357 when zero system fonts are installed (chromium#442747781)
  2. Electron event loop blocked -- renderer thread stuck in futex_wait_queue under xvfb, preventing setTimeout callbacks from firing
  3. Plugin config -- customMessageOnAutoBackup: true opens an interactive modal that hangs headlessly (fixed in wiki repo)

Solution

CDP (Chrome DevTools Protocol) messages use IPC which bypasses the stuck event loop. A systemd timer sends a CDP eval to trigger doAutoCommitAndSync() every 3 minutes, with websocket --ping-interval keepalives that pump the event loop for 15s while git operations complete.

All git operations still run through the obsidian-git plugin -- the timer just triggers what setTimeout can't.

Test plan

  • make shell-lint passes
  • make format -- no changes
  • shellspec -- 1247 examples, 0 failures
  • E2E: service starts, trigger fires, vault backup committed and pushed

Summary by cubic

Enable obsidian-git auto-backups on the headless kyber server by triggering doAutoCommitAndSync() via CDP on a systemd timer. Also fix the Chrome 140 Fontations crash and add fonts for stable headless Electron rendering.

  • New Features

    • Add obsidian-git-trigger.sh and a user systemd service + timer (every 3 min) to invoke the plugin via CDP on --remote-debugging-port=9222.
    • Keep the CDP websocket alive with --ping-interval for 15s so git operations complete; all work still runs inside the plugin.
  • Bug Fixes

    • Work around blocked renderer timers under xvfb by using CDP IPC instead of setTimeout.
    • Disable Fontations font indexer and install dejavu_fonts + fontconfig to prevent font-related crashes.

Written for commit 8d1499f. Summary will update on new commits.

The Electron renderer's setTimeout doesn't fire under headless xvfb
(futex_wait_queue blocks the event loop pump), so the obsidian-git
plugin's periodic backup timer never executes.

Fix by adding a systemd timer that triggers the plugin's
doAutoCommitAndSync() via Chrome DevTools Protocol every 3 minutes.
CDP messages use IPC which bypasses the stuck event loop. Websocket
ping-interval keeps the connection alive for 15s to pump the loop
while git operations complete.

Also fixes Chrome 140 Fontations font indexer crash (NOTREACHED in
remote_font_face_source.cc:357) by adding --disable-features=FontationsFontIndexer
and installing dejavu_fonts + fontconfig on the headless server.

Changes:
- Add --remote-debugging-port=9222 and --disable-features=FontationsFontIndexer
  to obsidian-headless.sh
- Add obsidian-git-trigger.sh CDP trigger script
- Add obsidian-git-trigger systemd timer (3 min interval)
- Add dejavu_fonts and fontconfig packages for headless rendering
- Add spec and coverage for new trigger script
@mesa-dot-dev

mesa-dot-dev Bot commented Apr 12, 2026

Copy link
Copy Markdown

You do not have enough credits to review this pull request. Please purchase more credits to continue.

@coderabbitai

coderabbitai Bot commented Apr 12, 2026

Copy link
Copy Markdown
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added automatic periodic backup service for Obsidian vault that triggers every 3 minutes after boot startup.
    • Integrated obsidian-git plugin's commit-and-sync workflow into automated scheduling.
  • Tests

    • Added test coverage for the new backup trigger functionality and syntax validation.

Walkthrough

A new automated trigger mechanism for Obsidian's git plugin is introduced using Chrome DevTools Protocol. A systemd timer and service are added to periodically invoke git auto-sync commits. The headless Obsidian wrapper is updated to expose port 9222 for CDP connections, and test coverage validates the new trigger script.

Changes

Cohort / File(s) Summary
Obsidian Service Enhancement
home-manager/services/obsidian/default.nix, home-manager/services/obsidian/obsidian-git-trigger.sh, home-manager/services/obsidian/obsidian-headless.sh
Added systemd user timer and oneshot service to periodically trigger obsidian-git plugin commits via Chrome DevTools Protocol; enabled remote debugging on port 9222 in headless Obsidian wrapper; implements CDP WebSocket-based script to call plugin's auto-sync method.
Test Specifications
spec/coverage_spec.sh, spec/obsidian_git_trigger_spec.sh
Extended test coverage with new shell script validation spec for obsidian-git-trigger, including shebang verification, Bash syntax checks after placeholder substitution, and CDP integration assertions.

Sequence Diagram

sequenceDiagram
    participant Timer as systemd Timer
    participant Service as obsidian-git-trigger.service
    participant Script as obsidian-git-trigger.sh
    participant CDP as Chrome DevTools<br/>(localhost:9222)
    participant Obsidian as Obsidian App
    
    Timer->>Service: Trigger (OnBootSec + OnUnitActiveSec)
    Note over Timer,Service: After obsidian.service
    Service->>Script: Execute obsidian-git-trigger
    Script->>CDP: GET /json (discover targets)
    CDP-->>Script: Return webSocketDebuggerUrl
    Script->>CDP: WebSocket Connect
    Script->>CDP: Send Runtime.evaluate request
    Note over Script,CDP: Call doAutoCommitAndSync()
    Script->>Obsidian: Execute plugin method via CDP
    Obsidian->>Obsidian: Commit & Sync via obsidian-git
    CDP-->>Script: WebSocket response
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly Related PRs

Suggested Labels

enhancement

Poem

🐰 A timer hops, a script takes flight,
Through DevTools Protocol in the night,
CDP whispers: "sync thy vault with glee!"
Obsidian commits, a git history spree. 🌙✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main change: enabling obsidian-git auto-backup on a headless server via CDP-based trigger.
Description check ✅ Passed The PR description comprehensively relates to the changeset, explaining the problems, solutions, and implementation details of the obsidian-git auto-backup feature.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/obsidian-git-headless-backup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@mesa-dot-dev

mesa-dot-dev Bot commented Apr 12, 2026

Copy link
Copy Markdown

Mesa Description

TL;DR

Fixes obsidian-git plugin's auto-backup on headless Kyber server by adding a CDP-based systemd timer, and addresses Chrome 140 Fontations crash and Electron event loop blocking issues.

What changed?

  • Implemented a CDP-based systemd timer to trigger obsidian-git's doAutoCommitAndSync() function every 3 minutes, bypassing Electron event loop issues.
  • Added --ping-interval to websocket for keepalives to pump the event loop during git operations.
  • Fixed Chrome 140 Fontations font indexer crash by disabling the feature (--disable-features=FontationsFontIndexer).
  • Installed necessary fonts (dejavu_fonts, fontconfig) for headless Electron rendering.

Description generated by Mesa. Update settings

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@home-manager/services/obsidian/obsidian-git-trigger.sh`:
- Around line 18-21: The script masks WebSocket/CDP failures by appending "||
true" to the websocat pipeline; remove the "|| true" so failures propagate to
systemd (or replace it with explicit error handling/logging), i.e. ensure the
websocat invocation line that pipes the JSON payload to `@websocat`@/bin/websocat
--ping-interval 1 "$WS_URL" returns its exit code instead of being ignored so
systemd sees and records delivery failures of the CDP trigger.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: af863916-7f5b-4375-8905-5ec9c056c072

📥 Commits

Reviewing files that changed from the base of the PR and between b594522 and 8d1499f.

📒 Files selected for processing (5)
  • home-manager/services/obsidian/default.nix
  • home-manager/services/obsidian/obsidian-git-trigger.sh
  • home-manager/services/obsidian/obsidian-headless.sh
  • spec/coverage_spec.sh
  • spec/obsidian_git_trigger_spec.sh

Comment on lines +18 to +21
{
echo '{"id":1,"method":"Runtime.evaluate","params":{"expression":"app.plugins.plugins['"'"'obsidian-git'"'"']?.automaticsManager?.doAutoCommitAndSync()"}}'
sleep 15
} | @websocat@/bin/websocat --ping-interval 1 "$WS_URL" >/dev/null 2>&1 || true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Do not mask CDP/websocket failures.

|| true hides trigger failures and reports success to systemd even when the backup call was never delivered.

Suggested patch
 {
   echo '{"id":1,"method":"Runtime.evaluate","params":{"expression":"app.plugins.plugins['"'"'obsidian-git'"'"']?.automaticsManager?.doAutoCommitAndSync()"}}'
   sleep 15
-} | `@websocat`@/bin/websocat --ping-interval 1 "$WS_URL" >/dev/null 2>&1 || true
+} | `@websocat`@/bin/websocat --ping-interval 1 "$WS_URL" >/dev/null 2>&1
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
echo '{"id":1,"method":"Runtime.evaluate","params":{"expression":"app.plugins.plugins['"'"'obsidian-git'"'"']?.automaticsManager?.doAutoCommitAndSync()"}}'
sleep 15
} | @websocat@/bin/websocat --ping-interval 1 "$WS_URL" >/dev/null 2>&1 || true
{
echo '{"id":1,"method":"Runtime.evaluate","params":{"expression":"app.plugins.plugins['"'"'obsidian-git'"'"']?.automaticsManager?.doAutoCommitAndSync()"}}'
sleep 15
} | `@websocat`@/bin/websocat --ping-interval 1 "$WS_URL" >/dev/null 2>&1
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@home-manager/services/obsidian/obsidian-git-trigger.sh` around lines 18 - 21,
The script masks WebSocket/CDP failures by appending "|| true" to the websocat
pipeline; remove the "|| true" so failures propagate to systemd (or replace it
with explicit error handling/logging), i.e. ensure the websocat invocation line
that pipes the JSON payload to `@websocat`@/bin/websocat --ping-interval 1
"$WS_URL" returns its exit code instead of being ignored so systemd sees and
records delivery failures of the CDP trigger.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an automated mechanism to trigger the obsidian-git plugin's auto-backup feature in headless environments. It adds a new bash script, obsidian-git-trigger.sh, which communicates with Obsidian via the Chrome DevTools Protocol (CDP) to bypass event loop limitations under xvfb. The script is scheduled to run every three minutes via a new systemd user timer. Feedback was provided to improve the robustness of the script's JSON parsing and error handling to ensure cleaner systemd logs when the debugging port is temporarily unreachable.


CDP="http://localhost:9222"

WS_URL=$(@curl@/bin/curl -sf "$CDP/json" | @jq@/bin/jq -r '.[0].webSocketDebuggerUrl // empty')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current jq filter will produce a parse error on stderr if curl returns an empty array [] or if the input is empty (e.g., when the debugging port is not yet reachable). Using the optional chaining operator ? and suppressing stderr for these discovery commands will make the script more robust and keep the systemd logs cleaner during service startup or downtime.

Suggested change
WS_URL=$(@curl@/bin/curl -sf "$CDP/json" | @jq@/bin/jq -r '.[0].webSocketDebuggerUrl // empty')
WS_URL=$(@curl@/bin/curl -sf "$CDP/json" 2>/dev/null | @jq@/bin/jq -r '.[0].webSocketDebuggerUrl? // empty' 2>/dev/null)

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 5 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="home-manager/services/obsidian/obsidian-git-trigger.sh">

<violation number="1" location="home-manager/services/obsidian/obsidian-git-trigger.sh:21">
P2: `|| true` masks websocat/CDP failures, so systemd always reports success even when the backup trigger wasn't delivered. Since this is a `Type=oneshot` service for backups, failures should propagate so they're visible in `systemctl status` and journal logs. The timer will retry on the next tick regardless.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

{
echo '{"id":1,"method":"Runtime.evaluate","params":{"expression":"app.plugins.plugins['"'"'obsidian-git'"'"']?.automaticsManager?.doAutoCommitAndSync()"}}'
sleep 15
} | @websocat@/bin/websocat --ping-interval 1 "$WS_URL" >/dev/null 2>&1 || true

@cubic-dev-ai cubic-dev-ai Bot Apr 12, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: || true masks websocat/CDP failures, so systemd always reports success even when the backup trigger wasn't delivered. Since this is a Type=oneshot service for backups, failures should propagate so they're visible in systemctl status and journal logs. The timer will retry on the next tick regardless.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At home-manager/services/obsidian/obsidian-git-trigger.sh, line 21:

<comment>`|| true` masks websocat/CDP failures, so systemd always reports success even when the backup trigger wasn't delivered. Since this is a `Type=oneshot` service for backups, failures should propagate so they're visible in `systemctl status` and journal logs. The timer will retry on the next tick regardless.</comment>

<file context>
@@ -0,0 +1,21 @@
+{
+  echo '{"id":1,"method":"Runtime.evaluate","params":{"expression":"app.plugins.plugins['"'"'obsidian-git'"'"']?.automaticsManager?.doAutoCommitAndSync()"}}'
+  sleep 15
+} | @websocat@/bin/websocat --ping-interval 1 "$WS_URL" >/dev/null 2>&1 || true
</file context>
Fix with Cubic

@shunkakinoki
shunkakinoki merged commit 798b7b9 into main Apr 13, 2026
38 of 40 checks passed
@shunkakinoki
shunkakinoki deleted the fix/obsidian-git-headless-backup branch April 13, 2026 02:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant