Architecture
Swarm ID uses an OAuth-style popup authentication flow. Chrome and Firefox work out of the box; where the browser partitions the proxy iframe’s storage — Safari’s Intelligent Tracking Prevention, or strict privacy settings elsewhere — the connect popup hands the iframe the account’s upload credentials directly, so uploads keep working. See #277 for the background.
System Overview
Section titled “System Overview”The architecture consists of three main components:
┌─────────────────────────────────────────────────────────────┐│ Your dApp (e.g., https://my-dapp.com) ││ ┌────────────────────────────────────────────────────┐ ││ │ SwarmIdClient │ ││ │ │ ││ │ ┌─────────────────────────────────────────────┐ │ ││ │ │ Hidden iframe (https://swarm-id.snaha.net) │ │ ││ │ │ │ │ ││ │ │ SwarmIdProxy │ │ ││ │ │ - Stores app-specific secret │ │ ││ │ │ - Proxies Bee API calls │ │ ││ │ └─────────────────────────────────────────────┘ │ ││ └────────────────────────────────────────────────────┘ ││ │ ││ │ Opens popup ││ ↓ ││ ┌────────────────────────────────────────────────────┐ ││ │ Auth Popup (https://swarm-id.snaha.net) │ ││ │ │ ││ │ Auth Popup (SvelteKit /connect route) │ ││ │ - Stores master key (first-party context) │ ││ │ - Derives app-specific secrets │ ││ │ - Sends secret to iframe via postMessage │ ││ └────────────────────────────────────────────────────┘ │└─────────────────────────────────────────────────────────────┘Components
Section titled “Components”SwarmIdClient
Section titled “SwarmIdClient”The main library used by parent dApp windows. It:
- Creates and embeds a hidden iframe from the trusted domain
- Provides authentication UI (login button, status)
- Exposes methods for Bee operations (upload, download, etc.)
- Communicates with the iframe via postMessage
SwarmIdProxy
Section titled “SwarmIdProxy”Runs inside the hidden iframe on the trusted domain. It:
- Receives app-specific secrets from the auth popup
- Stores secrets in partitioned localStorage
- Proxies Bee API calls with authentication
- Validates all incoming messages from the parent
Auth Popup
Section titled “Auth Popup”The authentication popup is implemented as a SvelteKit route (/connect) in the identity management UI. It:
- Manages the master key in first-party localStorage
- Derives app-specific secrets using HMAC-SHA256
- Sends secrets to the iframe via postMessage
- Provides the authentication UI (Passkey/WebAuthn and SIWE flows)
Key Hierarchy
Section titled “Key Hierarchy”All cryptographic keys are derived from a single master key produced during Passkey or SIWE authentication:
Master Key (from Passkey/SIWE challenge)├─> App-Specific Secret (HMAC-SHA256 with app origin)│ ├─> Low-stakes keys (feed, session) → shared with apps│ └─> High-stakes keys (stamps, ACT) → never shared├─> derivationKey (HMAC-SHA256 with "derivation-key", persisted in account)│ ├─> swarmEncryptionKey (HMAC-SHA256 with "swarm-encryption")│ │ ├─> backup AES key (HMAC-SHA256 with "swarm-id-backup-encryption-v1")│ │ │ └─> AES-GCM-256 CryptoKey (for .swarmid encryption)│ │ └─> backup feed key (HMAC-SHA256 with "backup-key")│ │ └─> backup private key (for Swarm epoch feed owner)│ └─> postageSignerKey (HMAC-SHA256 with "postage-signer" or "postage-signer:{identityId}")│ └─> PrivateKey for signing postage batch chunksApp Secret Derivation
Section titled “App Secret Derivation”// Pseudocode for key derivationappSecret = HMAC - SHA256(masterKey, appOrigin)This ensures:
- Deterministic - Same master key + app origin always produces the same secret
- Unique per app - Each app gets a different secret
- No cross-app leakage - Knowing one app’s secret doesn’t reveal others
- Master key protection - The master key never leaves the trusted domain
Storage Partitioning
Section titled “Storage Partitioning”Modern browsers partition storage by (iframe-origin, parent-origin). This means:
- An iframe from
swarm-id.snaha.netembedded inapp-a.comhas different storage than the same iframe embedded inapp-b.com - This provides browser-enforced isolation between apps
- Even if an app is compromised, it can’t access other apps’ secrets
Message Protocol
Section titled “Message Protocol”All cross-origin communication uses postMessage with Zod validation:
Parent → Iframe Messages
Section titled “Parent → Iframe Messages”| Message | Purpose |
|---|---|
parentIdentify |
Identify parent to iframe |
checkAuth |
Check authentication status |
requestAuth |
Request authentication (open popup) |
uploadData |
Upload data to Swarm |
downloadData |
Download data from Swarm |
Iframe → Parent Messages
Section titled “Iframe → Parent Messages”| Message | Purpose |
|---|---|
proxyReady |
Iframe is initialized |
authStatusResponse |
Authentication status |
authSuccess |
Authentication completed |
uploadDataResponse |
Upload result |
downloadDataResponse |
Download result |
error |
Error occurred |
Popup → Iframe Messages
Section titled “Popup → Iframe Messages”| Message | Purpose |
|---|---|
setSecret |
Send derived app-specific secret |
Backup & Recovery
Section titled “Backup & Recovery”Swarm ID supports account backup via encrypted .swarmid files and automatic Swarm-based sync.
.swarmid File Format
Section titled “.swarmid File Format”Each .swarmid file is a JSON object with two parts:
- Plaintext header — account metadata visible without decryption (account name, export timestamp, account type, and type-specific fields)
- Encrypted payload — AES-GCM-256 encrypted
AccountStateSnapshotcontaining identities, connected apps, and postage stamps
Header fields vary by account type:
| Account Type | Extra Header Fields |
|---|---|
| Passkey | credentialId |
| Ethereum | ethereumAddress |
| Agent | (none) |
During Ethereum account import, the user must re-enter their secret seed. The master key is re-derived from secretSeed + publicKey — key material is never stored in the backup header.
Export Flow
Section titled “Export Flow”Export requires no re-authentication — the derivationKey is already persisted in the account. The flow:
- Serialize account state via
serializeAccountStateSnapshot()(stripsappSecret) - Derive
swarmEncryptionKeyfromderivationKeyvia HMAC-SHA256 with context"swarm-encryption" - Derive AES-GCM-256 key:
swarmEncryptionKey→ HMAC-SHA256 with context"swarm-id-backup-encryption-v1" - Encrypt with random 12-byte IV
- Build header with account metadata + ciphertext
Import Flow
Section titled “Import Flow”Import requires authentication to re-derive the encryption key:
- Parse the plaintext header to identify account type and metadata
- User authenticates (passkey or wallet) to obtain
masterKey - Derive
derivationKey→swarmEncryptionKey→backupKey→ AES-GCM-256 key - Decrypt and validate the payload via
deserializeAccountStateSnapshot()
Swarm Restore Flow
Section titled “Swarm Restore Flow”When a passkey auth succeeds on a new device with no local account:
- Derive
derivationKeyfrommasterKey - Derive
swarmEncryptionKeyfromderivationKey - Derive
backupKey→ epoch feed owner private key - Build feed topic from account ID
- Look up latest epoch feed entry in Swarm
- Download and decrypt the account snapshot
Security: appSecret Exclusion
Section titled “Security: appSecret Exclusion”The appSecret field is structurally excluded from ExportedConnectedAppSchemaV1. Even if raw data contains an appSecret, Zod strips it during parsing. App secrets are re-derived on-demand from the master key — they are never stored in backups.
Security Considerations
Section titled “Security Considerations”What’s Protected
Section titled “What’s Protected”- Master key - Only stored in first-party context on trusted domain
- App secrets - Derived per-app, stored in partitioned storage
- Cross-app isolation - Browser-enforced through storage partitioning
Attack Vectors Mitigated
Section titled “Attack Vectors Mitigated”- XSS in dApp - Can’t access master key (different origin)
- Malicious dApp - Only gets its own derived secret
- Man-in-the-middle - HTTPS required for all communication
- Message spoofing - All messages validated with Zod schemas
Recommendations
Section titled “Recommendations”- Always use HTTPS in production
- Validate the
iframeOriginmatches your trusted domain - Keep the trusted domain secure and audited
- Use Content Security Policy to restrict iframe embedding