Purpose: Prevent future refactors β human or AI β from stripping the project's personality OR accidentally renaming cryptographic invariants. Both failures are real risks, and this guide draws the line between them.
Meow Decoder has two properties that must coexist:
- Serious cryptography. AES-256-GCM, Argon2id, X25519, ML-KEM, HKDF, HMAC β implemented in Rust, with proof sketches where applicable, and tested with 1000+ assertions.
- Cat-themed personality. Collar tags, kibbles, purring progress bars, whisker checks β a deliberate design choice that makes the project approachable without weakening security.
Past refactors have accidentally flattened one or the other. This guide prevents both.
Cat-themed function names are encouraged in meow_decoder/cat_api.py and similar faΓ§ade modules, as long as they delegate to real primitives without adding logic.
# β
Good β personality wrapper, delegates directly
def purr_encrypt(key, nonce, plaintext, aad=None):
"""Encrypt with a contented purr. (AES-256-GCM)"""
return _get_backend().aes_gcm_encrypt(key, nonce, plaintext, aad)
# β
Good β themed name, identical semantics
def scratch_mac(key, message):
"""Leave a scratch mark β compute a MAC. (HMAC-SHA256)"""
return _get_backend().hmac_sha256(key, message)
# β
Good β playful class wrapping a protocol object
class CollarTag:
"""A cat's collar tag β wraps a MEOW manifest."""
def __init__(self, manifest_bytes):
self._manifest = unpack_manifest(manifest_bytes)Brief personality in comments is fine at module and section level in Layer 3+.
# β
Good β playful section divider in encode.py
# --- πΎ Attach the collar tag (manifest) to frame 0 ---
manifest_frame = pack_manifest(...)
# β
Good β lighthearted docstring in a Layer 4 module
"""
π± Meow Encoder β Turn secrets into yarn balls (animated QR GIFs).
All the real hissing happens in crypto_backend.
"""Layers 4β5 (demos, docs, README, QUICKSTART, examples) are fully open to personality.
<!-- β
Good β README blurb -->
π Under the Fur: All secret-handling cryptography now lives in the Rust
core. The cat may look playful β but the claws are constant-time.
<!-- β
Good β QUICKSTART encouragement -->
π± Your secret file is now wearing its invisible collar!User-facing CLI output can (and should) be fun.
π± Encoding secret.txt...
β
Encrypted (AES-256-GCM)
β
Generated 18 QR frames (1.5x redundancy)
β
Saved to secret.gif β the cat is packed!
# β
Good β wraps ValueError with a friendly message
class CatError(Exception):
"""Something scared the cat."""
pass
class WhiskerCheckFailed(CatError):
"""HMAC verification failed β someone touched the whiskers."""
passDomain separation strings are protocol constants. They appear in formal proofs, test vectors, and interop specs. Changing them silently breaks every existing encoded file.
# β FORBIDDEN β renaming a domain separation label
info = b"kitty_kdf_magic" # NO
info = b"purr_pqxdh_v1" # NO
info = b"meow-purr-chain" # NO
# β
CORRECT β these are frozen protocol constants
info = b"meow_pqxdh_v1" # YES β do not touch
info = b"meow-fs-block" # YES β do not touch
info = b"meow-ratchet-chain" # YES β do not touchHKDF info parameters are domain separators. They are not branding.
# β FORBIDDEN
derived = backend.hkdf_expand(prk, b"cat_treats_key", 32)
# β
CORRECT
derived = backend.hkdf_expand(prk, b"meow-ratchet-chain", 32)Nonce construction is security-critical. Changing variable names, field order, or derivation steps risks catastrophic nonce reuse.
# β FORBIDDEN β renaming nonce variables for fun
catnip_nonce = os.urandom(12) # NO
yarn_iv = hkdf_expand(prk, info, 12) # NO
# β
CORRECT β standard cryptographic names
nonce = os.urandom(12)
synthetic_iv = hkdf_expand(prk, info, 12)The symmetric ratchet (MSR v1.2) has 10 HKDF domain separation constants, header encryption masks, and key commitment tags. These are protocol-level invariants.
# β FORBIDDEN β renaming ratchet state variables
yarn_key = hkdf(...) # NO β this is chain_key
paw_key = hkdf(...) # NO β this is message_key
# β
CORRECT
chain_key = hkdf(...)
message_key = hkdf(...)Layers 1β2 (Rust crypto core, crypto_backend.py) must contain zero humor. Auditors read this code.
// β FORBIDDEN β joke in Rust primitive
fn paws_gcm_encrypt(key: &[u8], nonce: &[u8], pt: &[u8]) -> Vec<u8> { ... }
// β
CORRECT
fn aes_gcm_encrypt(key: &[u8], nonce: &[u8], pt: &[u8]) -> Vec<u8> { ... }# β FORBIDDEN β joke in crypto_backend.py
class CatCryptoBackend:
def scratch_key(self, password, salt): ...
# β
CORRECT
class CryptoBackend:
def derive_key_argon2id(self, password, salt, ...): ...The 52 PyO3 bindings in meow_crypto_rs are the API contract (16 base ops + 36 opaque handle ops). Every Layer 3 module depends on these exact names.
β FORBIDDEN renames:
aes_gcm_encrypt β cat_encrypt
derive_key_hkdf β knead_key
x25519_exchange β hiss_exchange
hmac_sha256 β scratch_hash
secure_zero β shred_yarn
β
These names are permanent:
aes_gcm_encrypt, aes_gcm_decrypt, aes_ctr_crypt
derive_key_argon2id, derive_key_hkdf
hkdf_extract, hkdf_expand
hmac_sha256, hmac_sha256_verify
sha256
x25519_generate_keypair, x25519_exchange, x25519_public_from_private
constant_time_compare, secure_zero, secure_random
# β FORBIDDEN
MANIFEST_MAGIC = b"KITTY"
MODE_BYTE_PURR = 0x02
# β
CORRECT β these are frozen
MANIFEST_MAGIC = b"MEOW"
MODE_BYTE_MEOW2 = 0x02
MODE_BYTE_MEOW3 = 0x03
MODE_BYTE_MEOW4 = 0x04
MODE_BYTE_MEOW5 = 0x05| Area | Personality? | Example |
|---|---|---|
Rust function names (crypto_core/src/) |
No | aes_gcm_encrypt stays aes_gcm_encrypt |
crypto_backend.py method names |
No | CryptoBackend.derive_key_argon2id stays as-is |
| Domain separation labels | No | "meow_pqxdh_v1" is frozen |
| HKDF info strings | No | "meow-ratchet-chain" is frozen |
| Protocol variable names | No | chain_key, message_key, ephemeral_public_key |
| Manifest magic/mode bytes | No | MEOW2=0x02 through MEOW5=0x05 |
| AAD field ordering | No | Fixed struct layout, never reorder |
cat_api.py wrapper names |
Yes | purr_encrypt(), scratch_mac() |
| CLI output messages | Yes | "π± Encoding secret.txt..." |
| Docstrings in Layer 4+ | Yes | """Encrypt with a contented purr.""" |
| Section comments in Layer 3 | Light | # --- πΎ Attach the collar tag --- |
| README / QUICKSTART / demos | Yes | Cat puns, emoji, playful framing |
| THREAT_MODEL / SECURITY_INVARIANTS | No | Formal, precise, no jokes |
The cryptographic core is the trust anchor of this project. It is not a branding surface.
What "sacred" means:
- Do not rename any function in
crypto_core/src/orcrypto_backend.pyfor aesthetic reasons. - Do not alter domain separation strings, HKDF info labels, nonce derivation, AAD field order, or manifest byte layouts.
- Do not weaken Argon2id parameters, MAC verification, or AAD bindings for convenience.
- Do not add new cryptographic algorithms in Layer 4 or Layer 5. All crypto lives in Layer 1; orchestration lives in Layer 3.
- Do not bypass the
CryptoBackend()abstraction. If a module needs crypto, it calls Layer 2.
The personality exists because the core is sacred. Cat wrappers like purr_encrypt() are safe precisely because they add zero logic β they forward arguments unchanged to aes_gcm_encrypt() and return the result untouched. The moment a wrapper modifies a parameter, skips a check, or introduces a new algorithm, it stops being personality and becomes a security defect.
The cat is playful. The claws are constant-time. Both must remain true.
See docs/ARCHITECTURE.md for the full 5-layer boundary model.