Files
herodb/herodb/instructions/age_usage.md

5.7 KiB
Raw Blame History

HeroDB AGE usage: Stateless vs KeyManaged

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)
  • Keymanaged (serverpersisted, named keys)

If you are new to the codebase, the exact tests that exercise these behaviors are:

Implementation entry points:

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().

Quick start

Assuming the server is running on localhost on some PORT:

# 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 <recipient> "hello world"
# → returns ciphertext (base64 in a bulk string)

redis-cli -p PORT AGE DECRYPT <identity> <ciphertext_b64>
# → returns "hello world"

For keymanaged mode, generate a named key once and reference it by name afterwards:

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 <ciphertext_b64>

Stateless AGE (ephemeral)

Characteristics

  • No serverside storage of keys.
  • You pass the actual key material with every call.
  • Not listable via AGE LIST.

Commands and examples

  1. Ephemeral encryption keys
# Generate an ephemeral encryption keypair
redis-cli -p PORT AGE GENENC
# Example output (abridged):
# 1) "age1qz..."          # recipient (public)
# 2) "AGE-SECRET-KEY-1..." # identity (secret)

# Encrypt with the recipient
redis-cli -p PORT AGE ENCRYPT "age1qz..." "hello world"
# → returns bulk string payload: base64 ciphertext

# Decrypt with the identity (secret)
redis-cli -p PORT AGE DECRYPT "AGE-SECRET-KEY-1..." "<ciphertext_b64>"
# → "hello world"
  1. Ephemeral signing keys
# Generate an ephemeral signing keypair
redis-cli -p PORT AGE GENSIGN
# Example output:
# 1) "<verify_pub_b64>"
# 2) "<sign_secret_b64>"

# Sign a message with the secret
redis-cli -p PORT AGE SIGN "<sign_secret_b64>" "msg"
# → returns "<signature_b64>"

# Verify with the public key
redis-cli -p PORT AGE VERIFY "<verify_pub_b64>" "msg" "<signature_b64>"
# → 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 adhoc operations without persistence.

Reference test: rust.test_07_age_stateless_suite()

Keymanaged 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
# Create/persist a named encryption keypair
redis-cli -p PORT AGE KEYGEN app1
# → returns [recipient, identity] but also stores them under name "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 "<ciphertext_b64>"
# → "hello"
  1. Named signing keys
# 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"

# Sign using the stored secret
redis-cli -p PORT AGE SIGNNAME app1 "msg"
# → returns "<signature_b64>"

# Verify using the stored public key
redis-cli -p PORT AGE VERIFYNAME app1 "msg" "<signature_b64>"
# → 1 (valid) or 0 (invalid)
  1. List stored AGE keys
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()

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 Keymanaged 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 keymanaged mode, ensure server storage (and backups) are protected.
  • AGE operations here are applicationlevel crypto and are distinct from database-at-rest encryption configured in the test harness.

Repository pointers