Hermes R2 Vault

approved

by Ivan

Encrypts Obsidian notes locally and publishes them to Cloudflare R2 for Hermes. - This plugin has not been manually reviewed by Obsidian staff.

84 downloadsUpdated 1mo agoApache-2.0

Hermes R2 Vault

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.


Architecture

Obsidian vault → client-side encryption (XChaCha20-Poly1305) → Cloudflare R2
                                                       ↕
                                       Hermes control API (VPS)
                                                     ↓
                                   Hermes‑R2‑File‑Resolver (Python)
  • Cloudflare R2 is the only persistent knowledge store.
  • The control API never receives plaintext Markdown content.
  • Plaintext exists only in Obsidian and briefly in plugin memory during encryption.
  • Bidirectional sync with LWW (Last-Writer-Wins) conflict resolution.

Features

FeatureStatus
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

Project Structure

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

Installation

  1. Community plugin store (once approved): Settings → Community plugins → Browse → search for "Hermes R2 Vault" → Install → Enable.

  2. Manual install: copy main.js, manifest.json, and styles.css to <vault>/.obsidian/plugins/hermes-rvault/.

  3. Enable the plugin in Obsidian settings.

  4. Configure the API Base URL, Vault ID, and secret names.

  5. Run "Initialize encrypted vault" to generate the master key.

  6. Store the access token via the settings tab (SecretStorage).

  7. Run "Test connection" to verify connectivity.

  8. 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.

Configuration

Plugin settings are available under Hermes R2 Vault Settings in Obsidian settings.

General Settings

SettingDescriptionDefault
API Base URLControl API URL (HTTPS)
Vault IDVault UUID
Device IDDevice identifier (auto-generated)randomUUID()
Access Token Secret NameSecretStorage key name
Auto-uploadAutomatically upload changesfalse
Debounce delay (ms)Change processing delay1500
Max concurrent uploadsParallel upload limit2

Deletion Safety

SettingDescriptionDefault
Enable remote trashKeep deleted files in manifesttrue
Deleted retention (days)Deleted metadata retention period90
Mass deletion thresholdDeletion count threshold10
Require deletion approvalPrompt for confirmationtrue
Empty manifest protectionBlock sync on empty remote manifesttrue
Stale device guardBlock stale device synctrue
Pre-deletion snapshotCapture hash before deletionfalse

Revision History

SettingDescriptionDefault
Revision history enabledEnable revision historyfalse
Max revisions per fileMax revisions per file100
Max snapshot generationsMax snapshot count50
Revision TTL (days)Revision time-to-live365
Revision retention (days)Guaranteed retention period90
Minimum retained revisionsMinimum revisions to keep20

Commands

CommandDescription
Test connectionVerify API connectivity
Initialize encrypted vaultGenerate vault master key
Recover vault keyRecover key from passphrase
Change recovery passphraseChange recovery passphrase
Upload current noteUpload active note
Run full scanFull file scan
Pause automatic uploadsPause upload queue
Resume automatic uploadsResume upload queue
Show sync statusDisplay sync status
View sync conflictsShow sync conflicts
Open Conflict CenterOpen conflict resolution UI
Undo latest deletionUndo last deletion
Purge deleted fileSafe permanent purge
Preview retention planRetention plan preview
Apply retention planExecute retention GC
List vault restore pointsList available snapshots
Preview vault restorePreview vault state restore
Scan history integrityCheck history integrity
Show deletion safety metricsDisplay deletion statistics
Show recent faultsShow recent fault records
Clear local sync metadataClear local sync state

Sync Protocol

Full protocol specification: docs/sync-protocol-v1.md.

Protocol version: 1 (marker: docs/protocol-version-v1.json)

Feature Flags

FlagStatus
revisionHistory✅ true
fileRevisionRestore✅ true
pointInTimeRestore✅ true
historyIntegrityScan✅ true
retentionPlanner✅ true
safeGarbageCollection✅ true
resumableRestore✅ true
hermesReadWrite❌ false

R2 Layout

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

Security

  • Encryption: XChaCha20-Poly1305 with 24-byte nonce
  • KDF: Argon2id (64MB memory, 3 iterations)
  • Key derivation: HKDF-SHA256 (path key, content key)
  • Key exchange: X25519 ECDH
  • Authentication: HMAC-SHA256 for path identity

Details: docs/security-model.md, docs/threat-model.md.

Development

Scripts

CommandDescription
npm run buildBuild plugin (esbuild)
npm run testRun tests (Vitest)
npm run test:coverageCoverage report
npm run typecheckTypeScript type check
npm run lintESLint

Dependencies

  • Runtime: @noble/ciphers, @noble/curves, @noble/hashes
  • Dev: TypeScript, esbuild, Vitest, ESLint, Obsidian API

Python Resolver

The hermes-r2-file-resolver/ subproject implements the protocol in Python for the Hermes VPS backend.

Testing

Before committing:

npm run typecheck   # TypeScript — clean
npm run lint        # ESLint — clean
npm run test        # 379 tests, 21 files
npm run build       # esbuild — success

License

MIT

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.