Linear Sync
pendingby Samuel Ho
Bidirectional sync between Linear issues and notes with automatic file generation, multi-team support, and state-based organization.
Obsidian Linear Sync Plugin v1.0.0
Bidirectional sync between Linear and Obsidian with intelligent template-based file generation and multi-team support
📋 Overview
Bidirectional sync between Linear issues and Obsidian with automatic file generation, multi-team support, and state-based organization.
Key Features (v1.0)
- 🔄 Bidirectional Sync: Sync changes between Obsidian and Linear (configurable direction)
- 📥 Linear → Obsidian: Automatic issue sync with progressive template generation
- 📤 Obsidian → Linear: Update Linear issues by editing frontmatter (state, priority, estimate, assignee, due date)
- ⚡ Webhook Integration: Real-time updates via n8n webhook queue (file-based)
- 🔀 Conflict Resolution: Configurable strategies (most-recent-wins, linear-wins, obsidian-wins)
- 👥 Multi-Team Support: Sync multiple Linear teams with independent configurations
- 📁 Automatic Organization: Issues organized by team and workflow state
- 📝 Progressive Templates: Cumulative template generation as issues advance through states
- 🎯 State-Based Mapping: Customizable directory structure per Linear workflow state
- 📊 Kanban Board Generation: Auto-generated TRACKER.md kanban boards
- 🔁 Sync Loop Prevention: Timestamp-based guards prevent infinite write cycles
- 📱 Mobile Support: Platform-aware file watching and debouncing
- 🎨 Templater Integration: Context-aware file generation with Linear data injection
🔮 Planned Features
- v2.0: Advanced conflict resolution (vector clocks, CRDTs), enhanced webhook features
- v3.0: Real-time HTTP webhooks (if Obsidian adds server capability), collaborative editing
🏗️ System Architecture (v1.0)
graph TB
subgraph "Linear Platform"
LA[Linear API<br/>GraphQL Mutations]
LW[Linear Webhooks]
end
subgraph "n8n Integration"
N8N[n8n Workflow<br/>Webhook Receiver]
WQ[Webhook Queue<br/>.linear-webhook-queue.json]
end
subgraph "Obsidian Plugin"
PS[Plugin Service<br/>Polling Sync]
LC[LinearClient<br/>+ 5min Cache + Mutations]
SO[SyncOrchestrator<br/>Linear → Obsidian]
BS[BidirectionalSyncService<br/>Obsidian → Linear]
WP[WebhookQueueProcessor<br/>Real-time Updates]
DS[DirectorySyncService]
TG[TemplateGenerationService]
KB[KanbanBoardService]
CD[ObsidianChangeDetector<br/>File Watcher]
end
subgraph "Obsidian Vault"
TM[TRACKER.md<br/>Kanban Board]
TD[Team Directories<br/>Per-Team Config]
IF[Issue Files<br/>Markdown Docs]
TF[Template Files<br/>Per-Team Templates]
end
subgraph "External Dependencies"
TP[Templater Plugin<br/>Required]
end
%% Linear → Obsidian (Polling)
LA -.->|Poll| LC
LC --> SO
PS --> SO
SO --> DS
SO --> TG
SO --> KB
%% Obsidian → Linear (Bidirectional)
IF -.->|File Changes| CD
CD --> BS
BS --> LC
LC -.->|Mutations| LA
%% Webhook Flow
LW -.->|HTTP POST| N8N
N8N --> WQ
WQ -.->|Poll Queue| WP
WP --> SO
%% Template Processing
TG --> TP
TP --> TF
DS --> TD
DS --> IF
KB --> TM
TF --> IF
style LA fill:#5E6AD2
style PS fill:#7C3AED
style BS fill:#10B981
style WP fill:#F59E0B
style N8N fill:#FF6B6B
Key Architecture Features:
- Bidirectional Sync: Linear ↔ Obsidian with configurable conflict resolution
- Webhook Integration: Real-time updates via n8n file-based queue
- Sync Loop Prevention: Timestamp guards prevent infinite write cycles
- Mobile Support: Platform-aware file watching with optimized debouncing
🚀 Quick Start
Prerequisites
- Obsidian v1.0.0 or higher
- Templater Plugin installed and configured
- Linear API Key from Linear Settings → API
- Folder Structure (auto-created on first sync)
Installation
-
Install the Plugin
# The plugin is located at: docs/automation/plugins/obsidian-linear-sync/ # Symlinked to Obsidian plugins: .obsidian/plugins/obsidian-linear-sync/ -
Configure Settings
- Open Settings → Linear Sync
- Enter your Linear API key
- Set template folder path:
templates/kanban-template - Test connection
⚠️ SECURITY WARNING: Never commit your
data.jsonfile to version control! It contains your Linear API key. This file is already in.gitignore, but double-check before pushing to any repository. If you accidentally commit your API key:- Immediately revoke the key in Linear Settings → API
- Remove the file from git history using
git filter-branchor BFG Repo-Cleaner - Generate a new API key and configure the plugin again
-
First Sync
- Run command: "Linear Sync: Sync from Linear"
- Plugin creates folder structure automatically
🔗 Webhook Setup (Optional)
For real-time webhook updates instead of polling:
Prerequisites
- Local REST API Plugin - Install from Obsidian community plugins
- Public tunnel - ngrok, Cloudflare Tunnel, or localtunnel to expose localhost
Setup Steps
1. Install Local REST API Plugin:
- Open Obsidian Settings → Community plugins
- Search for "Local REST API"
- Install and enable
- Note the API key from Local REST API settings
2. Set Up Tunnel (Choose one):
Option A: ngrok (Recommended)
# Install ngrok: https://ngrok.com/download
# Get free authtoken: https://dashboard.ngrok.com/get-started/your-authtoken
ngrok config add-authtoken YOUR_AUTHTOKEN
# Start tunnel to Local REST API port
ngrok http 27123
Option B: Cloudflare Tunnel
# Install cloudflared: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/
cloudflared tunnel --url http://localhost:27123
Option C: localtunnel
npx localtunnel --port 27123
3. Configure Plugin:
- Open Settings → Linear Sync → Sync Configuration
- Set "Sync Method" to "Webhook"
- Copy your tunnel URL (e.g.,
https://abc123.ngrok.io) - Paste into "Webhook Public URL" field
- Copy the full webhook URL displayed below (includes endpoint path)
4. Configure Linear Webhook:
- Open Linear Settings → API → Webhooks
- Click "New Webhook"
- Paste the full webhook URL from plugin settings
- Select event types:
Issue(all events) - Optional: Set webhook secret (must match in plugin settings)
- Save
5. Test:
- Create or update an issue in Linear
- Check Obsidian - changes should appear within seconds!
Troubleshooting
Webhooks not received:
- Verify tunnel is running and URL is accessible
- Check webhook URL in Linear matches the full URL from plugin settings
- Verify Local REST API plugin is enabled
- Check HMAC secret matches if configured
- Enable Local REST API plugin logs for debugging
Tunnel disconnected:
- Restart your tunnel command
- Update the public URL in plugin settings
- Update webhook URL in Linear if it changed
📂 Phase Mapping System
The plugin uses a 6-phase workflow with progressive documentation:
stateDiagram-v2
[*] --> Planning
Planning --> Pending
Pending --> InProgress
InProgress --> Review
Review --> Staging
Staging --> Closed
Planning: 01-planning<br/>PRD + Architecture
Pending: 02-pending<br/>+ Task Planning
InProgress: 03-in-progress<br/>+ Implementation
Review: 04-review<br/>+ Audit Checklist
Staging: 05-staging<br/>+ Changelog
Closed: 06-closed<br/>Archived
Phase Details
| Phase | Directory | Linear States | Templates Generated | Purpose |
|---|---|---|---|---|
| Planning | 01-planning | Backlog, Triage | 01-prd.md 02-architecture.md | Requirements & Design |
| Pending | 02-pending | Unstarted | + 03-task.md | Ready for Development |
| In Progress | 03-in-progress | Started | + 04-audit.md | Active Development |
| Review | 04-review | Started (Review) | Same as Progress | Code Review & QA |
| Staging | 05-staging | Completed | + 05-changelog.md | Pre-production |
| Closed | 06-closed | Done, Canceled | All files | Archived |
🎯 Template System
Templates use Templater variables for dynamic content:
Available Variables
// Linear Issue Data
<% linear.id %> // Issue ID
<% linear.identifier %> // Issue key (e.g., ENG-123)
<% linear.title %> // Issue title
<% linear.description %> // Issue description
<% linear.state %> // Current state
<% linear.priority %> // Priority level
<% linear.assignee %> // Assigned user
<% linear.due_date %> // Due date
<% linear.project %> // Project name
<% linear.labels %> // Issue labels
<% linear.url %> // Linear URL
// Metadata
<% tp.date.now("YYYY-MM-DD HH:mm") %> // Current timestamp
<% phase %> // Current phase
Template Files
- 01-prd.md - Product Requirements Document
- 02-architecture.md - Technical Architecture
- 03-task.md - Implementation Tasks
- 04-audit.md - Review Checklist
- 05-changelog.md - Release Notes
⚙️ Configuration
Plugin Settings
# Settings accessible via Obsidian Settings → Linear Sync
# Authentication
apiKey: 'lin_api_...' # Your Linear API key
# Sync Configuration
# Choose sync method: polling or webhook (mutually exclusive)
syncMethod: 'polling' # polling | webhook
# Polling Configuration (only used when syncMethod = polling)
pollingConfig:
intervalMinutes: 5 # Minutes between polling syncs
# Webhook Configuration (only used when syncMethod = webhook)
# Requires Local REST API plugin to be installed
webhookConfig:
endpointPath: '/linear/webhook' # Webhook endpoint path
linearWebhookSecret: '' # Optional HMAC secret (must match Linear webhook secret)
batchSize: 10 # Processing batch size
maxRetries: 3 # Max retry attempts for failed events
# Sync Direction
syncDirection: 'linear-to-obsidian' # linear-to-obsidian | bidirectional | obsidian-to-linear
conflictResolution: 'most-recent-wins' # most-recent-wins | linear-wins | obsidian-wins (only for bidirectional)
# Field Sync
syncPriority: true # Sync priority field
syncEstimate: true # Sync estimate points
syncDueDate: true # Sync due dates
File Frontmatter
Synced files include metadata:
---
# Linear Metadata
linear_id: abc-123-def
linear_identifier: ENG-123
linear_url: https://linear.app/team/issue/ENG-123
linear_state: In Progress
linear_state_id: uuid
linear_priority: 2
linear_estimate: 3
linear_due_date: 2024-01-15
linear_team_id: uuid
linear_assignee_id: uuid
# Sync Settings
auto_sync: true # Enable bidirectional sync for this file
last_sync: 2024-01-10T10:30:00Z
# Template Metadata
template_type: prd
template_version: 4.0.0
phase: 03-in-progress
---
Bidirectional Sync Fields: Edit these frontmatter fields to update Linear:
linear_state- Change issue statepriority- Update priority (0-4)estimate- Update estimate pointsdue_date- Update due dateassignee- Update assignee by name
Webhook Setup
Prerequisites:
- Install Local REST API plugin from Community Plugins
- Enable Local REST API plugin
- Note the API key from Local REST API settings
Configuration:
-
Linear Sync Settings:
- Set Sync Method to "Webhook"
- Copy the webhook URL displayed (e.g.,
https://localhost:27123/linear/webhook?api=YOUR_KEY) - (Optional) Set Linear webhook secret for HMAC verification
-
Linear Settings:
- Go to Linear → Settings → API → Webhooks
- Create new webhook
- Paste the URL from Linear Sync
- Enter the same secret (if using)
- Subscribe to: Issue created, updated, deleted
-
For Remote Access (Optional):
# Use ngrok or similar tunnel ngrok http 27123 # Use tunnel URL in Linear https://abc123.ngrok.io/linear/webhook?api=YOUR_KEY
🔧 Commands
Access via Command Palette (Cmd/Ctrl + P):
| Command | Description | Hotkey |
|---|---|---|
| Sync from Linear | Fetch and update all issues | - |
| Sync to Linear | Push local changes to Linear | - |
| Batch sync all kanban files | Sync files with auto_sync: true | - |
| Create Linear issue from note | Create new issue from current file | - |
| Create kanban structure | Generate folder structure for issue | - |
| Test Linear connection | Verify API key and connection | - |
🔄 Data Flow
Linear → Obsidian (Polling & Webhooks)
sequenceDiagram
participant L as Linear
participant N as n8n
participant W as Webhook Queue
participant P as Plugin
participant V as Vault
participant T as Templater
Note over L,P: Polling Sync (Every 5 min)
L->>P: Fetch Issues (GraphQL)
P->>P: Check Cache (5min TTL)
P->>V: Find/Create Directory
P->>T: Request Template
T-->>P: Rendered Template
P->>P: Mark Plugin Write
P->>V: Write/Update Files
P->>V: Update TRACKER.md
Note over L,V: Webhook Flow (Real-time)
L->>N: Webhook Event
N->>W: Write to Queue File
P->>W: Poll Queue (Every 5s)
P->>P: Process Event
P->>L: Fetch Updated Issue
P->>V: Update Files
Obsidian → Linear (Bidirectional Sync)
sequenceDiagram
participant U as User
participant V as Vault
participant P as Plugin
participant L as Linear
Note over U,L: User Edits Frontmatter
U->>V: Edit File (state, priority, etc.)
V->>P: File Change Event
P->>P: Check Sync Loop Guard
P->>P: Detect Frontmatter Changes
P->>P: Debounce (300ms desktop)
alt Has Syncable Changes
P->>L: Update Issue (GraphQL Mutation)
L-->>P: Confirm Update
P->>P: Update Cache
Note over V,L: ✓ Synced to Linear
else No Changes
P->>P: Skip (No sync needed)
end
🐛 Troubleshooting
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| No API Key Error | Missing configuration | Settings → Linear Sync → Add API key |
| Files Not Syncing | Missing frontmatter | Ensure linear_id and auto_sync: true |
| Templates Not Working | Templater not configured | Install and configure Templater plugin |
| State Not Updating | File in wrong folder | Check file is in recognized kanban folder |
| Connection Issues | Network/API problems | Use "Test connection" in settings |
Debug Mode
- Open Developer Console:
Ctrl+Shift+I(Windows/Linux) orCmd+Opt+I(Mac) - Filter by "Linear Sync" for plugin logs
- Check for GraphQL errors or template issues
🔗 Integration
This plugin integrates with the automation stack:
- n8n Workflows: Webhook processing at
http://localhost:5678 - Cyrus AI Agent: Linear automation at
http://localhost:3456 - Docker Services: See INTEGRATION.md
📚 Documentation
- TEMPLATER-GUIDE.md - Template configuration
- API.md - Technical API reference
- INTEGRATION.md - Automation stack integration
- DEVELOPMENT.md - Developer guide
- examples/ - Sample files and templates
🤝 Contributing
- Fork the repository
- Create feature branch
- Test with mock Linear data
- Submit pull request
📄 License
MIT License - See LICENSE file
🙏 Credits
- Author: Samuel Ho
- Website: https://samuelho.space
- Dependencies: Obsidian API, Templater Plugin
- API: Linear GraphQL API
Last Updated: 2024-08-29 | Version: 1.0.0 | Requires: Obsidian 1.0.0+, Templater
For plugin developers
Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.