"The metadata standard. Every Relic conforms."
Every Relic has two layers.
Public (SFW) — anyone can fetch. Stored permanently on IPFS. Private (NSFW) — Witnesses only. Served via the DRM server. The key never leaves the server.
Stored on IPFS. URI written immutably into the Ethereum event log at mint time.
{
"name": "SockLove #{TOKEN_ID} — {TITLE}",
"description": "{DESCRIPTION}",
"image": "ipfs://{SAFE_POSTER_CID}",
"animation_url": "ipfs://{SAFE_TEASER_CID}",
"attributes": [
{ "trait_type": "Type", "value": "{TYPE}" },
{ "trait_type": "Creators", "value": "Sockpusher & Sockthief" },
{ "trait_type": "Moments", "value": {MOMENTS} },
{ "trait_type": "Duration", "value": "{DURATION}" },
{ "trait_type": "Release Date", "value": "{YYYY-MM-DD}" },
{ "trait_type": "Edition Size", "value": {EDITION_SIZE} },
{ "trait_type": "Rating", "value": "NSFW (18+)" }
]
}| Field | Value |
|---|---|
name |
"SockLove #1 — The First Night". Em dash. Space on both sides. |
description |
Narrative. Plain text. No Markdown. No URLs. |
image |
ipfs:// URI of the SFW poster JPEG. |
animation_url |
ipfs:// URI of the SFW teaser MP4. |
| Trait | Type | Notes |
|---|---|---|
Type |
string | "Photo" or "Video" |
Creators |
string | Always "Sockpusher & Sockthief" |
Moments |
integer | Number of photos. Always 1 for video tokens. |
Duration |
string | "00:00:00" for photo tokens. "hh:mm:ss" for video tokens. |
Release Date |
string | "YYYY-MM-DD" |
Edition Size |
integer | Number of copies minted. |
Rating |
string | Always "NSFW (18+)" |
Private content is not referenced in public metadata.
The PWA discovers the DRM server via the ENS text record drm on socklove.eth.
Authentication is via SIWE. Access is verified on-chain per request via balanceOf.
Content is decrypted server-side and streamed as plain bytes.
The decryption key never reaches the client.
- Photo: Full-resolution JPEG, maximum quality, sRGB
- Video: DNxHR HQX, full resolution
- Safe poster: 2000px long edge, JPEG 85%, sRGB
- Safe teaser: H.264, 1920×1080, CRF 23, AAC 128k,
+faststart
Strip all metadata from every file before any upload or encryption.
exiftool -all= *.jpg *.mov *.mp4Apply a visible SockLove logo watermark to all content files and safe assets before encryption. What gets encrypted is already watermarked — no clean copy ever exists.
Photos — ImageMagick:
magick input.jpg \
\( watermark.png -resize 15% -alpha set -evaluate multiply 0.5 \) \
-gravity SouthEast -geometry +40+40 \
-composite output_watermarked.jpgVideos — FFmpeg:
ffmpeg -i input.mp4 -i watermark.png \
-filter_complex "[1:v]scale=iw*0.1:-1,format=rgba,colorchannelmixer=aa=0.5[wm];[0:v][wm]overlay=W-w-40:H-h-40" \
-c:v libx264 -crf 23 -c:a copy \
output_watermarked.mp4AES-256-GCM. Performed by the DRM server (Node.js crypto module).
# Generate 32-byte key
openssl rand 32 > gallery.keyimport fs from 'fs';
import { createCipheriv, randomBytes } from 'crypto';
const key = fs.readFileSync('gallery.key');
const iv = randomBytes(12); // 96-bit nonce for GCM
const cipher = createCipheriv('aes-256-gcm', key, iv);
const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
const tag = cipher.getAuthTag();
// Persist: iv + tag + encryptedStore the gallery key and all encrypted files on the DRM server. Pin safe assets to IPFS — record CIDs.
AES-128-CBC. Performed by FFmpeg via -hls_key_info_file.
For segment-level encryption, HLS uses AES-128-CBC as the standard method.
# 1. Generate 16-byte AES-128 key
openssl rand 16 > hls.key
# 2. Create keyinfo file (key URI, key path, optional IV)
echo "https://placeholder/hls.key" > hls.keyinfo
echo "hls.key" >> hls.keyinfo
echo "$(openssl rand -hex 16)" >> hls.keyinfo
# 3. Transcode to encrypted HLS
ffmpeg -i input_watermarked.mp4 \
-c:v libx264 -crf 23 -c:a aac -b:a 128k \
-hls_time 6 \
-hls_key_info_file hls.keyinfo \
-hls_playlist_type vod \
-hls_segment_filename "segments/%03d.ts" \
playlist.m3u8The DRM server rewrites the playlist before serving — removing the key reference and rewriting all segment paths to authenticated proxy endpoints. The video player receives no key. The key never leaves the server. Quality is selected automatically for the Witness's connection.
Store encrypted segments, playlist, and key on the DRM server. Pin safe assets to IPFS — record CIDs.
1 Shoot and edit
2 Produce safe poster and safe teaser
3 Strip EXIF from all files
4 Apply visible watermark to all content files and safe assets
5 Encrypt content — store on DRM server
6 Pin safe poster and safe teaser to IPFS — record CIDs
7 Build token metadata JSON — verify no {PLACEHOLDER} strings remain
8 Pin token metadata to IPFS — record CID
9 Mint via the Safe (socklove.eth):
mint("ipfs://{METADATA_CID}", {EDITION_SIZE})
└── Sockpusher signs
└── Sockthief signs
└── Relic is live
Both founders must sign step 9. A Relic without both signatures does not exist.
socklove.eth · Ethereum · 🧦💜🔥