Filen Sync
approvedby Erik Vaněk
Sync your vault with Filen end-to-end encrypted cloud storage. - This plugin has not been manually reviewed by Obsidian staff.
Filen Sync for Obsidian
Sync your vault with Filen end-to-end encrypted cloud storage, on desktop and mobile. Talks to Filen directly through the official SDK, so there is no server to run and nothing to configure outside Obsidian.
Back up your vault before using this. It is early software that has not yet been run against a real Filen account, and a sync plugin writes and deletes files on both sides by design. Deletions go to a trash you can recover from and conflicts keep both copies, but neither is a substitute for a copy of your notes somewhere this plugin cannot reach.
Install
Not in the community plugin list yet, so pick one of these until it is.
With BRAT, which also keeps it updated:
- Install BRAT from Settings, Community plugins, Browse.
- In BRAT's settings, press Add Beta plugin and enter
NeDDy3z/filen-obsidian. - Enable Filen Sync under Settings, Community plugins.
By hand, if you would rather not add another plugin:
- Download
main.jsandmanifest.jsonfrom the latest release. - Put both into
<your vault>/.obsidian/plugins/filen-sync/, creating the folder if needed. - Restart Obsidian, then enable Filen Sync under Settings, Community plugins.
Once it is in the community list, Settings, Community plugins, Browse, search for Filen will replace both of these.
Setup
- Set Folder on Filen if you want something other than
Obsidian/<vault name>. It is created if it does not exist. - Enter your Filen email, password and two-factor code, then press Log in. Only the derived account keys are stored, never the password. Pasted values are tidied up, so a trailing newline from a password manager or the space in
123 456from an authenticator app does not break the login. - Press Check connection. It uploads, downloads, compares and deletes a small test file, so a pass means the whole round trip works against your account.
- Pick a direction and an auto-sync trigger.
- Sync from the ribbon icon, the status bar item, or the Sync now command.
On a second device, install, log in, and point it at the same folder on Filen.
Direction
| Mode | Effect |
|---|---|
| Both ways (default) | Changes travel in both directions |
| Vault to Filen only | Your vault is the source of truth |
| Filen to vault only | Filen is the source of truth |
| Off, do not sync | Nothing runs, including the automatic triggers |
One-way modes do not simply ignore the other side. If the non-authoritative side deleted a file, it is put back from the authoritative one, because ignoring it would leave the two sides permanently disagreeing and re-deciding the same thing on every sync. So in "Vault to Filen", deleting a note on another device brings it back from this vault; in "Filen to vault", deleting a note here brings it back from Filen.
Conflicts follow the direction too. Pushing uploads your copy, and Filen's version history keeps the old one. Pulling saves your copy beside the note as note.conflict-20260818-143005.md before writing Filen's version, so nothing is lost either way.
Auto-sync
Three independent triggers, configurable in settings:
- After you stop typing. Waits for the vault to be quiet for N seconds. Runs once after a writing session, not once per keystroke. Edits made while a sync is already running are not lost, another sync follows it. The plugin's own downloads do not count as edits.
- On an interval. Every N minutes.
- On startup. One sync shortly after Obsidian opens.
On desktop the status bar shows the current state and clicking it syncs. Mobile has no status bar, so it reports through notices instead.
How it works
Every sync compares three things: your vault now, Filen now, and a snapshot of the last successful sync kept per device. The snapshot is what tells a deletion apart from a new file.
To do that it lists every file in the vault and reads each one's path, size and modification time. A sync plugin cannot decide what to transfer without seeing what is there. Exclusions and the .obsidian setting are applied to that listing afterwards, and file contents are only ever read for files that are actually being uploaded.
| At last sync | In vault | On Filen | Result |
|---|---|---|---|
| no | yes | no | upload |
| no | no | yes | download |
| no | yes | yes | same: skip. different: conflict |
| yes | yes | no | deleted on Filen, so delete locally |
| yes | no | yes | deleted locally, so delete on Filen |
| yes | changed | unchanged | upload |
| yes | unchanged | changed | download |
| yes | changed | changed | conflict |
Conflicts keep both copies. The Filen version is saved next to yours as note.conflict-20260818-143005.md and your version is uploaded. Nothing is merged, nothing is silently overwritten.
Deletions are recoverable. Local ones go to the vault .trash, remote ones to the Filen trash, and Filen keeps previous versions of overwritten files.
Modification times travel in both directions, which is what stops two devices bouncing the same file back and forth forever.
Force sync reconciles as if this device had never synced. It starts from an empty snapshot, so no delete branch above can fire: nothing is removed on either side, and anything that differs becomes a kept-both-copies conflict. Reach for it when two devices have drifted apart.
Syncing the .obsidian folder
| Mode | Syncs |
|---|---|
| Off (default) | Notes only |
| Plugins and themes | Plugin code, the enabled-plugin lists, appearance.json, hotkeys.json, themes/, snippets/ |
| Everything except window layout | The whole folder, per-plugin settings included |
Synced settings only take effect after Obsidian restarts. It reads the enabled-plugin lists and the theme once at startup and holds them in memory, so a downloaded community-plugins.json or appearance.json changes nothing on a running app. The plugin tells you when a sync has written settings files. Reload reasonably promptly: if you change a setting first, Obsidian writes its in-memory copy back over the downloaded one, and the next sync uploads that instead.
workspace.json is never synced in any mode. Obsidian rewrites it every time you move a pane, so syncing it means two devices fighting over window layout forever, and a phone layout is not a desktop layout anyway.
The middle mode exists because plugin code is what you actually want on a new device, while plugins/<id>/data.json is the risky part: a running plugin rewrites its own settings from memory at unpredictable moments, so a downloaded copy can be clobbered or read half-written, and those settings are often device-specific. Pick "Everything" if you want it anyway.
This plugin's own folder is always skipped, so your credentials and this device's snapshot stay on this device.
Development
npm install
npm run dev # watch build into main.js
npm run build # typecheck and minified build
| File | What it is |
|---|---|
src/sync.ts | Decision table, direction narrowing, config filtering, snapshot format. No I/O |
src/local.ts | The vault side, via Obsidian's adapter |
src/filen.ts | The Filen side, via the SDK's cloud() calls |
src/main.ts | Plugin, settings, commands, action executor |
Releases are built by GitHub Actions, not locally. Push a tag matching the version in manifest.json and .github/workflows/release.yml builds main.js, signs it with a build provenance attestation, and publishes the release. That lets anyone confirm the shipped bundle came from this source rather than someone's laptop:
gh attestation verify main.js -R NeDDy3z/filen-obsidian
src/filen.ts avoids sdk.fs() deliberately. Those helpers resolve paths through an internal cache only sdk.fs().readdir() fills, and sdk.fs().writeFile() is node-only. One getDirectoryTree() call gives the whole subtree instead, and uploads go through cloud().uploadWebFile().
Caveats
- Credentials sit unencrypted in
.obsidian/plugins/filen-sync/data.json, as with any Obsidian sync plugin. They grant full access to your Filen drive, so treat vault backups as sensitive. @filen/sdkcalls itself a work in progress. The version is pinned exactly.- Files are held in memory during transfer, so exclude very large attachments.
- No file watcher beyond the triggers above, so notes can be up to one interval stale.
- Requires Obsidian 1.13.0 or later, since the settings use the declarative settings API added in that release.
Contributing
Issues and pull requests are welcome. The plugin is small on purpose, so the bar for adding a setting or a dependency is high: if a few lines of plain code will do, prefer that.
Getting set up:
git clone https://github.com/NeDDy3z/filen-obsidian
cd filen-obsidian
npm install
npm run dev
npm run dev rebuilds main.js on every change. To try it in a real vault, symlink the repo into that vault's plugin folder and reload Obsidian after each build:
ln -s "$(pwd)" "<your vault>/.obsidian/plugins/filen-sync"
Use a scratch vault rather than your real notes. This plugin deletes files on both sides when the decision table says to, and a bug in that table is the kind that eats notes.
Before opening a pull request, run npm run build, which typechecks and produces the bundle. Keep an eye on the size of main.js: most of it is the Filen SDK, and anything that pulls in another large dependency needs a good reason.
A few conventions:
- Commit subjects read as plain past-tense verbs, for example "Added the sync direction dropdown" or "Removed the redundant snapshot write".
- Comments explain why, not what. If a comment restates the code, delete it. The ones worth keeping record something you cannot see from the code, like why
sdk.fs()is avoided or whyworkspace.jsonis never synced. - Releases are cut by pushing a tag that matches the version in
manifest.json. GitHub Actions builds, signs and publishes it.
Contributions are accepted under the same license as the project.
License
Copyright 2026 Erik Vaněk. Licensed under the Apache License, Version 2.0.
Third-party code
The built main.js bundles @filen/sdk, which is licensed under the GNU Affero General Public License v3.0. AGPL-3.0 is a strong copyleft licence, so a distributed build that includes the SDK is a combined work and has to be offered under AGPL-3.0, whatever licence this repository's own source carries. Apache-2.0 code may be combined into an AGPL-3.0 work, but the result cannot be relicensed back to Apache-2.0.
In practice that means the Apache-2.0 licence above covers the source in src/, while any release artifact that embeds the SDK must be distributed under AGPL-3.0 with its source made available. Resolve this before publishing a release: either license the whole plugin AGPL-3.0 to match, or obtain different terms from Filen.
Other bundled dependencies are MIT (buffer, events, path-browserify, and the SDK's own MIT dependencies), BSD-2-Clause (dotenv, progress-stream), and BSD-3-Clause or GPL-2.0 (node-forge).
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.