7.6 KiB
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 0lives at<base_dir>/0.db- It is always encrypted using the
admin secretprovided at process startup (using the--admin-secret <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")
- String counter holding the next id to allocate (initialized to
-
admin:dbs- A hash acting as a set of existing database ids
- field = id (as string), value =
"1"
-
meta:db:<id>- A hash holding db-level metadata
- field
public="true"or"false"(defaults totrueif missing)
-
meta:db:<id>:keys- A hash mapping access-key hashes to the string
Permission:created_at_seconds - Examples:
Read:1713456789orReadWrite:1713456789 - The plaintext access keys are never stored; only their
SHA-256hashes are kept
- A hash mapping access-key hashes to the string
-
meta:db:<id>:enc- A string holding the per-database encryption key used to open
<id>.dbencrypted - This value is write-only from the perspective of the management APIs (it’s set at creation and never returned)
- A string holding the per-database encryption key used to open
-
age:key:<name>- Base64-encoded X25519 recipient (public encryption key) for named AGE keys
-
age:privkey:<name>- Base64-encoded X25519 identity (secret encryption key) for named AGE keys
-
age:signpub:<name>- Base64-encoded Ed25519 verify public key for named AGE keys
-
age:signpriv:<name>- 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
- Create a database (via JSON-RPC)
- The server allocates an id from
admin:next_id, registers it inadmin:dbs, and defaults the database topublic=true - If you pass an optional
encryption_keyduring creation, the server persists it inmeta:db:<id>:enc. That database will be opened in encrypted mode from then on
- Open and use a database
- Clients select a database over RESP using
SELECT - Authorization and encryption state are enforced using
DB 0metadata
- Delete database files
- Removing
<id>.dbremoves the physical storage DB 0remains 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 <id>with no key, and will getReadWritepermission
- Anyone can
- Private database
- You must provide an access key when selecting the database
- The server hashes the provided key with
SHA-256and checks membership inmeta:db:<id>:keys - Permissions are
ReadorReadWritedepending on how the key was added
- Admin
DB 0- Requires the exact admin secret as the
KEYargument toSELECT 0 - Permission is
ReadWritewhen the secret matches
- Requires the exact admin secret as the
Connections start with no database selected. Any command that requires storage (GET, SET, H*, L*, SCAN, etc.) will return an error until you issue a SELECT to choose a database. Admin DB 0 is never accessible without authenticating via SELECT 0 KEY <admin_secret>.
How to select databases with optional KEY
-
Public DB (no key required)
SELECT <id>
-
Private DB (access key required)
SELECT <id> KEY <plaintext_key>
-
Admin
DB 0(admin secret required)SELECT 0 KEY <admin_secret>
Examples (using redis-cli):
# 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 0asmeta:db:<id>:enc - When you later open the database, the engine checks whether
meta:db:<id>:encexists to decide if it must open<id>.dbin 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_keyto thecreateDatabaseRPC - Open later: simply
SELECTthe 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.
- Returns details: backend, encrypted flag, size on disk,
addAccessKey(db_id, key, permissions)- Adds a
ReadorReadWriteaccess key (permissions ="read"|"readwrite")
- Adds a
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.
Typical flows
- Public, unencrypted database
- Create a new database without an encryption key
- Clients can immediately
SELECT <id>without a key - You can later make it private and add keys if needed
- Private, encrypted database
- Create passing an
encryption_key - Mark it private (
setDatabasePublic false) and add access keys - Clients must use
SELECT <id> KEY <plaintext_access_key> - Storage opens in encrypted mode automatically
Security notes
- Only
SHA-256hashes of access keys are stored inDB 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 0and perform administrative actions
Troubleshooting
-
ERR invalid access keywhen selecting a private db- Ensure you passed the
KEYargument:SELECT <id> KEY <plaintext_key> - If you recently added the key, confirm the permissions and that you used the exact plaintext (hash must match)
- Ensure you passed the
-
Database X not found- The id isn’t registered in
DB 0(admin:dbs). Use the management APIs to create or list databases
- The id isn’t registered in
-
Cannot
SELECT 0- The
KEYmust be the exact admin secret passed at server startup
- The
Reference
- Admin metadata lives in
DB 0(0.db) and controls:- Existence:
admin:dbs - Access:
meta:db:<id>.publicandmeta:db:<id>:keys - Encryption:
meta:db:<id>:enc
- Existence:
For command examples and management payloads:
- RESP command basics: docs/basics.md
- Supported commands: docs/cmds.md
- JSON-RPC examples: docs/rpc_examples.md