Sync your Obsidian vault to an atproto Personal Data Server (PDS). Two backends, one engine:
pds_publish: true become site.standard.document records, displayed by standard.site readers such as Leaflet.site.standard.document with a markdown content block, plus a site.standard.publication helper (theme, icon, discovery, .well-known verification).Download main.js + manifest.json from a GitHub release - or build from source (see Develop) - and drop them into <vault>/.obsidian/plugins/pds-sync/, then enable in Settings -> Community plugins. Pre-releases install through BRAT.
---
pds: true # encrypt + sync privately (or pds_publish: true to publish publicly)
---
Nothing syncs unless flagged. After a sync the plugin writes its index back into the note's frontmatter - its "git object id" for change detection.
One note - one record. Each sync hashes the note (body + frontmatter) against the stored index, then creates / updates / skips. Push is compare-and-swap (swapRecord): if a record changed under you it writes a conflict copy instead of clobbering. Pull restores missing notes, applies remote changes, and reflects deletions.
Private notes are encrypted client-side with AES-256-GCM, the key derived by Argon2id (memory-hard) from your passphrase and a salt derived from your DID - deterministic and non-secret, so every device derives the same key.
Important: encrypted records live in your public, firehose-archived repo, which atproto maintainers discourage because archived ciphertext can be attacked offline indefinitely:
src/sync/targets/atsSpace.ts).data.json (don't keep that folder inside a vault you publish). Secret ids carry the vault name, since iOS shares secrets across vaults. App-password JWTs are never persisted (re-login each launch); OAuth tokens live in local storage, namespaced per plugin and vault.pds: true -> private, pds_publish: true -> public; remove or set the flag to false to unpublish.site.standard.publication and auto-fills the Publication URI your documents reference.client_id doc + redirect page (in public/) are served at obsidian-pds-sync.2877686.xyz. To self-host, serve public/ at a host root and update the metadata + settings together.Talks only to atproto infrastructure - no analytics or telemetry: your PDS (records/blobs), your PDS's OAuth server (sign-in), and the static OAuth host above. Sign-in resolves your handle -> DID with no appview - Cloudflare DNS-over-HTTPS (cloudflare-dns.com) raced against a .well-known fetch on your handle's domain - then your DID document via plc.directory or did:web. Credentials and your passphrase never leave the device except to your own PDS / authorization server.
bun install
bun run dev
bun run build
Symlink the repo into <vault>/.obsidian/plugins/pds-sync/ and enable it in Community plugins.
Tag manifest.json's version and push it; .github/workflows/release.yml builds, attests and opens a draft GitHub release - the channel the Obsidian directory reads - which you then publish. For a beta, tag a suffixed version (1.1.1-beta.1) and tick Set as a pre-release when publishing so it doesn't become the latest release. Annotated-tag artifacts on Tangled stay as a mirror; the Spindle CI (.tangled/workflows/build.yml) validates the build on every push and tag.
MIT