.msvy
File format spec

A real encrypted media container format: MSVY1 magic bytes + AES-256-GCM ciphertext. Used by LuisThread and LuisDrive as two independent producers of the same format.

Why this exists

A bare media URL is normally directly hotlinkable, scrapable, and viewable by anyone who has the link -- no login required. .msvy flips that: the file on disk is genuine ciphertext. Without the matching key (fetched separately through an authenticated API call), the raw bytes are useless. It's a real speed bump against bots, scrapers, and lazy hotlinking -- not a defense against a legitimate, logged-in user screenshotting what they can already see on their own screen, which no client-side scheme can prevent.

Renaming a .msvy file to .jpg or .png does nothing -- the bytes are still ciphertext, not a real image, so your OS will correctly refuse to open it.

Byte layout

"MSVY1"
5 bytes, ASCII
AES-256-GCM ciphertext (rest of file, 16-byte auth tag appended at the end)

That's the entire file. No header for key/IV, no metadata block -- those live entirely outside the file, in whichever service produced it.

FieldDetail
MagicLiteral ASCII bytes MSVY1 -- a format marker, not currently used for version branching
CipherAES-256-GCM (256-bit key, 96-bit/12-byte IV, 128-bit/16-byte auth tag)
AADNone (no additional authenticated data)
Key/IV storageNever in the file -- stored server-side by whichever service encrypted it, keyed by that file's own ID

Known producers

Each service that creates .msvy files manages its own keys independently -- there's no shared key registry. A .msvy file from LuisThread can't be decrypted using LuisDrive's key API and vice versa, even though both use the identical container format.

LuisThread

Encrypts every media upload server-side on arrival. Keys live in a media_keys table, fetched via an authenticated /api/media-key/<id> endpoint.

LuisDrive

Automatically encrypts uploaded images (.png/.jpg/.jpeg/.gif/.webp). Keys live alongside file metadata, fetched via /api/drive/media-key?id=....

Decrypting one, in code

Python (server-side, matches how both producers encrypt):

from cryptography.hazmat.primitives.ciphers.aead import AESGCM

data = open("file.msvy", "rb").read()
magic, ciphertext = data[:5], data[5:]   # b"MSVY1"
key = bytes.fromhex(key_hex)              # from the producer's key API
iv = bytes.fromhex(iv_hex)
plaintext = AESGCM(key).decrypt(iv, ciphertext, None)

JavaScript (browser, via WebCrypto -- what the decoder below actually runs):

const ciphertext = rawBytes.slice(5); // skip the "MSVY1" magic
const cryptoKey = await crypto.subtle.importKey(
  "raw", keyBytes, { name: "AES-GCM" }, false, ["decrypt"]
);
const plaintext = await crypto.subtle.decrypt(
  { name: "AES-GCM", iv: ivBytes }, cryptoKey, ciphertext
);

Try it

Upload a real .msvy file and paste its key/IV (get these from whichever service's media-key endpoint produced it) to decrypt and view it. Everything happens locally in your browser via WebCrypto -- nothing here is uploaded anywhere.