OtterWiki Bidirectional Sync Design¶
Status: Future Enhancement (Roadmap) Priority: Low Date: 2025-12-10
Current Workflow: Edit docs in
tower-fleet/docs/, push to GitHub, changes sync to OtterWiki automatically. This works well for now.
Problem¶
Currently, OtterWiki is read-only from a user perspective: - git-sync pulls from tower-fleet-docs → OtterWiki - Edits made in OtterWiki UI are overwritten on next sync (60 seconds)
We want bidirectional sync so users can edit in the wiki UI.
Current Architecture¶
tower-fleet/docs → (GitHub Action) → tower-fleet-docs → (git-sync) → OtterWiki
│
OVERWRITES
wiki edits
Proposed Architecture¶
┌────────────────────────────────────────┐
│ GitHub │
│ │
│ tower-fleet/docs ◀──── GitHub Action │
│ │ │ │
│ │ tower-fleet-docs │
│ │ │
└─────────┼───────────────────────────────┘
│
push │ pull
│
┌─────────▼───────────────────────────────┐
│ OtterWiki Pod │
│ │
│ ┌──────────┐ ┌─────────────────┐ │
│ │OtterWiki │────▶│ /app-data/ │ │
│ │ (UI) │ │ (git repo) │ │
│ └──────────┘ │ │ │
│ │ remote: origin │ │
│ │ → tower-fleet │ │
│ └────────┬────────┘ │
│ │ │
│ post-receive hook │
│ (auto push) │
└────────────────────────────────────────┘
Implementation Options¶
Option 1: Native Git Remote (Recommended)¶
Configure OtterWiki's internal git repo with a remote and hooks.
Steps:
-
Remove git-sync sidecar (no longer needed)
-
Initialize remote in OtterWiki's repo:
-
Add post-commit hook to push on every save:
-
Add cron/timer for pull (catch remote changes):
-
Mount SSH key for GitHub auth
Tradeoffs: | Pros | Cons | |------|------| | Simple, native git | Conflicts need manual resolution | | Real-time push on save | Need SSH key in pod | | No extra sidecars | Pull is polling-based |
Option 2: Bidirectional Sync Sidecar¶
Keep git-sync for pulling, add push sidecar.
Architecture:
containers:
- name: otterwiki
# ... wiki container
- name: git-sync
# Pulls from GitHub every 60s
- name: git-push
# Watches for new commits, pushes to GitHub
image: alpine/git
command: ["/scripts/push-watcher.sh"]
push-watcher.sh:
#!/bin/sh
cd /app-data
LAST_COMMIT=""
while true; do
CURRENT=$(git rev-parse HEAD 2>/dev/null)
if [ "$CURRENT" != "$LAST_COMMIT" ] && [ -n "$LAST_COMMIT" ]; then
echo "New commit detected, pushing..."
git push origin main
fi
LAST_COMMIT="$CURRENT"
sleep 5
done
Tradeoffs: | Pros | Cons | |------|------| | Separation of concerns | Two sidecars = more complexity | | Can add custom logic | Potential race conditions | | Easy to disable one direction | More resource usage |
Option 3: GitHub Webhook + Pull¶
Real-time bidirectional like Wiki.js 3.
Architecture:
Requirements:
- Expose webhook endpoint (e.g., /api/git-webhook)
- Configure GitHub webhook to POST on push
- OtterWiki or sidecar handles webhook
Tradeoffs: | Pros | Cons | |------|------| | Instant sync both ways | Need webhook endpoint exposed | | Most responsive | More complex setup | | Like Wiki.js 3 | OtterWiki may not support webhooks |
Conflict Resolution¶
All options face the same conflict challenge:
Scenario: You edit page A in OtterWiki. Someone else pushes changes to page A in tower-fleet.
Resolution strategies:
- Last write wins - Whoever pushes last overwrites
-
Simple but can lose changes
-
Git merge - Let git handle it
- Works for different files
-
Same file = merge conflict (needs manual resolution)
-
Wiki-first - Wiki changes always win
- Never lose wiki edits
-
May overwrite repo changes
-
Repo-first - Repo changes always win
- Current behavior (git-sync)
- Wiki edits get overwritten
Recommendation: Git merge with notification on conflict.
Path Mapping¶
Challenge: OtterWiki stores at /app-data/, tower-fleet has docs at /docs/
Options:
- Sync to tower-fleet-docs (flat structure, matches wiki)
- Sync to tower-fleet/docs (need path prefix)
- Sparse checkout (only checkout /docs subdirectory)
Recommendation: Continue using tower-fleet-docs as intermediate, or configure sparse checkout of tower-fleet.
Recommended Implementation¶
Start with Option 1 (Native Git Remote):
- It's simplest and uses standard git
- OtterWiki already has git internally
- Easy to understand and debug
- Can upgrade to webhooks later if needed
Implementation steps:
# 1. Stop current git-sync
kubectl scale deployment otterwiki -n otterwiki --replicas=0
# 2. Exec into pod and configure
kubectl exec -it -n otterwiki deployment/otterwiki -- sh
# Inside pod:
cd /app-data
git remote add origin git@github.com:jakecelentano/tower-fleet-docs.git
git fetch origin
git branch --set-upstream-to=origin/main main
# 3. Create post-commit hook
cat > .git/hooks/post-commit << 'EOF'
#!/bin/sh
git push origin main 2>&1
EOF
chmod +x .git/hooks/post-commit
# 4. Create pull script (run via cron or sidecar)
# Pull every 5 minutes to catch remote changes
Migration Plan¶
- Backup current wiki content
- Test in dev environment first
- Configure SSH key secret with write access
- Remove git-sync sidecar
- Add remote and hooks
- Monitor for conflicts
- Document conflict resolution process
Security Considerations¶
- SSH key needs write access to repo
- Key stored as K8s secret
- Consider deploy key with limited scope
- Audit trail via git commits