obsidian plugin
build an Obsidian community plugin as a marketing/distribution channel. no signup required — plugin reads file metadata directly from xattrs set by FileProvider.
consistency rule: VS Code plugin (src/vscode-sublimated/, v0.3.5, 2,951 LOC) is the reference implementation. both plugins must show the same information in the same format, adapted to each platform's UI. when adding features to either plugin, check the other for parity.
related docs (in main sublimated repo):
https://sublimated.com/sublimated/docs/MARKETING/OBSIDIAN — plugin ideas, submission requirements, promotion channels, competitive research
https://sublimated.com/sublimated/docs/MARKETING/ZED_LANGUAGE_SERVER — language server design (shared API endpoints, feature set, comment anchoring)
https://sublimated.com/sublimated/docs/SDK/FILEPROVIDER_DATA_MODEL — FileProvider data model and sync status
https://sublimated.com/sublimated/docs/DEV/OPEN_IN — "open in" deeplink design
https://sublimated.com/sublimated/docs/KNOWLEDGE_BASE/OBSIDIAN — integration strategy, pain points, features
xattrs (existing)
FileProvider already writes these on every synced file:
com.sublimated.version#PS— version number (integer)com.sublimated.commit#PS— last commit hashcom.sublimated.url#PS— web link (sublimated.com/anton/blog/readme_md)com.sublimated.content-hash#PS— BLAKE3 hashcom.sublimated.id#PS— item ID (stable across renames)com.sublimated.created-by#PS— original author namecom.sublimated.modified-by#PS— last author namecom.sublimated.modified-by-type#PS— person/ai_agent/bot
plugin reads these directly from filesystem. no API calls, no token, no signup for core features.
xattrs (new — add to FileProvider)
com.sublimated.sync-status#PS— synced/uploading/downloading/error (set by FileProvider during operations, more reliable than trying to read system badges)com.sublimated.space#PS— space slug (so plugin knows which space without parsing paths)com.sublimated.branch#PS— current branch namecom.sublimated.modified-at#PS— server-side timestamp of last change
note: Finder's FileProvider sync badges (green checkmark, cloud arrow etc) are NOT readable by other apps — NSFileProviderItemDecorating is private IPC between FileProvider and Finder. the sync-status xattr is our workaround: FileProvider writes it, plugin reads it.
alternative: SF_DATALESS flag via stat -f "%Xf" tells if file is cloud-only placeholder vs materialized locally. works for all FileProvider files but only gives binary downloaded/not-downloaded.
URI scheme: sublimated://
registered in apps/mac/Resources/Info.plist. existing "open in" feature uses:
sublimated://open-in?tool=zed&type=file&path={contentPath}already supports: Zed, Cursor, Claude Code, Codex, Amp. implementation in apps/mac/Sublimated/URLHandler.swift.
open in Obsidian (web -> local)
add Obsidian as another tool in URLHandler.swift:
sublimated://open-in?tool=obsidian&type=file&path=blog/post.mdURLHandler resolves local FileProvider path (~/Library/CloudStorage/Sublimated-{space}/{subpath}) then opens:
obsidian://open?vault={vaultName}&file={relativePath}web UI: add Obsidian to "OPEN IN" section in SharePopup.astro (next to Zed). show only when space has .obsidian/ folder (detected from items table — .obsidian is synced or known to exist).
open on web (local -> web)
plugin reads com.sublimated.url xattr -> opens https://{url} in browser. no URI scheme needed, just a browser open.
v1 scope
1. sync status bar — DONE (basic), needs update
done: basic text status bar, click to open on web, updates on file change missing (VS Code has these):
sync status icons: checkmark synced, up-arrow uploading, down-arrow downloading, ! error, warning conflict
visibility indicator: globe public, people shared, lock private
branch display (hide when
main)AI agent prefix:
by AI: namewhenmodifiedByType === "ai_agent"(vs plainby name)visibilityfield missing from Obsidian's FileMetadata type (VS Code has 13 fields, Obsidian has 12)target format:
sublimated: v12 · checkmark synced · by anton · feature-x · globe(matches VS Code)
2. version history — DONE (basic), needs update
done: sidebar view, version list with number/author/timestamp, "view on web" link missing (VS Code has these):
group versions by day: "Today", "Yesterday", "X days ago", "Mon DD"
show commit message per version
diff viewer (click version to see diff against current)
3. view on web — DONE
done: command palette + status bar click opens sublimated.com URL from xattr
4. open in Obsidian (from sublimated.com) — DONE (plugin side)
done: URI handler for obsidian://sublimated?action=open&file=path + OAuth token action=auth
not done: web UI changes (add Obsidian to SharePopup.astro OPEN IN section), macOS app URLHandler.swift
5. welcome modal — DONE
done: first-run modal with setup steps, links, hasSeenWelcome flag
6. settings page — DONE (basic)
done: API token input, test connection, workspace detection (.sbl/ folder), links missing: OAuth sign-in button (see oauth-signin section below)
7. file explorer decorations — NEW (VS Code has this)
VS Code shows sync status badges on files in the explorer tree:
checkmark synced (no color), up-arrow uploading (blue), down-arrow downloading (blue), ! error (red), warning conflict (red)
Obsidian equivalent: FileExplorerPlugin API or CSS-based decorations on file tree items
read sync-status xattr per file, show badge/icon next to filename
8. cache layer — NEW (VS Code has this)
VS Code has cache.ts (85 lines) with in-memory cache for xattr reads and API responses. Obsidian plugin currently re-reads all xattrs on every file switch with no caching.
add TTL-based cache for xattr reads (avoid shelling out
xattr13 times on every tab switch)cache API responses (folder context, file context)
invalidate on file save events
9. workspace detection — needs update
done: checks for .sbl/ folder in vault root
missing (VS Code has these):
CloudStorage mount path detection:
~/Library/CloudStorage/Sublimated-{space}/(prod) and~/Library/CloudStorage/sublimated-dev-{space}/(dev).sbl/configor.sbl/config.ymlparsing for space slug (VS Code reads plain textspace=...or YAMLspace: ...)detection result should include: space slug, folder path, root path, detection mode (cloud-storage vs sbl-config vs sbl-folder)
10. comments and threads (VS Code has full implementation)
VS Code: comments/controller.ts (513 lines) — native CommentController with gutter "+", threading, resolve/reopen, reactions, anchor resolution (text search -> line number -> top of file).
Obsidian equivalent:
sidebar view: list all comments on the active file (anchored to specific text)
each comment shows: author, timestamp, quoted text, resolved/unresolved status
threaded replies shown inline under parent comment
clicking a comment scrolls to the anchored text in the editor
anchor resolution: same cascade as LSP — Loro cursors -> PM JSON blockId -> text search (quote+prefix+suffix) -> blockIndex -> orphaned
unresolved comments shown as notices/callouts or gutter markers
"resolve" button on each comment (API call, just a toggle)
"reply on sublimated.com" button opens browser at the thread
new comment creation happens on sublimated.com (no input forms in plugin)
API endpoint: GET /v1/spaces/{slug}/items/{id}/context (returns comments with FileAnchor)
11. reactions
show reactions on file or section level in sidebar or status bar
hover or click to see who reacted
adding reactions happens on sublimated.com — plugin links to web UI
reactions are anchored to text via FileAnchor (same as comments)
12. section-level history (VS Code has CodeLens equivalent)
VS Code: codelens.ts (157 lines) — CodeLens above headings showing "last: {author}, {timeAgo}" and version/comment counts. Obsidian has no CodeLens, so use different UI.
command palette: "sublimated: show section history"
for each heading in the current file, show: last author, edit count, actor count
sections derived from AST (heading boundaries)
useful for long docs — see which sections changed recently and by whom
API provides this via file context endpoint (sections array with lastAuthor, editCount, actorCount)
13. hover and diagnostics (VS Code has these, Obsidian needs equivalents)
VS Code: hover.ts (163 lines) — wikilink hovers showing file info (versions, comments, backlinks). diagnostics.ts (104 lines) — broken link warnings, unresolved comment indicators.
Obsidian equivalent:
reading view: render broken
[[links]]with visual indicator (strikethrough, red text)sidebar or notice: list unresolved comments with context
hover on wikilinks (if Obsidian API supports it): show version count, last author, comment count
14. block attribution (PM JSON)
sidebar or hover: per-paragraph attribution
each block (PM JSON blockId) shows: original author, creation date, edit count, last editor
requires PM JSON representation (block IDs stable across edits)
15. character-level attribution (Loro)
deeper attribution: percentage breakdown of who wrote what in a paragraph
distinguishes human vs AI edits
Loro CRDT tracks character-level authorship
could render as colored highlights in reading view (different color per author)
16. @mention rendering
render
@usernameas styled inline elements (colored, linked)clicking @mention opens that user's profile on sublimated.com
in editing mode: autocomplete dropdown when typing
@(users with access to the space)API provides user list via folder context endpoint
17. stale content detection
embedded files (
![[data.csv]],![[report.md]]) checked for stalenessif referenced file changed since current file was last edited, show notice
uses content-hash xattr on referenced file vs stored hash at embed time
18. activity tracking
plugin sends editor events to API as they happen (same as LSP):
file opened, content changed (with position info), file saved, file closed
API endpoint: POST /v1/spaces/{slug}/items/{id}/events
server computes section-level focus data from change positions
powers writing analytics: time spent editing vs reading per section
19. backlinks
sidebar view: list all files that link to the current file via
[[wikilinks]]each backlink shows: file path, link context (surrounding text), version count, last author
clicking a backlink opens that file
data from item_dependencies table (dep_type = 'links_to')
Obsidian has native backlinks but sublimated adds: version count, author info, comment count per linking file
20. broken link detection
[[links]]pointing to nonexistent files flagged in reading view or sidebaroffer to create the missing file
also detects broken content embeds (
![[missing.png]])uses folder context items list to check existence
implementation work
plugin — exists, needs updates
existing: TypeScript + esbuild, main.ts + manifest.json + styles.css, plugin ID sublimated
existing: xattr.ts (reads 12 attrs), api.ts (versions + test connection), settings.ts, version-view.ts, welcome-modal.ts
immediate work (VS Code parity for status bar):
add
visibilityto FileMetadata in xattr.ts (13th field, matching VS Code)update status bar format to match VS Code: icons, visibility, branch, AI prefix
add cache layer (new cache.ts) for xattr reads and API responses
update workspace detection: CloudStorage mount path,
.sbl/configparsingadd file explorer decorations (sync badges)
later work:
comments sidebar (equivalent of VS Code's CommentController)
section-level info (equivalent of VS Code's CodeLens)
hover/diagnostics (equivalent of VS Code's hover + diagnostics providers)
API client: expand to match VS Code's api.ts (folder context, file context, editor events, comments)
FileProvider changes (sdk/swift/)
add 4 new xattrs: sync-status, space, branch, modified-at
add visibility xattr (already in VS Code constants.ts but needs FileProvider to set it)
set sync-status during upload/download/error operations in SublimatedFileProviderExtension.swift
web UI changes (sublimated.com worker)
add Obsidian to OPEN IN tools in SharePopup.astro (with Obsidian icon)
detect
.obsidian/folder presence to show/hide the button
macOS app changes
add
obsidiancase to URLHandler.swift tool routerresolve path ->
obsidian://open?vault={vault}&file={path}
API (api.sublimated.com)
same endpoints as language server:
GET /v1/spaces/:slug/items/:folderId/context — folder context (items, links, users)
GET /v1/spaces/:slug/items/:id/context — file context (comments, versions, sections, blocks, attribution, reactions, session)
POST /v1/spaces/:slug/items/:id/events — editor events (open, change, save, close)
POST /v1/spaces/:slug/messages — comment resolve
plugin uses folder context on vault open, file context on note open
editor events sent as user interacts (same as LSP)
OAuth sign-in
add OAuth redirect flow so users can sign in with one click instead of pasting API tokens.
how it works:
user clicks "Sign in with sublimated" in plugin settings
plugin opens browser to
id.sublimated.com/auth/obsidianuser signs in (GitHub, Apple, email — whatever id.sublimated.com supports)
after sign-in, server redirects to
obsidian://sublimated?action=auth&token={sessionToken}plugin URI handler catches the redirect, stores the token
API calls use
Authorization: Bearer {token}— same as the macOS app
server-side: add GET /auth/obsidian route to id.sublimated.com. bearer plugin already set up.
plugin-side: main.ts already handles obsidian://sublimated?action=auth&token=... in handleUri(). settings.ts needs a "Sign in" button that opens the browser URL.
notes:
keep manual API token input as fallback
token should be long-lived (months/never) since there's no session refresh in Obsidian
consider scoped access token (read-only, specific space) vs full session
submission
source of truth: https://sublimated.com/sublimated/code/obsidian (dogfooded on our own platform)
GitHub mirror for submission: https://github.com/sublimates/obsidian — Obsidian community plugins require a GitHub repo URL in community-plugins.json. keep sublimated.com as primary, push to GitHub for releases
PR to obsidianmd/obsidian-releases adding entry to community-plugins.json
macOS only for now (iOS later). no Windows/Linux support planned
automated validation + manual review
README with screenshots, description, setup instructions
stale PR policy: 30 days warning, 45 days auto-close
platform support
macOS only for now. sublimated's FileProvider and xattrs are macOS-specific
iOS later (Obsidian has an iOS app, FileProvider works on iOS too)
no Windows/Linux support planned — sublimated itself is macOS/iOS only
open questions
git-only users (no FileProvider)
what if someone clones via git clone https://sublimated.com/slug.git and opens the clone as an Obsidian vault? no FileProvider means no xattrs, no sync status, no .sbl/ detection. options:
plugin detects
.git/with sublimated remote URL instead of.sbl/foldershell out to
git log,git status,git diffto show version/sync infoshow unsynced changes by running
git status— dirty files = unsyncedpush/pull via git commands from within the plugin
this is a separate mode from FileProvider mode — need to detect which one we're in
auto-push
should the plugin offer auto-push in settings? when user saves a file:
run
git add . && git commit -m "auto" && git pushin backgroundor debounce: push every N minutes if there are uncommitted changes
risk: merge conflicts if someone else pushes. how to handle?
could be opt-in toggle in settings: "auto-sync" on/off, with interval selector
only makes sense for git-clone mode — FileProvider already syncs automatically
sessions, checkpoints, and focus mode
the macOS app tracks editing sessions with checkpoints, focus mode, and attribution (human vs AI edits). how does this surface in Obsidian?
session sidebar: show active session with elapsed time, word count delta, checkpoint list
checkpoints: auto-snapshot at intervals or on save. "restore checkpoint" command to revert to a point-in-time
focus mode: start/stop focus session from command palette. track uninterrupted writing time, words written
attribution: show which parts were edited by human vs AI agent (inline highlights or gutter marks)
this is a differentiator vs plain Obsidian — writing analytics and session tracking built into the sync layer
sharing and access visibility
shared/public folders and files should be visible in Obsidian:
status bar: show sharing status alongside sync info
file explorer decorations: small icon (people, globe) next to shared/public files and folders
command: "sublimated: show access" — opens panel showing who has access, roles, visibility
needs:
com.sublimated.visibility#PSxattr (private/shared/public) and optionallycom.sublimated.shared-with#PSAPI endpoint:
GET /v1/items/{id}/accessfor detailed access panel
promotion
r/ObsidianMD (https://www.reddit.com/r/ObsidianMD/)
Obsidian Forum "Share & showcase"
Obsidian Discord #updates
twitter #ObsidianMD
blog post: "how to use Obsidian with sublimated"