# 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`