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