Encode an identifier into an image's pixels; verify a JSON record against any copy
Project description
Mememage
Encode an identifier into an image's pixels; verify a JSON record against any copy.
Mememage writes a 2-pixel-tall bar into the bottom of an image. It carries an identifier (a short pointer to a JSON record you store anywhere) and a content hash (first 16 hex of SHA-256 over that record). verify recomputes the hash and compares it to the bar. Change any field, verification fails.
Core proves the record-to-image binding, by hash alone. It does not police the pixels (edit the image but leave the bar and it still verifies) and does not prove authorship (a signature's job, out of scope). Two keys stay outside the hash by design, signature and _-prefixed keys, and encode refuses both as your own field names.
The bar survives JPEG, resaves, screenshots, and re-uploads. Downscaling is the limit: images ≥ ~1000px wide survive a shrink to ~0.8× plus one recompression (59/60 real-image round-trips across three resamplers and JPEG q70 to q80). Past that, no promise.
pip install mememage # encode / decode / verify (Pillow included)
# pip install "mememage[encrypt]" # adds AES-256 field encryption
Quickstart
import mememage
# encode: write the bar, build a record from your fields
result = mememage.encode("photo.png", {"title": "Morning fog", "by": "catmemes"})
result.identifier # 'mememage-3dc5f03a747bb38e' (derived from your fields)
result.save("photo.json") # a record, stored or served separately
# decode: read the bar back out of any copy (PNG, JPEG, a screenshot)
bar = mememage.decode("photo.jpg")
bar.identifier, bar.content_hash
# verify: does a record match an image?
mememage.verify("photo.jpg", result.record) # truthy if intact
Core does no networking. decode hands you an identifier; resolve the record wherever you kept it (a dict, a file, a DB, a URL), then verify.
Hash models (hash_version). Core implements the open model — it hashes every field except content_hash/signature, so whatever you put in the record is tamper-evident. encode stamps hash_version: "open". An application can define its own curated hash_version (a fixed inclusion set) that core doesn't implement; verify reports such a record as unsupported (Verification.supported == False) rather than a hash mismatch — it fails closed (bool() is False) but this is not tamper evidence. Verify those records with the application that defines the version (e.g. its own decoder). The CLI prints UNSUPPORTED and exits 3.
Inputs / outputs. encode, decode, and verify accept a path, bytes, a file-like, a PIL Image, or a numpy array (HEIC needs the [heic] extra). encode returns a barred Record.image and, given a destination, writes a lossless PNG (in place for a PNG path, a .png sibling otherwise, or out=<path/stream>); an in-memory input with no destination never touches disk. Record fields are yours (captions, credits, generation params, links) except a few reserved names: identifier, content_hash, hash_version, signature, encrypted_fields.
Encrypt private fields
Mark fields private to encrypt them (AES-256-GCM via PBKDF2) under a password. It still verifies without the password (the hash covers the ciphertext), and unlock reveals the fields. Passwords are never stored.
result = mememage.encode("photo.png", {"title": "Public", "gps": "45.5,-122.6"},
password="hunter2", private=["gps"])
mememage.verify("photo.png", result.record) # matches, no password
mememage.unlock(result, "hunter2")["gps"] # '45.5,-122.6'
Command line
mememage encode photo.png --field title="Morning fog" -o photo.json # write the record
mememage decode photo.jpg --record photo.json # VERIFIED (0) / RECORD ALTERED (1) / UNSUPPORTED (3)
mememage decode photo.jpg # read the identifier only
Without -o, the record lands beside the image as <identifier>.json. With --record, decode exits 0 on a match, 1 on a mismatch; without it, exit 0 just means a bar was read.
License
MIT.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mememage-0.1.6.tar.gz.
File metadata
- Download URL: mememage-0.1.6.tar.gz
- Upload date:
- Size: 53.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bc46addf2e5cfd4f75b6b960e8fe85e7a3b8af52e257315809e88a10503bb46a
|
|
| MD5 |
ee0b053b43d801f3f44c62d7b742676b
|
|
| BLAKE2b-256 |
04f308b5055564b99247560ad9dda2c23d1814a79eafbef00d74491e6c176675
|
Provenance
The following attestation bundles were made for mememage-0.1.6.tar.gz:
Publisher:
publish.yml on sememtac/mememage
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mememage-0.1.6.tar.gz -
Subject digest:
bc46addf2e5cfd4f75b6b960e8fe85e7a3b8af52e257315809e88a10503bb46a - Sigstore transparency entry: 2180705120
- Sigstore integration time:
-
Permalink:
sememtac/mememage@2e9ac6e6229e1eed9dde5c4d01f9b30a04e453bd -
Branch / Tag:
refs/tags/v0.1.6 - Owner: https://github.com/sememtac
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2e9ac6e6229e1eed9dde5c4d01f9b30a04e453bd -
Trigger Event:
release
-
Statement type:
File details
Details for the file mememage-0.1.6-py3-none-any.whl.
File metadata
- Download URL: mememage-0.1.6-py3-none-any.whl
- Upload date:
- Size: 39.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5da7a35af52fbf9895b609e1a435f527441560f02f63ded01741f3c14fd1e405
|
|
| MD5 |
1312fe8fe3b02eb4dd9151e8ff5b8e48
|
|
| BLAKE2b-256 |
d09babbb4d90b450a400616a32e5b22bdb6d032c511b5905bd851324512fbc73
|
Provenance
The following attestation bundles were made for mememage-0.1.6-py3-none-any.whl:
Publisher:
publish.yml on sememtac/mememage
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mememage-0.1.6-py3-none-any.whl -
Subject digest:
5da7a35af52fbf9895b609e1a435f527441560f02f63ded01741f3c14fd1e405 - Sigstore transparency entry: 2180705309
- Sigstore integration time:
-
Permalink:
sememtac/mememage@2e9ac6e6229e1eed9dde5c4d01f9b30a04e453bd -
Branch / Tag:
refs/tags/v0.1.6 - Owner: https://github.com/sememtac
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2e9ac6e6229e1eed9dde5c4d01f9b30a04e453bd -
Trigger Event:
release
-
Statement type: