diff --git a/README.md b/README.md index e308eaf..82762bf 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ The main purpose of HeroDB is to offer a lightweight, embeddable, and Redis-comp - **Expiration**: Time-to-live (TTL) functionality for keys. - **Scanning**: Cursor-based iteration for keys and hash fields (`SCAN`, `HSCAN`). - **AGE Cryptography Commands**: HeroDB-specific extensions for cryptographic operations. +- **Symmetric Encryption**: Stateless symmetric encryption using XChaCha20-Poly1305. +- **Admin Database 0**: Centralized control for database management, access control, and per-database encryption. ## Quick Start @@ -30,31 +32,14 @@ cargo build --release ### Running HeroDB -You can start HeroDB with different backends and encryption options: - -#### Default `redb` Backend +Launch HeroDB with the required `--admin-secret` flag, which encrypts the admin database (DB 0) and authorizes admin access. Optional flags include `--dir` for the database directory, `--port` for the TCP port (default 6379), `--sled` for the sled backend, and `--enable-rpc` to start the JSON-RPC management server on port 8080. +Example: ```bash -./target/release/herodb --dir /tmp/herodb_redb --port 6379 +./target/release/herodb --dir /tmp/herodb --admin-secret myadminsecret --port 6379 --enable-rpc ``` -#### `sled` Backend - -```bash -./target/release/herodb --dir /tmp/herodb_sled --port 6379 --sled -``` - -#### `redb` with Encryption - -```bash -./target/release/herodb --dir /tmp/herodb_encrypted --port 6379 --encrypt --encryption_key mysecretkey -``` - -#### `sled` with Encryption - -```bash -./target/release/herodb --dir /tmp/herodb_sled_encrypted --port 6379 --sled --encrypt --encryption_key mysecretkey -``` +For detailed launch options, see [Basics](docs/basics.md). ## Usage with Redis Clients @@ -76,10 +61,24 @@ redis-cli -p 6379 SCAN 0 MATCH user:* COUNT 10 # 2) 1) "user:1" ``` +## Cryptography + +HeroDB supports asymmetric encryption/signatures via AGE commands (X25519 for encryption, Ed25519 for signatures) in stateless or key-managed modes, and symmetric encryption via SYM commands. Keys are persisted in the admin database (DB 0) for managed modes. + +For details, see [AGE Cryptography](docs/age.md) and [Basics](docs/basics.md). + +## Database Management + +Databases are managed via JSON-RPC API, with metadata stored in the encrypted admin database (DB 0). Databases are public by default upon creation; use RPC to set them private, requiring access keys for SELECT operations (read or readwrite based on permissions). This includes per-database encryption keys, access control, and lifecycle management. + +For examples, see [JSON-RPC Examples](docs/rpc_examples.md) and [Admin DB 0 Model](docs/admin.md). + ## Documentation For more detailed information on commands, features, and advanced usage, please refer to the documentation: - [Basics](docs/basics.md) - [Supported Commands](docs/cmds.md) -- [AGE Cryptography](docs/age.md) \ No newline at end of file +- [AGE Cryptography](docs/age.md) +- [Admin DB 0 Model (access control, per-db encryption)](docs/admin.md) +- [JSON-RPC Examples (management API)](docs/rpc_examples.md) \ No newline at end of file diff --git a/docs/admin.md b/docs/admin.md new file mode 100644 index 0000000..55b9ffb --- /dev/null +++ b/docs/admin.md @@ -0,0 +1,181 @@ +# Admin Database 0 (`0.db`) + +This page explains what the Admin Database `DB 0` is, why HeroDB uses it, and how to work with it as a developer and end-user. It’s a practical guide covering how databases are created, listed, secured with access keys, and encrypted using per-database secrets. + +## What is `DB 0`? + +`DB 0` is the control-plane for a HeroDB instance. It stores metadata for all user databases (`db_id >= 1`) so the server can: +- Know which databases exist (without scanning the filesystem) +- Enforce access control (public/private with access keys) +- Enforce per-database encryption (whether a given database must be opened encrypted and with which write-only key) + +`DB 0` itself is always encrypted with the admin secret (the process-level secret provided at startup). + +## How `DB 0` is created and secured + +- `DB 0` lives at `/0.db` +- It is always encrypted using the `admin secret` provided at process startup (using the `--admin-secret ` CLI flag) +- Only clients that provide the correct admin secret can `SELECT 0` (see “`SELECT` + `KEY`” below) + +At startup, the server bootstraps `DB 0` (initializes counters and structures) if it’s missing. + +## Metadata stored in `DB 0` + +Keys in `DB 0` (internal layout, but useful to understand how things work): + +- `admin:next_id` + - String counter holding the next id to allocate (initialized to `"1"`) + +- `admin:dbs` + - A hash acting as a set of existing database ids + - field = id (as string), value = `"1"` + +- `meta:db:` + - A hash holding db-level metadata + - field `public` = `"true"` or `"false"` (defaults to `true` if missing) + +- `meta:db::keys` + - A hash mapping access-key hashes to the string `Permission:created_at_seconds` + - Examples: `Read:1713456789` or `ReadWrite:1713456789` + - The plaintext access keys are never stored; only their `SHA-256` hashes are kept + +- `meta:db::enc` + - A string holding the per-database encryption key used to open `.db` encrypted + - This value is write-only from the perspective of the management APIs (it’s set at creation and never returned) + +- `age:key:` + - Base64-encoded X25519 recipient (public encryption key) for named AGE keys +- `age:privkey:` + - Base64-encoded X25519 identity (secret encryption key) for named AGE keys +- `age:signpub:` + - Base64-encoded Ed25519 verify public key for named AGE keys +- `age:signpriv:` + - Base64-encoded Ed25519 signing secret key for named AGE keys + +> You don’t need to manipulate these keys directly; they’re listed to clarify the model. AGE keys are managed via AGE commands. + +## Database lifecycle + +1) Create a database (via JSON-RPC) +- The server allocates an id from `admin:next_id`, registers it in `admin:dbs`, and defaults the database to `public=true` +- If you pass an optional `encryption_key` during creation, the server persists it in `meta:db::enc`. That database will be opened in encrypted mode from then on + +2) Open and use a database +- Clients select a database over RESP using `SELECT` +- Authorization and encryption state are enforced using `DB 0` metadata + +3) Delete database files +- Removing `.db` removes the physical storage +- `DB 0` remains the source of truth for existence and may be updated by future management methods as the system evolves + +## Access control model + +- Public database (default) + - Anyone can `SELECT ` with no key, and will get `ReadWrite` permission +- Private database + - You must provide an access key when selecting the database + - The server hashes the provided key with `SHA-256` and checks membership in `meta:db::keys` + - Permissions are `Read` or `ReadWrite` depending on how the key was added +- Admin `DB 0` + - Requires the exact admin secret as the `KEY` argument to `SELECT 0` + - Permission is `ReadWrite` when the secret matches + +### How to select databases with optional `KEY` + +- Public DB (no key required) + - `SELECT ` + +- Private DB (access key required) + - `SELECT KEY ` + +- Admin `DB 0` (admin secret required) + - `SELECT 0 KEY ` + +Examples (using `redis-cli`): +```bash +# Public database +redis-cli -p $PORT SELECT 1 +# → OK + +# Private database +redis-cli -p $PORT SELECT 2 KEY my-db2-access-key +# → OK + +# Admin DB 0 +redis-cli -p $PORT SELECT 0 KEY my-admin-secret +# → OK +``` + +## Per-database encryption + +- At database creation, you can provide an optional per-db encryption key +- If provided, the server persists that key in `DB 0` as `meta:db::enc` +- When you later open the database, the engine checks whether `meta:db::enc` exists to decide if it must open `.db` in encrypted mode +- The per-db key is not returned by RPC—it is considered write-only configuration data + +Operationally: +- Create with encryption: pass a non-null `encryption_key` to the `createDatabase` RPC +- Open later: simply `SELECT` the database; encryption is transparent to clients + +## Management via JSON-RPC + +You can manage databases using the management RPC (namespaced `herodb.*`). Typical operations: +- `createDatabase(backend, config, encryption_key?)` + - Allocates a new id, sets optional encryption key +- `listDatabases()` + - Lists database ids and info (including whether storage is currently encrypted) +- `getDatabaseInfo(db_id)` + - Returns details: backend, encrypted flag, size on disk, `key_count`, timestamps, etc. +- `addAccessKey(db_id, key, permissions)` + - Adds a `Read` or `ReadWrite` access key (permissions = `"read"` | `"readwrite"`) +- `listAccessKeys(db_id)` + - Returns hashes and permissions; you can use these hashes to delete keys +- `deleteAccessKey(db_id, key_hash)` + - Removes a key by its hash +- `setDatabasePublic(db_id, public)` + - Toggles public/private + +Copyable JSON examples are provided in the [RPC examples documentation](./rpc_examples.md). + +## Typical flows + +1) Public, unencrypted database +- Create a new database without an encryption key +- Clients can immediately `SELECT ` without a key +- You can later make it private and add keys if needed + +2) Private, encrypted database +- Create passing an `encryption_key` +- Mark it private (`setDatabasePublic false`) and add access keys +- Clients must use `SELECT KEY ` +- Storage opens in encrypted mode automatically + +## Security notes + +- Only `SHA-256` hashes of access keys are stored in `DB 0`; keep plaintext keys safe on the client side +- The per-db encryption key is never exposed via the API after it is set +- The admin secret must be kept secure; anyone with it can `SELECT 0` and perform administrative actions + +## Troubleshooting + +- `ERR invalid access key` when selecting a private db + - Ensure you passed the `KEY` argument: `SELECT KEY ` + - If you recently added the key, confirm the permissions and that you used the exact plaintext (hash must match) + +- `Database X not found` + - The id isn’t registered in `DB 0` (`admin:dbs`). Use the management APIs to create or list databases + +- Cannot `SELECT 0` + - The `KEY` must be the exact admin secret passed at server startup + +## Reference + +- Admin metadata lives in `DB 0` (`0.db`) and controls: + - Existence: `admin:dbs` + - Access: `meta:db:.public` and `meta:db::keys` + - Encryption: `meta:db::enc` + +For command examples and management payloads: +- RESP command basics: `docs/basics.md` +- Supported commands: `docs/cmds.md` +- JSON-RPC examples: `docs/rpc_examples.md` \ No newline at end of file diff --git a/docs/age.md b/docs/age.md index 9a440bb..3dc8358 100644 --- a/docs/age.md +++ b/docs/age.md @@ -1,188 +1,96 @@ -# HeroDB AGE usage: Stateless vs Key‑Managed +# HeroDB AGE Cryptography -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) +HeroDB provides AGE-based asymmetric encryption and digital signatures over the Redis protocol using X25519 for encryption and Ed25519 for signatures. Keys can be used in stateless (ephemeral) or key-managed (persistent, named) modes. -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) +In key-managed mode, HeroDB uses a unified keypair concept: a single Ed25519 signing key is deterministically derived into X25519 keys for encryption, allowing one keypair to handle both encryption and signatures transparently. -Implementation entry points: -- [herodb/src/age.rs](herodb/src/age.rs) -- Dispatch from [herodb/src/cmd.rs](herodb/src/cmd.rs) +## Cryptographic Algorithms -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). +### X25519 (Encryption) +- Elliptic-curve Diffie-Hellman key exchange for symmetric key derivation. +- Used for encrypting/decrypting messages. -## Quick start +### Ed25519 (Signatures) +- EdDSA digital signatures for message authentication. +- Used for signing/verifying messages. -Assuming the server is running on localhost on some $PORT: +### Key Derivation +Ed25519 signing keys are deterministically converted to X25519 keys for encryption. This enables a single keypair to support both operations without additional keys. Derivation uses the Ed25519 secret scalar clamped for X25519. + +In named keypairs, Ed25519 keys are stored, and X25519 keys are derived on-demand and cached. + +## Stateless Mode (Ephemeral Keys) +No server-side storage; keys are provided with each command. + +Available commands: +- `AGE GENENC`: Generate ephemeral X25519 keypair. Returns `[recipient, identity]`. +- `AGE GENSIGN`: Generate ephemeral Ed25519 keypair. Returns `[verify_pub, sign_secret]`. +- `AGE ENCRYPT `: Encrypt message. Returns base64 ciphertext. +- `AGE DECRYPT `: Decrypt ciphertext. Returns plaintext. +- `AGE SIGN `: Sign message. Returns base64 signature. +- `AGE VERIFY `: Verify signature. Returns 1 (valid) or 0 (invalid). + +Example: ```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 -``` +redis-cli AGE GENENC +# → 1) "age1qz..." # recipient (X25519 public) +# 2) "AGE-SECRET-KEY-1..." # identity (X25519 secret) +redis-cli AGE ENCRYPT "age1qz..." "hello" +# → base64_ciphertext -```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 "" +redis-cli AGE DECRYPT "AGE-SECRET-KEY-1..." base64_ciphertext # → "hello" ``` -2) Named signing keys +## Key-Managed Mode (Persistent Named Keys) +Keys are stored server-side under names. Supports unified keypairs for both encryption and signatures. +Available commands: +- `AGE KEYGEN `: Generate and store unified keypair. Returns `[recipient, identity]` in age format. +- `AGE SIGNKEYGEN `: Generate and store Ed25519 signing keypair. Returns `[verify_pub, sign_secret]`. +- `AGE ENCRYPTNAME `: Encrypt with named key. Returns base64 ciphertext. +- `AGE DECRYPTNAME `: Decrypt with named key. Returns plaintext. +- `AGE SIGNNAME `: Sign with named key. Returns base64 signature. +- `AGE VERIFYNAME `: Verify with named key. Returns 1 or 0. +- `AGE LIST`: List all stored key names. Returns sorted array of names. + +### AGE LIST Output +Returns a flat, deduplicated, sorted array of key names (strings). Each name corresponds to a stored keypair, which may include encryption keys (X25519), signing keys (Ed25519), or both. + +Output format: `["name1", "name2", ...]` + +Example: ```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) +redis-cli AGE LIST +# → 1) "" +# 2) "" ``` -3) List stored AGE keys +For unified keypairs (from `AGE KEYGEN`), the name handles both encryption (derived X25519) and signatures (stored Ed25519) transparently. +Example with named keys: ```bash -redis-cli -p $PORT AGE LIST -# Example output includes labels such as "encpub" and your key names (e.g., "app1") +redis-cli AGE KEYGEN app1 +# → 1) "age1..." # recipient +# 2) "AGE-SECRET-KEY-1..." # identity + +redis-cli AGE ENCRYPTNAME app1 "secret message" +# → base64_ciphertext + +redis-cli AGE DECRYPTNAME app1 base64_ciphertext +# → "secret message" + +redis-cli AGE SIGNNAME app1 "message" +# → base64_signature + +redis-cli AGE VERIFYNAME app1 "message" base64_signature +# → 1 ``` -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. +## Choosing a Mode +- **Stateless**: For ad-hoc operations without persistence; client manages keys. +- **Key-managed**: For centralized key lifecycle; server stores keys for convenience and discoverability. -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) \ No newline at end of file +Implementation: [herodb/src/age.rs](herodb/src/age.rs)
+Tests: [herodb/tests/usage_suite.rs](herodb/tests/usage_suite.rs) \ No newline at end of file diff --git a/docs/basics.md b/docs/basics.md index a00a3a8..20d73bd 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -1,4 +1,58 @@ -Here's an expanded version of the cmds.md documentation to include the list commands: +# HeroDB Basics + +## Launching HeroDB + +To launch HeroDB, use the binary with required and optional flags. The `--admin-secret` flag is mandatory, encrypting the admin database (DB 0) and authorizing admin access. + +### Launch Flags +- `--dir `: Directory for database files (default: current directory). +- `--port `: TCP port for Redis protocol (default: 6379). +- `--debug`: Enable debug logging. +- `--sled`: Use Sled backend (default: Redb). +- `--enable-rpc`: Start JSON-RPC management server on port 8080. +- `--rpc-port `: Custom RPC port (default: 8080). +- `--admin-secret `: Required secret for DB 0 encryption and admin access. + +Example: +```bash +./target/release/herodb --dir /tmp/herodb --admin-secret mysecret --port 6379 --enable-rpc +``` + +Deprecated flags (`--encrypt`, `--encryption-key`) are ignored for data DBs; per-database encryption is managed via RPC. + +## Admin Database (DB 0) + +DB 0 acts as the administrative database instance, storing metadata for all user databases (IDs >= 1). It controls existence, access control, and per-database encryption. DB 0 is always encrypted with the `--admin-secret`. + +When creating a new database, DB 0 allocates an ID, registers it, and optionally stores a per-database encryption key (write-only). Databases are public by default; use RPC to set them private, requiring access keys for SELECT (read or readwrite based on permissions). Keys are persisted in DB 0 for managed AGE operations. + +Access DB 0 with `SELECT 0 KEY `. + +## Symmetric Encryption + +HeroDB supports stateless symmetric encryption via SYM commands, using XChaCha20-Poly1305 AEAD. + +Commands: +- `SYM KEYGEN`: Generate 32-byte key. Returns base64-encoded key. +- `SYM ENCRYPT `: Encrypt message. Returns base64 ciphertext. +- `SYM DECRYPT `: Decrypt. Returns plaintext. + +Example: +```bash +redis-cli SYM KEYGEN +# → base64_key + +redis-cli SYM ENCRYPT base64_key "secret" +# → base64_ciphertext + +redis-cli SYM DECRYPT base64_key base64_ciphertext +# → "secret" +``` + +## RPC Options + +Enable the JSON-RPC server with `--enable-rpc` for database management. Methods include creating databases, managing access keys, and setting encryption. See [JSON-RPC Examples](./rpc_examples.md) for payloads. + # HeroDB Commands HeroDB implements a subset of Redis commands over the Redis protocol. This document describes the available commands and their usage. @@ -575,6 +629,29 @@ redis-cli -p $PORT AGE LIST # 2) "keyname2" ``` +## SYM Commands + +### SYM KEYGEN +Generate a symmetric encryption key. +```bash +redis-cli -p $PORT SYM KEYGEN +# → base64_encoded_32byte_key +``` + +### SYM ENCRYPT +Encrypt a message with a symmetric key. +```bash +redis-cli -p $PORT SYM ENCRYPT "message" +# → base64_encoded_ciphertext +``` + +### SYM DECRYPT +Decrypt a ciphertext with a symmetric key. +```bash +redis-cli -p $PORT SYM DECRYPT +# → decrypted_message +``` + ## Server Information Commands ### INFO @@ -621,3 +698,27 @@ This expanded documentation includes all the list commands that were implemented 10. LINDEX - get element by index 11. LRANGE - get range of elements + +## Updated Database Selection and Access Keys + +HeroDB uses an `Admin DB 0` to control database existence, access, and encryption. Access to data DBs can be public (no key) or private (requires a key). See detailed model in `docs/admin.md`. + +Examples: + +```bash +# Public database (no key required) +redis-cli -p $PORT SELECT 1 +# → OK +``` + +```bash +# Private database (requires access key) +redis-cli -p $PORT SELECT 2 KEY my-db2-access-key +# → OK +``` + +```bash +# Admin DB 0 (requires admin secret) +redis-cli -p $PORT SELECT 0 KEY my-admin-secret +# → OK +``` diff --git a/docs/cmds.md b/docs/cmds.md index 78a6e78..2f61c87 100644 --- a/docs/cmds.md +++ b/docs/cmds.md @@ -122,4 +122,27 @@ redis-cli -p 6379 --rdb dump.rdb # Import to sled redis-cli -p 6381 --pipe < dump.rdb +``` + +## Authentication and Database Selection + +HeroDB uses an `Admin DB 0` to govern database existence, access and per-db encryption. Access control is enforced via `Admin DB 0` metadata. See the full model in `docs/admin.md`. + +Examples: +```bash +# Public database (no key required) +redis-cli -p $PORT SELECT 1 +# → OK +``` + +```bash +# Private database (requires access key) +redis-cli -p $PORT SELECT 2 KEY my-db2-access-key +# → OK +``` + +```bash +# Admin DB 0 (requires admin secret) +redis-cli -p $PORT SELECT 0 KEY my-admin-secret +# → OK ``` \ No newline at end of file diff --git a/docs/rpc_examples.md b/docs/rpc_examples.md new file mode 100644 index 0000000..2b941cc --- /dev/null +++ b/docs/rpc_examples.md @@ -0,0 +1,141 @@ +# HeroDB JSON-RPC Examples + +These examples show full JSON-RPC 2.0 payloads for managing HeroDB via the RPC API (enable with `--enable-rpc`). Methods are named as `hero_`. Params are positional arrays; enum values are strings (e.g., `"Redb"`). Copy-paste into Postman or similar clients. + +## Database Management + +### Create Database +Creates a new database with optional per-database encryption key (stored write-only in Admin DB 0). + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "hero_createDatabase", + "params": [ + "Redb", + { "name": null, "storage_path": null, "max_size": null, "redis_version": null }, + null + ] +} +``` + +With encryption: +```json +{ + "jsonrpc": "2.0", + "id": 2, + "method": "hero_createDatabase", + "params": [ + "Sled", + { "name": "secure-db", "storage_path": null, "max_size": null, "redis_version": null }, + "my-per-db-encryption-key" + ] +} +``` + +### List Databases +Returns array of database infos (id, backend, encrypted status, size, etc.). + +```json +{ + "jsonrpc": "2.0", + "id": 3, + "method": "hero_listDatabases", + "params": [] +} +``` + +### Get Database Info +Retrieves detailed info for a specific database. + +```json +{ + "jsonrpc": "2.0", + "id": 4, + "method": "hero_getDatabaseInfo", + "params": [1] +} +``` + +### Delete Database +Removes physical database file; metadata remains in Admin DB 0. + +```json +{ + "jsonrpc": "2.0", + "id": 5, + "method": "hero_deleteDatabase", + "params": [1] +} +``` + +## Access Control + +### Add Access Key +Adds a hashed access key for private databases. Permissions: `"read"` or `"readwrite"`. + +```json +{ + "jsonrpc": "2.0", + "id": 6, + "method": "hero_addAccessKey", + "params": [2, "my-access-key", "readwrite"] +} +``` + +### List Access Keys +Returns array of key hashes, permissions, and creation timestamps. + +```json +{ + "jsonrpc": "2.0", + "id": 7, + "method": "hero_listAccessKeys", + "params": [2] +} +``` + +### Delete Access Key +Removes key by its SHA-256 hash. + +```json +{ + "jsonrpc": "2.0", + "id": 8, + "method": "hero_deleteAccessKey", + "params": [2, "0123abcd...keyhash..."] +} +``` + +### Set Database Public/Private +Toggles public access (default true). Private databases require access keys. + +```json +{ + "jsonrpc": "2.0", + "id": 9, + "method": "hero_setDatabasePublic", + "params": [2, false] +} +``` + +## Server Info + +### Get Server Stats +Returns stats like total databases and uptime. + +```json +{ + "jsonrpc": "2.0", + "id": 10, + "method": "hero_getServerStats", + "params": [] +} +``` + +## Notes +- Per-database encryption keys are write-only; set at creation and used transparently. +- Access keys are hashed (SHA-256) for storage; provide plaintext in requests. +- Backend options: `"Redb"` (default) or `"Sled"`. +- Config object fields (name, storage_path, etc.) are optional and currently ignored but positional. \ No newline at end of file