Ivan86 downloadsEncrypts Obsidian notes locally and publishes them to Cloudflare R2 for Hermes.
Russian version: README.ru.md
Encrypted Obsidian vault with Cloudflare R2 sync.
Client-side encrypted note publishing to Cloudflare R2 via a control API (Hermes VPS). Supports bidirectional sync, revision history, safe deletion, and vault restore.
Obsidian vault → client-side encryption (XChaCha20-Poly1305) → Cloudflare R2
↕
Hermes control API (VPS)
↓
Hermes‑R2‑File‑Resolver (Python)
| Feature | Status |
|---|---|
| Client-side encryption (XChaCha20-Poly1305 + Argon2id) | ✅ |
| Bidirectional sync | ✅ |
| Content-addressable storage (CAS) for manifests | ✅ |
| Multi-device enrollment | ✅ |
| Setup URI for device pairing | ✅ |
| File revision history | ✅ |
| Single-file restore | ✅ |
| Point-in-time vault restore | ✅ |
| History integrity scanner | ✅ |
| Retention planner | ✅ |
| Safe garbage collection | ✅ |
| Mass deletion protection | ✅ |
| Conflict Center UI | ✅ |
| Audit log | ✅ |
| Russian and English localization | ✅ |
hermes-r2-vault/
├── src/ # Plugin source code
│ ├── main.ts # Entry point, commands, modals
│ ├── sync.ts # Sync orchestrator (core)
│ ├── models.ts # Interfaces and state types
│ ├── constants.ts # Constants and defaults
│ ├── crypto.ts # XChaCha20-Poly1305, Argon2id, X25519
│ ├── envelope.ts # Binary envelope with AAD
│ ├── transport.ts # HTTP client for presigned URLs
│ ├── cas-store-r2.ts # Content-addressable storage (R2)
│ ├── manifest-snapshot.ts # Manifest snapshots
│ ├── file-history.ts # File revision history
│ ├── reference-graph.ts # Revision reference graph
│ ├── retention-planner.ts # Retention planner
│ ├── vault-restore.ts # Vault restore
│ ├── integrity-scanner.ts # Integrity scanner
│ ├── download.ts # Download and decrypt
│ ├── conflict-center.ts # Conflict resolution UI
│ ├── mass-deletion.ts # Mass deletion protection
│ ├── deletion-metrics.ts # Deletion counters
│ ├── audit-log.ts # Event logging
│ ├── fault-handler.ts # Error handling
│ ├── safety.ts # Empty manifest + stale device guards
│ ├── key-envelope.ts # Key management
│ ├── path-utils.ts # Path normalization
│ ├── filter.ts # File filtering
│ ├── queue.ts # Change queue
│ ├── ulid.ts # ULID generation
│ ├── settings.ts # Settings UI
│ ├── log.ts # Structured logging
│ ├── errors.ts # Typed errors
│ ├── i18n/ # Internationalization
│ │ └── index.ts # Russian + English
│ └── protocol/ # Protocol types
│ ├── manifest.ts # SyncManifestV1
│ ├── manifest-snapshot.ts # ManifestSnapshotV1
│ ├── revision.ts # RevisionMetadataV1
│ ├── tombstone.ts # TombstoneV1
│ ├── file-history.ts # FileHistoryIndexV1
│ ├── differ.ts # Diff + LWW resolution
│ ├── conflict.ts # Conflict classification
│ ├── validation.ts # Type validation
│ ├── identifiers.ts # UUID/ULID validation
│ ├── paths.ts # Path encoding
│ ├── canonical-json.ts # Canonical JSON
│ ├── cas-store.ts # CasStore interface
│ ├── device.ts # Device metadata
│ ├── device-envelope.ts # Device key envelope
│ ├── setup-uri.ts # Setup URI
│ ├── setup-token.ts # Setup token
│ └── index.ts # Re-exports
├── tests/ # Tests (379 tests, Vitest)
│ ├── revision-history.test.ts
│ ├── retention-planner.test.ts
│ ├── vault-restore.test.ts
│ ├── integrity-scanner.test.ts
│ ├── protocol.test.ts
│ ├── deletion-safety.test.ts
│ ├── crypto.test.ts
│ ├── key-envelope.test.ts
│ ├── device-identity.test.ts
│ ├── transport.test.ts
│ ├── queue.test.ts
│ ├── i18n.test.ts
│ ├── differ.test.ts
│ └── ... (21 files)
├── hermes-r2-file-resolver/ # Python resolver (Hermes VPS)
├── docs/ # Documentation
│ ├── architecture.md
│ ├── sync-protocol-v1.md
│ ├── security-model.md
│ ├── threat-model.md
│ ├── revision-model-v1.md
│ ├── r2-layout-v1.md
│ ├── manifest-v1.md
│ ├── deletion-safety.md
│ └── ... (16 files)
├── package.json
├── tsconfig.json
├── vitest.config.ts
├── build.mjs
└── manifest.json
Community plugin store (once approved): Settings → Community plugins → Browse → search for "Hermes R2 Vault" → Install → Enable.
Manual install: copy main.js, manifest.json, and styles.css to <vault>/.obsidian/plugins/hermes-rvault/.
Enable the plugin in Obsidian settings.
Configure the API Base URL, Vault ID, and secret names.
Run "Initialize encrypted vault" to generate the master key.
Store the access token via the settings tab (SecretStorage).
Run "Test connection" to verify connectivity.
Run "Run full scan" to upload existing notes.
Note: This plugin is desktop-only. The Obsidian mobile app does not expose the SecretStorage API required for keys and tokens.
Plugin settings are available under Hermes R2 Vault Settings in Obsidian settings.
| Setting | Description | Default |
|---|---|---|
| API Base URL | Control API URL (HTTPS) | — |
| Vault ID | Vault UUID | — |
| Device ID | Device identifier (auto-generated) | randomUUID() |
| Access Token Secret Name | SecretStorage key name | — |
| Auto-upload | Automatically upload changes | false |
| Debounce delay (ms) | Change processing delay | 1500 |
| Max concurrent uploads | Parallel upload limit | 2 |
| Setting | Description | Default |
|---|---|---|
| Enable remote trash | Keep deleted files in manifest | true |
| Deleted retention (days) | Deleted metadata retention period | 90 |
| Mass deletion threshold | Deletion count threshold | 10 |
| Require deletion approval | Prompt for confirmation | true |
| Empty manifest protection | Block sync on empty remote manifest | true |
| Stale device guard | Block stale device sync | true |
| Pre-deletion snapshot | Capture hash before deletion | false |
| Setting | Description | Default |
|---|---|---|
| Revision history enabled | Enable revision history | false |
| Max revisions per file | Max revisions per file | 100 |
| Max snapshot generations | Max snapshot count | 50 |
| Revision TTL (days) | Revision time-to-live | 365 |
| Revision retention (days) | Guaranteed retention period | 90 |
| Minimum retained revisions | Minimum revisions to keep | 20 |
| Command | Description |
|---|---|
| Test connection | Verify API connectivity |
| Initialize encrypted vault | Generate vault master key |
| Recover vault key | Recover key from passphrase |
| Change recovery passphrase | Change recovery passphrase |
| Upload current note | Upload active note |
| Run full scan | Full file scan |
| Pause automatic uploads | Pause upload queue |
| Resume automatic uploads | Resume upload queue |
| Show sync status | Display sync status |
| View sync conflicts | Show sync conflicts |
| Open Conflict Center | Open conflict resolution UI |
| Undo latest deletion | Undo last deletion |
| Purge deleted file | Safe permanent purge |
| Preview retention plan | Retention plan preview |
| Apply retention plan | Execute retention GC |
| List vault restore points | List available snapshots |
| Preview vault restore | Preview vault state restore |
| Scan history integrity | Check history integrity |
| Show deletion safety metrics | Display deletion statistics |
| Show recent faults | Show recent fault records |
| Clear local sync metadata | Clear local sync state |
Full protocol specification: docs/sync-protocol-v1.md.
Protocol version: 1 (marker: docs/protocol-version-v1.json)
| Flag | Status |
|---|---|
revisionHistory |
✅ true |
fileRevisionRestore |
✅ true |
pointInTimeRestore |
✅ true |
historyIntegrityScan |
✅ true |
retentionPlanner |
✅ true |
safeGarbageCollection |
✅ true |
resumableRestore |
✅ true |
hermesReadWrite |
❌ false |
vaults/{vaultId}/
├── manifests/
│ ├── current.json # Manifest pointer
│ ├── by-hash/{sha256}.json # Content-addressable manifests
│ └── snapshots/{generation}.json # Per-generation snapshots
├── history/
│ ├── manifests/index.v1.json # Snapshot index
│ └── files/{fileId}/index.v1.json # File history indices
├── revisions/{fileId}/{revisionId}.hvr # Immutable revisions
├── tombstones/{fileId}.hvr # Tombstones
└── config/
├── owner-key-envelope.v1.json
└── recipients/{recipientId}.key-envelope.v1.json
Details: docs/security-model.md, docs/threat-model.md.
| Command | Description |
|---|---|
npm run build |
Build plugin (esbuild) |
npm run test |
Run tests (Vitest) |
npm run test:coverage |
Coverage report |
npm run typecheck |
TypeScript type check |
npm run lint |
ESLint |
@noble/ciphers, @noble/curves, @noble/hashesThe hermes-r2-file-resolver/ subproject implements the protocol in Python for the Hermes VPS backend.
Before committing:
npm run typecheck # TypeScript — clean
npm run lint # ESLint — clean
npm run test # 379 tests, 21 files
npm run build # esbuild — success
MIT