integration guides + artist mode polish
- Add docs/ipfs-distribution.md: complete guide for IPFS hosting
- Installation, pinning services, gateways, best practices
- Full album release workflow example
- Add docs/payment-integration.md: Stripe, Gumroad, PayPal examples
- Webhook handlers for automated license delivery
- Serverless options (Vercel/Netlify)
- Manual workflow for non-technical artists
- Demo artist mode improvements:
- WASM loads on-demand (fixes 6s delay on 4G)
- Generate button enabled by password only
- Vi demo preloads when WASM ready
- Update RFC-001 section 8.3: mark completed items
33 KiB
RFC-001: Open Source DRM for Independent Artists
Status: Proposed Author: Snider Created: 2026-01-10 License: EUPL-1.2
Revision History
| Date | Status | Notes |
|---|---|---|
| 2026-01-12 | Proposed | Chunked streaming: v3 now supports optional ChunkSize for independently decryptable chunks - enables seek, HTTP Range, and decrypt-while-downloading. |
| 2026-01-12 | Proposed | v3 Streaming: LTHN rolling keys with configurable cadence (daily/12h/6h/1h). CEK wrapping for zero-trust streaming. WASM v1.3.0 with decryptV3(). |
| 2026-01-10 | Proposed | Technical review passed. Fixed section numbering (7.x, 8.x, 9.x, 11.x). Updated WASM size to 5.9MB. Implementation verified complete for stated scope. |
Abstract
This RFC describes an open-source Digital Rights Management (DRM) system designed for independent artists to distribute encrypted media directly to fans without platform intermediaries. The system uses ChaCha20-Poly1305 authenticated encryption with a "password-as-license" model, enabling zero-trust distribution where the encryption key serves as both the license and the decryption mechanism.
1. Motivation
1.1 The Problem
Traditional music distribution forces artists into platforms that:
- Take 30-70% of revenue (Spotify, Apple Music, Bandcamp)
- Control the relationship between artist and fan
- Require ongoing subscription for access
- Can delist content unilaterally
Existing DRM systems (Widevine, FairPlay) require:
- Platform integration and licensing fees
- Centralized key servers
- Proprietary implementations
- Trust in third parties
1.2 The Solution
A DRM system where:
- The password IS the license - no key servers, no escrow
- Artists keep 100% - sell direct, any payment processor
- Host anywhere - CDN, IPFS, S3, personal server
- Browser or native - same encryption, same content
- Open source - auditable, forkable, community-owned
2. Design Philosophy
2.1 "Honest DRM"
Traditional DRM operates on a flawed premise: that sufficiently complex technology can prevent copying. History proves otherwise—every DRM system has been broken. The result is systems that:
- Punish paying customers with restrictions
- Get cracked within days/weeks anyway
- Require massive infrastructure (key servers, license servers)
- Create single points of failure
This system embraces a different philosophy: DRM for honest people.
The goal isn't to stop determined pirates (impossible). The goal is:
- Make the legitimate path easy and pleasant
- Make casual sharing slightly inconvenient
- Create a social/economic deterrent (sharing = giving away money)
- Remove all friction for paying customers
2.2 Password-as-License
The password IS the license. This is not a limitation—it's the core innovation.
Traditional DRM:
Purchase → License Server → Device Registration → Key Exchange → Playback
(5 steps, 3 network calls, 2 points of failure)
dapp.fm:
Purchase → Password → Playback
(2 steps, 0 network calls, 0 points of failure)
Benefits:
- No accounts - No email harvesting, no password resets, no data breaches
- No servers - Artist can disappear; content still works forever
- No revocation anxiety - You bought it, you own it
- Transferable - Give your password to a friend (like lending a CD)
- Archival - Works in 50 years if you have the password
2.3 Encryption as Access Control
We use military-grade encryption (ChaCha20-Poly1305) not because we need military-grade security, but because:
- It's fast (important for real-time media)
- It's auditable (open standard, RFC 8439)
- It's already implemented everywhere (Go stdlib, browser crypto)
- It provides authenticity (Poly1305 MAC prevents tampering)
The threat model isn't nation-states—it's casual piracy. The encryption just needs to be "not worth the effort to crack for a $10 album."
3. Architecture
3.1 System Components
┌─────────────────────────────────────────────────────────────┐
│ DISTRIBUTION LAYER │
│ CDN / IPFS / S3 / GitHub / Personal Server │
│ (Encrypted .smsg files - safe to host anywhere) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ PLAYBACK LAYER │
│ ┌─────────────────┐ ┌─────────────────────────────┐ │
│ │ Browser Demo │ │ Native Desktop App │ │
│ │ (WASM) │ │ (Wails + Go) │ │
│ │ │ │ │ │
│ │ ┌───────────┐ │ │ ┌───────────────────────┐ │ │
│ │ │ stmf.wasm │ │ │ │ Go SMSG Library │ │ │
│ │ │ │ │ │ │ (pkg/smsg) │ │ │
│ │ │ ChaCha20 │ │ │ │ │ │ │
│ │ │ Poly1305 │ │ │ │ ChaCha20-Poly1305 │ │ │
│ │ └───────────┘ │ │ └───────────────────────┘ │ │
│ └─────────────────┘ └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ LICENSE LAYER │
│ Password = License Key = Decryption Key │
│ (Sold via Gumroad, Stripe, PayPal, Crypto, etc.) │
└─────────────────────────────────────────────────────────────┘
3.2 SMSG Container Format
See: examples/formats/smsg-format.md
Key properties:
- Magic number: "SMSG" (0x534D5347)
- Algorithm: ChaCha20-Poly1305 (authenticated encryption)
- Format: v1 (JSON+base64) or v2 (binary, 25% smaller)
- Compression: zstd (default), gzip, or none
- Manifest: Unencrypted metadata (title, artist, license, expiry, links)
- Payload: Encrypted media with attachments
Format Versions
| Format | Payload Structure | Size | Speed | Use Case |
|---|---|---|---|---|
| v1 | JSON with base64-encoded attachments | +33% overhead | Baseline | Legacy |
| v2 | Binary header + raw attachments + zstd | ~Original size | 3-10x faster | Download-to-own |
| v3 | CEK + wrapped keys + rolling LTHN | ~Original size | 3-10x faster | Streaming |
| v3+chunked | v3 with independently decryptable chunks | ~Original size | Seekable | Chunked streaming |
v2 is recommended for download-to-own (perpetual license). v3 is recommended for streaming (time-limited access). v3 with chunking is recommended for large files requiring seek capability or decrypt-while-downloading.
3.3 Key Derivation (v1/v2)
License Key (password)
│
▼
SHA-256 Hash
│
▼
32-byte Symmetric Key
│
▼
ChaCha20-Poly1305 Decryption
Simple, auditable, no key escrow.
Note on password hashing: SHA-256 is used for simplicity and speed. For high-value content, artists may choose to use stronger KDFs (Argon2, scrypt) in custom implementations. The format supports algorithm negotiation via the header.
3.4 Streaming Key Derivation (v3)
v3 format uses LTHN rolling keys for zero-trust streaming. The platform controls key refresh cadence.
┌──────────────────────────────────────────────────────────────────┐
│ v3 STREAMING KEY FLOW │
├──────────────────────────────────────────────────────────────────┤
│ │
│ SERVER (encryption time): │
│ ───────────────────────── │
│ 1. Generate random CEK (Content Encryption Key) │
│ 2. Encrypt content with CEK (one-time) │
│ 3. For current period AND next period: │
│ streamKey = SHA256(LTHN(period:license:fingerprint)) │
│ wrappedKey = ChaCha(CEK, streamKey) │
│ 4. Store wrapped keys in header (CEK never transmitted) │
│ │
│ CLIENT (decryption time): │
│ ──────────────────────── │
│ 1. Derive streamKey = SHA256(LTHN(period:license:fingerprint)) │
│ 2. Try to unwrap CEK from current period key │
│ 3. If fails, try next period key │
│ 4. Decrypt content with unwrapped CEK │
│ │
└──────────────────────────────────────────────────────────────────┘
LTHN Hash Function
LTHN is rainbow-table resistant because the salt is derived from the input itself:
LTHN(input) = SHA256(input + reverse_leet(input))
where reverse_leet swaps: o↔0, l↔1, e↔3, a↔4, s↔z, t↔7
Example:
LTHN("2026-01-12:license:fp")
= SHA256("2026-01-12:license:fp" + "pf:3zn3ci1:21-10-6202")
You cannot compute the hash without knowing the original input.
Cadence Options
The platform chooses the key refresh rate. Faster cadence = tighter access control.
| Cadence | Period Format | Rolling Window | Use Case |
|---|---|---|---|
daily |
2026-01-12 |
24-48 hours | Standard streaming |
12h |
2026-01-12-AM/PM |
12-24 hours | Premium content |
6h |
2026-01-12-00/06/12/18 |
6-12 hours | High-value content |
1h |
2026-01-12-15 |
1-2 hours | Live events |
The rolling window ensures smooth key transitions. At any time, both the current period key AND the next period key are valid.
Zero-Trust Properties
- Server never stores keys - Derived on-demand from LTHN
- Keys auto-expire - No revocation mechanism needed
- Sharing keys is pointless - They expire within the cadence window
- Fingerprint binds to device - Different device = different key
- License ties to user - Different user = different key
3.5 Chunked Streaming (v3 with ChunkSize)
When StreamParams.ChunkSize > 0, v3 format splits content into independently decryptable chunks, enabling:
- Decrypt-while-downloading - Play media as chunks arrive
- HTTP Range requests - Fetch specific chunks by byte offset
- Seekable playback - Jump to any position without decrypting previous chunks
┌──────────────────────────────────────────────────────────────────┐
│ V3 CHUNKED FORMAT │
├──────────────────────────────────────────────────────────────────┤
│ │
│ Header (cleartext): │
│ format: "v3" │
│ chunked: { │
│ chunkSize: 1048576, // 1MB default │
│ totalChunks: N, │
│ totalSize: X, // unencrypted total │
│ index: [ // for HTTP Range / seeking │
│ { offset: 0, size: Y }, │
│ { offset: Y, size: Z }, │
│ ... │
│ ] │
│ } │
│ wrappedKeys: [...] // same as non-chunked v3 │
│ │
│ Payload: │
│ [chunk 0: nonce + encrypted + tag] │
│ [chunk 1: nonce + encrypted + tag] │
│ ... │
│ [chunk N: nonce + encrypted + tag] │
│ │
└──────────────────────────────────────────────────────────────────┘
Key insight: Each chunk is encrypted with the same CEK but gets its own random nonce, making chunks independently decryptable. The chunk index in the header enables:
- Seeking: Calculate which chunk contains byte offset X, fetch just that chunk
- Range requests: Use HTTP Range headers to fetch specific encrypted chunks
- Streaming: Decrypt chunk 0 for metadata, then stream chunks 1-N as they arrive
Usage example:
params := &StreamParams{
License: "user-license",
Fingerprint: "device-fp",
ChunkSize: 1024 * 1024, // 1MB chunks
}
// Encrypt with chunking
encrypted, _ := EncryptV3(msg, params, manifest)
// For streaming playback:
header, _ := GetV3Header(encrypted)
cek, _ := UnwrapCEKFromHeader(header, params)
payload, _ := GetV3Payload(encrypted)
for i := 0; i < header.Chunked.TotalChunks; i++ {
chunk, _ := DecryptV3Chunk(payload, cek, i, header.Chunked)
player.Write(chunk) // Stream to audio/video player
}
3.6 Supported Content Types
SMSG is content-agnostic. Any file can be an attachment:
| Type | MIME | Use Case |
|---|---|---|
| Audio | audio/mpeg, audio/flac, audio/wav | Music, podcasts |
| Video | video/mp4, video/webm | Music videos, films |
| Images | image/png, image/jpeg | Album art, photos |
| Documents | application/pdf | Liner notes, lyrics |
| Archives | application/zip | Multi-file releases |
| Any | application/octet-stream | Anything else |
Multiple attachments per SMSG are supported (e.g., album + cover art + PDF booklet).
4. Demo Page Architecture
Live Demo: https://demo.dapp.fm
4.1 Components
demo/
├── index.html # Single-page application
├── stmf.wasm # Go WASM decryption module (~5.9MB)
├── wasm_exec.js # Go WASM runtime
├── demo-track.smsg # Sample encrypted content (v2/zstd)
└── profile-avatar.jpg # Artist avatar
4.2 UI Modes
The demo has three modes, accessible via tabs:
| Mode | Purpose | Default |
|---|---|---|
| Profile | Artist landing page with auto-playing content | Yes |
| Fan | Upload and decrypt purchased .smsg files | No |
| Artist | Re-key content, create new packages | No |
4.3 Profile Mode (Default)
┌─────────────────────────────────────────────────────────────┐
│ dapp.fm [Profile] [Fan] [Artist] │
├─────────────────────────────────────────────────────────────┤
│ Zero-Trust DRM ⚠️ Demo pre-seeded with keys │
├─────────────────────────────────────────────────────────────┤
│ [No Middlemen] [No Fees] [Host Anywhere] [Browser/Native] │
├─────────────────┬───────────────────────────────────────────┤
│ SIDEBAR │ MAIN CONTENT │
│ ┌───────────┐ │ ┌─────────────────────────────────────┐ │
│ │ Avatar │ │ │ 🛒 Buy This Track on Beatport │ │
│ │ │ │ │ 95%-100%* goes to the artist │ │
│ │ Artist │ │ ├─────────────────────────────────────┤ │
│ │ Name │ │ │ │ │
│ │ │ │ │ VIDEO PLAYER │ │
│ │ Links: │ │ │ (auto-starts at 1:08) │ │
│ │ Beatport │ │ │ with native controls │ │
│ │ Spotify │ │ │ │ │
│ │ YouTube │ │ ├─────────────────────────────────────┤ │
│ │ etc. │ │ │ About the Artist │ │
│ └───────────┘ │ │ (Bio text) │ │
│ │ └─────────────────────────────────────┘ │
├─────────────────┴───────────────────────────────────────────┤
│ GitHub · EUPL-1.2 · Viva La OpenSource 💜 │
└─────────────────────────────────────────────────────────────┘
4.4 Decryption Flow
User clicks "Play Demo Track"
│
▼
fetch(demo-track.smsg)
│
▼
Convert to base64 ◄─── CRITICAL: Must handle binary vs text format
│ See: examples/failures/001-double-base64-encoding.md
▼
BorgSMSG.getInfo(base64)
│
▼
Display manifest (title, artist, license)
│
▼
BorgSMSG.decryptStream(base64, password)
│
▼
Create Blob from Uint8Array
│
▼
URL.createObjectURL(blob)
│
▼
<audio> or <video> element plays content
4.5 Fan Unlock Tab
Allows fans to:
- Upload any
.smsgfile they purchased - Enter their license key (password)
- Decrypt and play locally
No server communication - everything in browser.
5. Artist Portal (License Manager)
The License Manager (js/borg-stmf/artist-portal.html) is the artist-facing tool for creating and issuing licenses.
5.1 Workflow
┌─────────────────────────────────────────────────────────────┐
│ ARTIST PORTAL │
├─────────────────────────────────────────────────────────────┤
│ 1. Upload Content │
│ - Drag/drop audio or video file │
│ - Or use demo content for testing │
├─────────────────────────────────────────────────────────────┤
│ 2. Define Track List (CD Mastering) │
│ - Track titles │
│ - Start/end timestamps → chapter markers │
│ - Mix types (full, intro, chorus, drop, etc.) │
├─────────────────────────────────────────────────────────────┤
│ 3. Configure License │
│ - Perpetual (own forever) │
│ - Rental (time-limited) │
│ - Streaming (24h access) │
│ - Preview (30 seconds) │
├─────────────────────────────────────────────────────────────┤
│ 4. Generate License │
│ - Auto-generate token or set custom │
│ - Token encrypts content with manifest │
│ - Download .smsg file │
├─────────────────────────────────────────────────────────────┤
│ 5. Distribute │
│ - Upload .smsg to CDN/IPFS/S3 │
│ - Sell license token via payment processor │
│ - Fan receives token, downloads .smsg, plays │
└─────────────────────────────────────────────────────────────┘
5.2 License Types
| Type | Duration | Use Case |
|---|---|---|
| Perpetual | Forever | Album purchase, own forever |
| Rental | 7-90 days | Limited edition, seasonal content |
| Streaming | 24 hours | On-demand streaming model |
| Preview | 30 seconds | Free samples, try-before-buy |
5.3 Track List as Manifest
The artist defines tracks like mastering a CD:
{
"tracks": [
{"title": "Intro", "start": 0, "end": 45, "type": "intro"},
{"title": "Main Track", "start": 45, "end": 240, "type": "full"},
{"title": "The Drop", "start": 120, "end": 180, "type": "drop"},
{"title": "Outro", "start": 240, "end": 300, "type": "outro"}
]
}
Same master file, different licensed "cuts":
- Full Album: All tracks, perpetual
- Radio Edit: Tracks 2-3 only, rental
- DJ Extended: Loop points enabled, perpetual
- Preview: First 30 seconds, expires immediately
5.4 Stats Dashboard
The Artist Portal tracks:
- Total licenses issued
- Potential revenue (based on entered prices)
- 100% cut (reminder: no platform fees)
6. Economic Model
6.1 The Offer
Self-host for 0%. Let us host for 5%.
That's it. No hidden fees, no per-stream calculations, no "recoupable advances."
| Option | Cut | What You Get |
|---|---|---|
| Self-host | 0% | Tools, format, documentation. Host on your own CDN/IPFS/server |
| dapp.fm hosted | 5% | CDN, player embed, analytics, payment integration |
Compare to:
- Spotify: ~30% of $0.003/stream (you need 300k streams to earn $1000)
- Apple Music: ~30%
- Bandcamp: ~15-20%
- DistroKid: Flat fee but still platform-dependent
6.2 License Key Strategies
Artists can choose their pricing model:
Per-Album License
Album: "My Greatest Hits"
Price: $10
License: "MGH-2024-XKCD-7829"
→ One password unlocks entire album
Per-Track License
Track: "Single Release"
Price: $1
License: "SINGLE-A7B3-C9D2"
→ Individual track, individual price
Tiered Licenses
Standard: $10 → MP3 version
Premium: $25 → FLAC + stems + bonus content
→ Different passwords, different content
Time-Limited Previews
Preview license expires in 7 days
Full license: permanent
→ Manifest contains expiry date
6.3 License Key Best Practices
For artists generating license keys:
# Good: Memorable but unique
MGH-2024-XKCD-7829
ALBUM-[year]-[random]-[checksum]
# Good: UUID for automation
550e8400-e29b-41d4-a716-446655440000
# Avoid: Dictionary words (bruteforceable)
password123
mysecretalbum
Recommended entropy: 64+ bits (e.g., 4 random words, or 12+ random alphanumeric)
6.4 No Revocation (By Design)
Q: What if someone leaks the password?
A: Then they leak it. Same as if someone photocopies a book or rips a CD.
This is a feature, not a bug:
- No revocation server = No single point of failure
- No phone home = Works offline, forever
- Leaked keys = Social problem, not technical problem
Mitigation strategies for artists:
- Personalized keys per buyer (track who leaked)
- Watermarked content (forensic tracking)
- Time-limited keys for subscription models
- Social pressure (small community = reputation matters)
The system optimizes for happy paying customers, not punishing pirates.
7. Security Model
7.1 Threat Model
| Threat | Mitigation |
|---|---|
| Man-in-the-middle | Content encrypted at rest; HTTPS for transport |
| Key server compromise | No key server - password-derived keys |
| Platform deplatforming | Self-hostable, decentralized distribution |
| Unauthorized sharing | Economic/social deterrent (password = paid license) |
| Memory extraction | Accepted risk - same as any DRM |
7.2 What This System Does NOT Prevent
- Users sharing their password (same as sharing any license)
- Screen recording of playback
- Memory dumping of decrypted content
This is intentional. The goal is not unbreakable DRM (which is impossible) but:
- Making casual piracy inconvenient
- Giving artists control of their distribution
- Enabling direct artist-to-fan sales
- Removing platform dependency
7.3 Trust Boundaries
TRUSTED UNTRUSTED
──────── ─────────
User's browser/device Distribution CDN
Decryption code (auditable) Payment processor
License key (in user's head) Internet transport
Local playback Third-party hosting
8. Implementation Status
8.1 Completed
- SMSG format specification (v1, v2, v3)
- Go encryption/decryption library (pkg/smsg)
- WASM build for browser (pkg/wasm/stmf)
- Native desktop app (Wails, cmd/dapp-fm-app)
- Demo page with Profile/Fan/Artist modes
- License Manager component
- Streaming decryption API (v1.2.0)
- v2 binary format - 25% smaller files
- zstd compression - 3-10x faster than gzip
- Manifest links - Artist platform links in metadata
- Live demo - https://demo.dapp.fm
- RFC-quality demo file with cryptographically secure password
- v3 streaming format - LTHN rolling keys with CEK wrapping
- Configurable cadence - daily/12h/6h/1h key rotation
- WASM v1.3.0 -
BorgSMSG.decryptV3()for streaming - Chunked streaming - Independently decryptable chunks for seek/streaming
8.2 Fixed Issues
Double base64 encoding bug- Fixed by using binary formatDemo file format detection- v2 format auto-detected via headerKey wrapping for streaming- Implemented in v3 format
8.3 Future Work
- Multi-bitrate adaptive streaming (like HLS/DASH but encrypted)
- Payment integration examples (see
docs/payment-integration.md) - IPFS distribution guide (see
docs/ipfs-distribution.md) - Demo page "Streaming" tab for v3 showcase
9. Usage Examples
9.1 Artist Workflow
# 1. Package your media (uses v2 binary format + zstd by default)
go run ./cmd/mkdemo my-track.mp4 my-track.smsg
# Output:
# Created: my-track.smsg (29220077 bytes)
# Master Password: PMVXogAJNVe_DDABfTmLYztaJAzsD0R7
# Store this password securely - it cannot be recovered!
# Or programmatically:
msg := smsg.NewMessage("Welcome to my album")
msg.AddBinaryAttachment("track.mp4", mediaBytes, "video/mp4")
manifest := smsg.NewManifest("Track Title")
manifest.Artist = "Artist Name"
manifest.AddLink("home", "https://linktr.ee/artist")
encrypted, _ := smsg.EncryptV2WithManifest(msg, password, manifest)
# 2. Upload to any hosting
aws s3 cp my-track.smsg s3://my-bucket/releases/
# or: ipfs add my-track.smsg
# or: scp my-track.smsg myserver:/var/www/
# 3. Sell license keys
# Use Gumroad, Stripe, PayPal - any payment method
# Deliver the master password on purchase
9.2 Fan Workflow
1. Purchase from artist's website → receive license key
2. Download .smsg file from CDN/IPFS/wherever
3. Open demo page or native app
4. Enter license key
5. Content decrypts and plays locally
9.3 Browser Integration
<script src="wasm_exec.js"></script>
<script src="stmf.wasm.js"></script>
<script>
async function playContent(smsgUrl, licenseKey) {
const response = await fetch(smsgUrl);
const bytes = new Uint8Array(await response.arrayBuffer());
const base64 = arrayToBase64(bytes); // Must be binary→base64
const msg = await BorgSMSG.decryptStream(base64, licenseKey);
const blob = new Blob([msg.attachments[0].data], {
type: msg.attachments[0].mime
});
document.querySelector('audio').src = URL.createObjectURL(blob);
}
</script>
10. Comparison to Existing Solutions
| Feature | dapp.fm (self) | dapp.fm (hosted) | Spotify | Bandcamp | Widevine |
|---|---|---|---|---|---|
| Artist revenue | 100% | 95% | ~30% | ~80% | N/A |
| Platform cut | 0% | 5% | ~70% | ~15-20% | Varies |
| Self-hostable | Yes | Optional | No | No | No |
| Open source | Yes | Yes | No | No | No |
| Key escrow | None | None | Required | Required | Required |
| Browser support | WASM | WASM | Web | Web | CDM |
| Offline support | Yes | Yes | Premium | Download | Depends |
| Platform lock-in | None | None | High | Medium | High |
| Works if platform dies | Yes | Yes | No | No | No |
11. Interoperability & Versioning
11.1 Format Versioning
SMSG includes version and format fields for forward compatibility:
| Version | Format | Features |
|---|---|---|
| 1.0 | v1 | ChaCha20-Poly1305, JSON+base64 attachments |
| 1.0 | v2 | Binary attachments, zstd compression (25% smaller, 3-10x faster) |
| 2 (future) | - | Algorithm negotiation, multiple KDFs |
| 3 (future) | - | Streaming chunks, adaptive bitrate, key wrapping |
Decoders MUST reject versions they don't understand. Encoders SHOULD use v2 format for production (smaller, faster).
11.2 Third-Party Implementations
The format is intentionally simple to implement:
Minimum Viable Player (any language):
- Parse 4-byte magic ("SMSG")
- Read version (2 bytes) and header length (4 bytes)
- Parse JSON header
- SHA-256 hash the password
- ChaCha20-Poly1305 decrypt payload
- Parse JSON payload, extract attachments
Reference implementations:
- Go:
pkg/smsg/(canonical) - WASM:
pkg/wasm/stmf/(browser) - (contributions welcome: Rust, Python, JS-native)
11.3 Embedding & Integration
SMSG files can be:
- Embedded in HTML: Base64 in data attributes
- Served via API: JSON wrapper with base64 content
- Bundled in apps: Compiled into native binaries
- Stored on IPFS: Content-addressed, immutable
- Distributed via torrents: Encrypted = safe to share publicly
The player is embeddable:
<iframe src="https://dapp.fm/embed/HASH" width="400" height="200"></iframe>
12. References
- Live Demo: https://demo.dapp.fm
- ChaCha20-Poly1305: RFC 8439
- zstd compression: https://github.com/klauspost/compress/tree/master/zstd
- SMSG Format:
examples/formats/smsg-format.md - Demo Page Source:
demo/index.html - WASM Module:
pkg/wasm/stmf/ - Native App:
cmd/dapp-fm-app/ - Demo Creator Tool:
cmd/mkdemo/
13. License
This specification and implementation are licensed under EUPL-1.2.
Viva La OpenSource 💜