Garland: Exploring the Architecture

Max

npub1klkk3vrzme455yh9rl2jshq7rc8dpegj3ndf82c3ks2sk40dxt7qulx3vt

hex

ef658034a53c947344b82816177d0f2b8795758715bc51fa943d25a9bae1aecd

nevent

nevent1qqsw7evqxjjne9rngjuzs9sh058jhpu4wkr3t0z3l22r6fdfhts6angprpmhxue69uhhyetvv9ujuem4d36kwatvw5hx6mm9qgst0mtgkp3du662ztj3l4fgts0purksu5fgek5n4vgmg9gt2hkn9lq522fhr

naddr

naddr1qqgrwvnzxgcxgvfev3jxzetrx9jrvqgcwaehxw309aex2mrp0yhxwatvw4nh2mr49ekk7egzyzm7669svt0xkjsju50a22zurc0qa589z2xd4yatzx6p2z64a5e0cqcyqqq823cq5klwh

Kind-30023 (Article)

2025-12-01T07:27:27Z

In our previous post we introduced Garland: an S3-style virtual drive over blossom that gives you complete data sovereignty. Every file recoverable with just your nsec.

nostr:naddr1qqgrsde5xcexzvny8qerjvf3vgmkxqghwaehxw309aex2mrp0yhxz7n6v9kk7tnwv46z7q3qklkk3vrzme455yh9rl2jshq7rc8dpegj3ndf82c3ks2sk40dxt7qxpqqqp65wkk7fek

Today we're sharing an architecture we're considering - and we want your feedback before we commit to it.

The proposed stack

  • Encryption: Cryptomator (via yombar, the Rust implementation)
  • Blob storage: Blossom servers
  • State/metadata: Nostr events

The key insight

Cryptomator expects a password. We give it your nsec.

Cryptomator runs scrypt on passwords to derive encryption keys - that's designed to slow down brute-force attacks on weak passwords. Your nsec is already 256 bits of entropy, so the scrypt step is overkill. But it's harmless, and it means we can use yombar completely unmodified.

nsec + optional passphrase
        ↓
  Cryptomator password
        ↓
  Standard vault encryption

Data Flow

Saving Files

  1. User modifies files locally
  2. Cryptomator encrypts to .c9r format (32 KiB chunks, AES-256-GCM)
  3. Each .c9r uploads to Blossom as a blob
  4. Build manifest: { path: "/photos/dog.jpg", blob: "sha256:abc...", size: 524288 }
  5. NIP-44 encrypt manifest + vault config with nsec
  6. Publish to Nostr relays

Recovery

  1. Fetch Nostr events with nsec
  2. NIP-44 decrypt to get manifest + vault config
  3. Fetch blobs from Blossom servers listed in manifest
  4. Open vault with nsec (+ passphrase)
  5. Decrypt files

Recovery requires no local database, no device keys, and no additional state beyond your nsec.

What Cryptomator gives us

| Concern | Solution | |---------|----------| | Chunk encryption | 32 KiB chunks, AES-256-GCM | | Chunk integrity | AAD binding prevents reordering | | Filename privacy | AES-SIV encryption | | Key derivation | scrypt (N=32768, r=8) | | Vault integrity | HMAC-SHA256 signed JWT |

Audited cryptography (Cure53, 2017). No need to reinvent it.

What we'd build

  1. Blossom backend for yombar - Upload/download encrypted files as blobs
  2. Nostr manifest layer - Store file tree + blob locations in events
  3. NIP-44 wrapper - Encrypt vault config for relay storage

The encryption code stays untouched. We're adding storage backends, not forking crypto.

File Mapping

Cryptomator normally outputs files to paths like:

d/BZ/R4VZSS5.../ENCRYPTED.c9r

We flatten this. Each .c9r becomes a Blossom blob addressed by hash. The directory structure lives in the Nostr manifest, not in blob paths.

Recovery Guarantees

A user with only:

  • Their nsec
  • Optional passphrase
  • Access to any Nostr relay with their events
  • Access to the Blossom servers referenced in manifest

...can recover all files from a fresh device.

What do you think?

We're not committed to this approach yet. Some open questions:

  • Is reusing Cryptomator the right call, or are we inheriting unnecessary complexity?
  • Should chunks be individual Blossom blobs, or is one blob per file simpler?
  • Are there edge cases in the nsec-as-password approach we're missing?

If you see holes in this architecture or have experience with Cryptomator and yombar, we'd love to hear from you. Reach out on Nostr or open an issue on GitHub.

Raw JSON

{
  "kind": 30023,
  "id": "ef658034a53c947344b82816177d0f2b8795758715bc51fa943d25a9bae1aecd",
  "pubkey": "b7ed68b062de6b4a12e51fd5285c1e1e0ed0e5128cda93ab11b4150b55ed32fc",
  "created_at": 1777543606,
  "tags": [
    [
      "d",
      "72b20d19ddaec1d6"
    ],
    [
      "image",
      "https://image.nostr.build/8452b72adcf67eabca7f7fe44df3cb42d188e1f226b53b9d390582bd0ea30128.jpg"
    ],
    [
      "title",
      "Garland: Exploring the Architecture"
    ],
    [
      "summary",
      "We're exploring an architecture for Garland: Cryptomator for encryption, Blossom for blob storage, Nostr for state. Your nsec becomes your Cryptomator password. We think this works - but we'd love your feedback before we commit."
    ],
    [
      "published_at",
      "1764574047"
    ],
    [
      "t",
      "austrian-economics"
    ],
    [
      "t",
      "freedom-tech"
    ],
    [
      "t",
      "garland"
    ],
    [
      "t",
      "blossom"
    ],
    [
      "t",
      "nostr"
    ],
    [
      "t",
      "encryption"
    ],
    [
      "t",
      "cryptography"
    ],
    [
      "t",
      "self-sovereignty"
    ],
    [
      "t",
      "privacy"
    ],
    [
      "t",
      "cryptomator"
    ],
    [
      "t",
      "file-storage"
    ],
    [
      "t",
      "rfc"
    ]
  ],
  "content": "In our previous post we introduced Garland: an S3-style virtual drive over blossom that gives you complete data sovereignty. Every file recoverable with just your nsec.\n\nnostr:naddr1qqgrsde5xcexzvny8qerjvf3vgmkxqghwaehxw309aex2mrp0yhxz7n6v9kk7tnwv46z7q3qklkk3vrzme455yh9rl2jshq7rc8dpegj3ndf82c3ks2sk40dxt7qxpqqqp65wkk7fek\n\nToday we're sharing an architecture we're considering - and we want your feedback before we commit to it.\n\n## The proposed stack\n\n- **Encryption:** Cryptomator (via yombar, the Rust implementation)\n- **Blob storage:** Blossom servers\n- **State/metadata:** Nostr events\n\n## The key insight\n\nCryptomator expects a password. We give it your nsec.\n\nCryptomator runs scrypt on passwords to derive encryption keys - that's designed to slow down brute-force attacks on weak passwords. Your nsec is already 256 bits of entropy, so the scrypt step is overkill. But it's harmless, and it means we can use yombar completely unmodified.\n\n```\nnsec + optional passphrase\n        ↓\n  Cryptomator password\n        ↓\n  Standard vault encryption\n```\n\n## Data Flow\n\n### Saving Files\n\n1. User modifies files locally\n2. Cryptomator encrypts to `.c9r` format (32 KiB chunks, AES-256-GCM)\n3. Each `.c9r` uploads to Blossom as a blob\n4. Build manifest: `{ path: \"/photos/dog.jpg\", blob: \"sha256:abc...\", size: 524288 }`\n5. NIP-44 encrypt manifest + vault config with nsec\n6. Publish to Nostr relays\n\n### Recovery\n\n1. Fetch Nostr events with nsec\n2. NIP-44 decrypt to get manifest + vault config\n3. Fetch blobs from Blossom servers listed in manifest\n4. Open vault with nsec (+ passphrase)\n5. Decrypt files\n\nRecovery requires no local database, no device keys, and no additional state beyond your nsec.\n\n## What Cryptomator gives us\n\n| Concern | Solution |\n|---------|----------|\n| Chunk encryption | 32 KiB chunks, AES-256-GCM |\n| Chunk integrity | AAD binding prevents reordering |\n| Filename privacy | AES-SIV encryption |\n| Key derivation | scrypt (N=32768, r=8) |\n| Vault integrity | HMAC-SHA256 signed JWT |\n\nAudited cryptography (Cure53, 2017). No need to reinvent it.\n\n## What we'd build\n\n1. **Blossom backend for yombar** - Upload/download encrypted files as blobs\n2. **Nostr manifest layer** - Store file tree + blob locations in events  \n3. **NIP-44 wrapper** - Encrypt vault config for relay storage\n\nThe encryption code stays untouched. We're adding storage backends, not forking crypto.\n\n## File Mapping\n\nCryptomator normally outputs files to paths like:\n```\nd/BZ/R4VZSS5.../ENCRYPTED.c9r\n```\n\nWe flatten this. Each `.c9r` becomes a Blossom blob addressed by hash. The directory structure lives in the Nostr manifest, not in blob paths.\n\n## Recovery Guarantees\n\nA user with only:\n- Their nsec\n- Optional passphrase  \n- Access to any Nostr relay with their events\n- Access to the Blossom servers referenced in manifest\n\n...can recover all files from a fresh device.\n\n## What do you think?\n\nWe're not committed to this approach yet. Some open questions:\n\n- Is reusing Cryptomator the right call, or are we inheriting unnecessary complexity?\n- Should chunks be individual Blossom blobs, or is one blob per file simpler?\n- Are there edge cases in the nsec-as-password approach we're missing?\n\nIf you see holes in this architecture or have experience with Cryptomator and yombar, we'd love to hear from you. Reach out on Nostr or open an issue on GitHub.",
  "sig": "9b1ef958bc9829eb71b2b11fc75689d65c6b25b84b981a22f68a7b323d4cd6aaaa1bb93d3d0aa4d2ee11dff1c0b7f2a0934246796ca4212361a6fc897cbaba28"
}