# HeroDB AGE usage: Stateless vs Key‑Managed This document explains how to use the AGE cryptography commands exposed by HeroDB over the Redis protocol in two modes: - Stateless (ephemeral keys; nothing stored on the server) - Key‑managed (server‑persisted, named keys) If you are new to the codebase, the exact tests that exercise these behaviors are: - [rust.test_07_age_stateless_suite()](herodb/tests/usage_suite.rs:495) - [rust.test_08_age_persistent_named_suite()](herodb/tests/usage_suite.rs:555) Implementation entry points: - [herodb/src/age.rs](herodb/src/age.rs) - Dispatch from [herodb/src/cmd.rs](herodb/src/cmd.rs) Note: Database-at-rest encryption flags in the test harness are unrelated to AGE commands; those flags control storage-level encryption of DB files. See the harness near [rust.start_test_server()](herodb/tests/usage_suite.rs:10). ## Quick start Assuming the server is running on localhost on some $PORT: ```bash ~/code/git.ourworld.tf/herocode/herodb/herodb/build.sh ~/code/git.ourworld.tf/herocode/herodb/target/release/herodb --dir /tmp/data --debug --$PORT 6381 --encryption-key 1234 --encrypt ``` ```bash export PORT=6381 # Generate an ephemeral keypair and encrypt/decrypt a message (stateless mode) redis-cli -p $PORT AGE GENENC # → returns an array: [recipient, identity] redis-cli -p $PORT AGE ENCRYPT "hello world" # → returns ciphertext (base64 in a bulk string) redis-cli -p $PORT AGE DECRYPT # → returns "hello world" ``` For key‑managed mode, generate a named key once and reference it by name afterwards: ```bash redis-cli -p $PORT AGE KEYGEN app1 # → persists encryption keypair under name "app1" redis-cli -p $PORT AGE ENCRYPTNAME app1 "hello" redis-cli -p $PORT AGE DECRYPTNAME app1 ``` ## Stateless AGE (ephemeral) Characteristics - No server‑side storage of keys. - You pass the actual key material with every call. - Not listable via AGE LIST. Commands and examples 1) Ephemeral encryption keys ```bash # Generate an ephemeral encryption keypair redis-cli -p $PORT AGE GENENC # Example output (abridged): # 1) "age1qz..." # recipient (public key) = can be used by others e.g. to verify what I sign # 2) "AGE-SECRET-KEY-1..." # identity (secret) = is like my private, cannot lose this one # Encrypt with the recipient public key redis-cli -p $PORT AGE ENCRYPT "age1qz..." "hello world" # → returns bulk string payload: base64 ciphertext (encrypted content) # Decrypt with the identity (secret) in other words your private key redis-cli -p $PORT AGE DECRYPT "AGE-SECRET-KEY-1..." "" # → "hello world" ``` 2) Ephemeral signing keys > ? is this same as my private key ```bash # Generate an ephemeral signing keypair redis-cli -p $PORT AGE GENSIGN # Example output: # 1) "" # 2) "" # Sign a message with the secret redis-cli -p $PORT AGE SIGN "" "msg" # → returns "" # Verify with the public key redis-cli -p $PORT AGE VERIFY "" "msg" "" # → 1 (valid) or 0 (invalid) ``` When to use - You do not want the server to store private keys. - You already manage key material on the client side. - You need ad‑hoc operations without persistence. Reference test: [rust.test_07_age_stateless_suite()](herodb/tests/usage_suite.rs:495) ## Key‑managed AGE (persistent, named) Characteristics - Server generates and persists keypairs under a chosen name. - Clients refer to keys by name; raw secrets are not supplied on each call. - Keys are discoverable via AGE LIST. Commands and examples 1) Named encryption keys ```bash # Create/persist a named encryption keypair redis-cli -p $PORT AGE KEYGEN app1 # → returns [recipient, identity] but also stores them under name "app1" > TODO: should not return identity (security, but there can be separate function to export it e.g. AGE EXPORTKEY app1) # Encrypt using the stored public key redis-cli -p $PORT AGE ENCRYPTNAME app1 "hello" # → returns bulk string payload: base64 ciphertext # Decrypt using the stored secret redis-cli -p $PORT AGE DECRYPTNAME app1 "" # → "hello" ``` 2) Named signing keys ```bash # Create/persist a named signing keypair redis-cli -p $PORT AGE SIGNKEYGEN app1 # → returns [verify_pub_b64, sign_secret_b64] and stores under name "app1" > TODO: should not return sign_secret_b64 (for security, but there can be separate function to export it e.g. AGE EXPORTSIGNKEY app1) # Sign using the stored secret redis-cli -p $PORT AGE SIGNNAME app1 "msg" # → returns "" # Verify using the stored public key redis-cli -p $PORT AGE VERIFYNAME app1 "msg" "" # → 1 (valid) or 0 (invalid) ``` 3) List stored AGE keys ```bash redis-cli -p $PORT AGE LIST # Example output includes labels such as "encpub" and your key names (e.g., "app1") ``` When to use - You want centralized key storage/rotation and fewer secrets on the client. - You need names/labels for workflows and can trust the server with secrets. - You want discoverability (AGE LIST) and simpler client commands. Reference test: [rust.test_08_age_persistent_named_suite()](herodb/tests/usage_suite.rs:555) ## Choosing a mode - Prefer Stateless when: - Minimizing server trust for secret material is the priority. - Clients already have a secure mechanism to store/distribute keys. - Prefer Key‑managed when: - Centralized lifecycle, naming, and discoverability are beneficial. - You plan to integrate rotation, ACLs, or auditability on the server side. ## Security notes - Treat identities and signing secrets as sensitive; avoid logging them. - For key‑managed mode, ensure server storage (and backups) are protected. - AGE operations here are application‑level crypto and are distinct from database-at-rest encryption configured in the test harness. ## Repository pointers - Stateless examples in tests: [rust.test_07_age_stateless_suite()](herodb/tests/usage_suite.rs:495) - Key‑managed examples in tests: [rust.test_08_age_persistent_named_suite()](herodb/tests/usage_suite.rs:555) - AGE implementation: [herodb/src/age.rs](herodb/src/age.rs) - Command dispatch: [herodb/src/cmd.rs](herodb/src/cmd.rs) - Bash demo: [herodb/examples/age_bash_demo.sh](herodb/examples/age_bash_demo.sh) - Rust persistent demo: [herodb/examples/age_persist_demo.rs](herodb/examples/age_persist_demo.rs) - Additional notes: [herodb/instructions/encrypt.md](herodb/instructions/encrypt.md)