eDB — Unified Multi-Model Database v0.1.0

eDB is a lightweight, embeddable multi-model database for EmbeddedOS that unifies SQL (relational), document (NoSQL), and key-value storage under a single Python API and REST interface. Designed for resource-constrained devices, edge computing, and rapid prototyping — with built-in JWT authentication, AES-256-GCM encryption, and an AI-powered natural language query assistant.

Language: Python 3.10+ | Package: pip install edb | License: MIT | Backend: SQLite3

Architecture Overview

SQL Engine: Full relational database powered by SQLite — CREATE, INSERT, SELECT, UPDATE, DELETE, JOINs, indexes.
Document Store: Schema-free JSON document collections with query operators ($eq, $gt, $lt, $in, $regex).
Key-Value Store: Fast in-memory key-value with optional TTL-based expiry and persistence.
REST API: FastAPI-powered HTTP interface for all three engines — serve with edb serve.
eBot AI: Natural language → SQL/NoSQL translation using rule-based NLP.
Security: JWT auth (RBAC), AES-256-GCM encryption at rest, tamper-resistant audit logging.

Quick Start
pip install edb
edb init myproject.sqlite      # Create database
edb shell                      # Interactive SQL shell
edb serve --port 8080          # Launch REST API server

🗃 SQL Engine

Full relational database with SQLite backend. Module: edb.core.relational

Database Initialization

RelationalDB RelationalDB(db_path: str)

Create or open a SQLite database at the given file path. Creates the file and parent directories if they don’t exist.

Parameters
db_pathstrPath to the SQLite database file (e.g., "mydb.sqlite").
Returns

RelationalDB instance.

Example
from edb.core.relational import RelationalDB

db = RelationalDB("mydb.sqlite")
list[dict] db.execute(sql: str, params: tuple = ())

Execute a raw SQL statement. For SELECT queries, returns a list of row dicts. For write operations, commits automatically and returns an empty list.

Parameters
sqlstrSQL statement with optional ? placeholders.
paramstuplePositional parameters for placeholders.
Returns

List of dicts for SELECT; empty list for DDL/DML.

Example
db.execute("""
    CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        email TEXT UNIQUE NOT NULL,
        role TEXT DEFAULT 'user',
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    )
""")

CRUD Operations

list[dict] db.execute("INSERT INTO ...")

Insert one or more rows into a table. Use parameterized queries to prevent SQL injection.

Example
# Single insert
db.execute(
    "INSERT INTO users (name, email, role) VALUES (?, ?, ?)",
    ("Alice", "alice@example.com", "admin")
)

# Batch insert
users = [("Bob", "bob@example.com", "user"), ("Carol", "carol@example.com", "user")]
for name, email, role in users:
    db.execute("INSERT INTO users (name, email, role) VALUES (?, ?, ?)", (name, email, role))
list[dict] db.execute("SELECT ...")

Query rows from one or more tables. Supports WHERE, JOIN, ORDER BY, LIMIT, GROUP BY, HAVING, and aggregate functions.

Example
# Basic select
rows = db.execute("SELECT * FROM users WHERE role = ?", ("admin",))
for row in rows:
    print(f"{row['name']} — {row['email']}")

# Join with aggregation
results = db.execute("""
    SELECT u.name, COUNT(o.id) AS order_count
    FROM users u LEFT JOIN orders o ON u.id = o.user_id
    GROUP BY u.id ORDER BY order_count DESC LIMIT 10
""")
list[dict] db.execute("UPDATE ...")

Update existing rows matching a WHERE condition.

Example
db.execute(
    "UPDATE users SET role = ? WHERE email = ?",
    ("admin", "bob@example.com")
)
list[dict] db.execute("DELETE ...")

Delete rows matching a WHERE condition.

Example
db.execute("DELETE FROM users WHERE email = ?", ("carol@example.com",))

Schema & Index Management

list[dict] db.execute("CREATE INDEX ...")

Create indexes to accelerate queries. Supports unique indexes, composite indexes, and partial indexes.

Example
# Unique index
db.execute("CREATE UNIQUE INDEX idx_users_email ON users(email)")

# Composite index
db.execute("CREATE INDEX idx_orders_user_date ON orders(user_id, created_at)")

# List all tables
tables = db.execute("SELECT name FROM sqlite_master WHERE type='table'")

Interactive Shell

edb shell
$ edb shell
eDB Shell v0.1.0 (SQLite 3.39.0)
Connected to: mydb.sqlite

edb> CREATE TABLE sensors (id INTEGER PRIMARY KEY, name TEXT, value REAL);
OK

edb> INSERT INTO sensors VALUES (1, 'temperature', 23.5);
OK (1 row affected)

edb> SELECT * FROM sensors;
+----+-------------+-------+
| id | name        | value |
+----+-------------+-------+
|  1 | temperature |  23.5 |
+----+-------------+-------+
1 row(s)

edb> .tables
sensors
users

edb> .schema sensors
CREATE TABLE sensors (id INTEGER PRIMARY KEY, name TEXT, value REAL);

edb> .quit

📄 Document Store

Schema-free JSON document collections with query operators. Module: edb.core.document

Collection Management

DocumentStore DocumentStore(db_path: str)

Initialize the document store engine. Uses the same SQLite file as the SQL engine — documents are stored as JSON blobs with auto-generated UUIDs.

Parameters
db_pathstrPath to the database file.
Example
from edb.core.document import DocumentStore

docs = DocumentStore("mydb.sqlite")
Collection docs.collection(name: str)

Get or create a named collection. Collections are created lazily on first insert.

Parameters
namestrCollection name (alphanumeric, underscores).
Returns

Collection handle.

Example
sensors = docs.collection("sensors")
users = docs.collection("users")

Document Operations

str collection.insert(doc: dict)

Insert a JSON document into the collection. Auto-generates a UUID _id field if not provided.

Parameters
docdictJSON-serializable document.
Returns

Document _id string (UUID).

Example
doc_id = sensors.insert({
    "type": "temperature",
    "value": 23.5,
    "unit": "celsius",
    "location": "lab-1",
    "tags": ["indoor", "calibrated"],
    "metadata": {"firmware": "v2.1", "battery": 85}
})
print(f"Inserted: {doc_id}")
list[str] collection.insert_many(docs: list[dict])

Bulk insert multiple documents. Runs in a single transaction for atomicity.

Parameters
docslist[dict]List of documents to insert.
Returns

List of inserted _id strings.

Example
ids = sensors.insert_many([
    {"type": "humidity", "value": 65.2, "unit": "percent"},
    {"type": "pressure", "value": 1013.25, "unit": "hPa"},
    {"type": "temperature", "value": 19.8, "unit": "celsius"}
])
list[dict] collection.find(query: dict = {})

Query documents matching filter criteria. Supports operators: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $regex, $exists.

Parameters
querydictFilter document. Empty dict returns all documents.
Returns

List of matching documents (dicts with _id).

Example
# Exact match
temps = sensors.find({"type": "temperature"})

# Comparison operators
hot = sensors.find({"value": {"$gt": 30}, "type": "temperature"})

# Regex match
lab_sensors = sensors.find({"location": {"$regex": "^lab-"}})

# $in operator
specific = sensors.find({"type": {"$in": ["temperature", "humidity"]}})

# Nested field access
old_fw = sensors.find({"metadata.firmware": {"$lt": "v2.0"}})
dict | None collection.find_one(query: dict)

Find the first document matching the query. Returns None if no match.

Parameters
querydictFilter document.
Returns

Matching document or None.

Example
doc = sensors.find_one({"_id": doc_id})
if doc:
    print(f"Value: {doc['value']} {doc['unit']}")
int collection.update(query: dict, update: dict)

Update all documents matching the query. Supports $set, $unset, $inc, $push, $pull operators.

Parameters
querydictFilter to select documents.
updatedictUpdate operations to apply.
Returns

Number of documents modified.

Example
# Set fields
sensors.update(
    {"type": "temperature", "location": "lab-1"},
    {"$set": {"value": 24.1, "updated_at": "2025-01-15T10:30:00Z"}}
)

# Increment a numeric field
sensors.update({"_id": doc_id}, {"$inc": {"metadata.battery": -1}})

# Push to an array
sensors.update({"_id": doc_id}, {"$push": {"tags": "verified"}})
int collection.delete(query: dict)

Delete all documents matching the query.

Parameters
querydictFilter to select documents for deletion.
Returns

Number of documents deleted.

Example
deleted = sensors.delete({"type": "pressure", "value": {"$lt": 900}})
print(f"Removed {deleted} documents")
int collection.count(query: dict = {})

Count documents matching the query. Empty query counts all documents in the collection.

Parameters
querydictOptional filter.
Returns

Number of matching documents.

Example
total = sensors.count()
temp_count = sensors.count({"type": "temperature"})
print(f"{temp_count}/{total} are temperature readings")

Query Operators Reference

Supported Query Operators

$eqdictEquals (implicit when using direct value match)
$nedictNot equal
$gt / $gtedictGreater than / greater than or equal
$lt / $ltedictLess than / less than or equal
$in / $nindictValue in / not in array
$regexdictRegular expression match
$existsdictField exists (true) or not (false)

Supported Update Operators

$setdictSet field values (create if missing)
$unsetdictRemove fields from document
$incdictIncrement numeric fields
$pushdictAppend value to array field
$pulldictRemove value from array field

🔑 Key-Value Store

Fast in-memory key-value store with optional TTL expiry and disk persistence. Module: edb.core.keyvalue

KeyValueStore KeyValueStore(db_path: str)

Initialize the key-value store. Keys are strings; values are any JSON-serializable type. Supports optional TTL-based expiry for cache-like behavior.

Parameters
db_pathstrPath to the persistence file. Values are loaded into memory at startup.
Example
from edb.core.keyvalue import KeyValueStore

kv = KeyValueStore("mydb.sqlite")
None kv.set(key: str, value: Any, ttl: int | None = None)

Store a key-value pair. If the key already exists, its value is overwritten. Optional TTL in seconds causes automatic expiry.

Parameters
keystrKey string (max 512 bytes).
valueAnyJSON-serializable value (str, int, float, dict, list, bool, None).
ttlint | NoneTime-to-live in seconds. None = no expiry.
Example
# Permanent storage
kv.set("config:version", "2.1.0")
kv.set("device:info", {"name": "sensor-01", "firmware": "v2.1", "calibrated": True})

# Cache with TTL (expires after 5 minutes)
kv.set("cache:weather", {"temp": 22.5, "humidity": 60}, ttl=300)

# Session token (expires after 1 hour)
kv.set("session:abc123", {"user_id": 42, "role": "admin"}, ttl=3600)
Any | None kv.get(key: str)

Retrieve a value by key. Returns None if the key doesn’t exist or has expired.

Parameters
keystrKey to look up.
Returns

Stored value or None.

Example
version = kv.get("config:version")     # "2.1.0"
info = kv.get("device:info")           # {"name": "sensor-01", ...}
missing = kv.get("nonexistent")        # None
bool kv.delete(key: str)

Delete a key-value pair. Returns True if the key existed and was deleted.

Parameters
keystrKey to delete.
Returns

True if deleted, False if key not found.

Example
kv.delete("session:abc123")   # True
kv.delete("nonexistent")      # False
bool kv.exists(key: str)

Check if a key exists and has not expired.

Parameters
keystrKey to check.
Returns

True if key exists and is not expired.

list[str] kv.keys(pattern: str = "*")

List all keys matching a glob pattern. Excludes expired keys.

Parameters
patternstrGlob pattern (* = all, session:* = all sessions).
Returns

List of matching key strings.

Example
all_keys = kv.keys()                   # All keys
sessions = kv.keys("session:*")        # Session keys only
configs = kv.keys("config:*")          # Config keys only
None kv.flush()

Delete all key-value pairs from the store and persist the change to disk.

None kv.cleanup_expired()

Remove all expired keys from the store. Called automatically on get(), but can be invoked manually for batch cleanup.

🌐 REST API

FastAPI-powered HTTP interface for all database engines. Module: edb.api

Server Launch
# Start the API server
edb serve --port 8080

# With authentication enabled
edb serve --port 8080 --auth

# Custom host and database
edb serve --host 0.0.0.0 --port 8080 --db /data/production.sqlite

SQL Endpoints

POST /api/sql/query

Execute a SQL query and return results as JSON.

Request Body
sqlstringSQL statement to execute.
paramsarrayOptional positional parameters.
Example
curl -X POST http://localhost:8080/api/sql/query \
  -H "Content-Type: application/json" \
  -d '{"sql": "SELECT * FROM users WHERE role = ?", "params": ["admin"]}'

# Response:
{
  "columns": ["id", "name", "email", "role"],
  "rows": [
    {"id": 1, "name": "Alice", "email": "alice@example.com", "role": "admin"}
  ],
  "row_count": 1
}
GET /api/sql/tables

List all tables in the database with row counts.

Response

JSON array of {"name": "...", "row_count": N} objects.

GET /api/sql/tables/{table}/schema

Get the schema (column names, types, constraints) of a specific table.

Document Endpoints

POST /api/documents/{collection}

Insert a document into a collection.

Request Body
documentobjectJSON document to insert.
Example
curl -X POST http://localhost:8080/api/documents/sensors \
  -H "Content-Type: application/json" \
  -d '{"document": {"type": "temperature", "value": 23.5}}'

# Response:
{"_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "status": "inserted"}
POST /api/documents/{collection}/find

Query documents with MongoDB-style filter operators.

Request Body
queryobjectFilter document with optional operators.
Example
curl -X POST http://localhost:8080/api/documents/sensors/find \
  -H "Content-Type: application/json" \
  -d '{"query": {"type": "temperature", "value": {"$gt": 20}}}'
PUT /api/documents/{collection}

Update documents matching a query.

Request Body
queryobjectFilter to select documents.
updateobjectUpdate operations ($set, $inc, etc.).
DELETE /api/documents/{collection}

Delete documents matching a query.

Request Body
queryobjectFilter to select documents for deletion.

Key-Value Endpoints

GET /api/kv/{key}

Retrieve a value by key.

Response

{"key": "...", "value": ..., "ttl_remaining": N|null}

Example
curl http://localhost:8080/api/kv/config:version

# Response:
{"key": "config:version", "value": "2.1.0", "ttl_remaining": null}
PUT /api/kv/{key}

Set a key-value pair with optional TTL.

Request Body
valueanyJSON-serializable value.
ttlinteger | nullOptional TTL in seconds.
Example
curl -X PUT http://localhost:8080/api/kv/cache:token \
  -H "Content-Type: application/json" \
  -d '{"value": "abc123xyz", "ttl": 3600}'
DELETE /api/kv/{key}

Delete a key-value pair.

Response

{"key": "...", "deleted": true|false}

GET /api/kv

List all keys, optionally filtered by glob pattern via ?pattern=session:* query parameter.

Response

{"keys": ["key1", "key2", ...], "count": N}

🔒 Authentication

JWT-based authentication with role-based access control (RBAC). Module: edb.auth

RBAC Roles

admin: Full access — create/drop tables, manage users, read/write all data.
read_write: Read and write data, create indexes. Cannot drop tables or manage users.
read_only: Read-only access to all queries. Cannot modify any data.

Auth Endpoints

POST /api/auth/register

Register a new user account. First registered user automatically gets admin role.

Request Body
usernamestringUnique username (3-64 chars).
passwordstringPassword (min 8 chars, hashed with bcrypt).
rolestringOptional: "admin", "read_write", or "read_only" (default).
Example
curl -X POST http://localhost:8080/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{"username": "alice", "password": "secureP@ss123", "role": "admin"}'

# Response:
{"user_id": 1, "username": "alice", "role": "admin", "created": true}
POST /api/auth/login

Authenticate and receive access + refresh token pair. Access token expires in 15 minutes; refresh token in 7 days.

Request Body
usernamestringRegistered username.
passwordstringUser password.
Example
curl -X POST http://localhost:8080/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "alice", "password": "secureP@ss123"}'

# Response:
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer",
  "expires_in": 900,
  "role": "admin"
}
POST /api/auth/refresh

Exchange a valid refresh token for a new access token.

Request Body
refresh_tokenstringValid refresh token.
Response

New access_token with fresh expiry.

GET /api/auth/me

Get the currently authenticated user’s profile. Requires Authorization: Bearer <token> header.

Response

{"user_id": N, "username": "...", "role": "...", "created_at": "..."}

Using Authenticated Endpoints
# All API endpoints require the Authorization header when --auth is enabled:
curl -X POST http://localhost:8080/api/sql/query \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{"sql": "SELECT * FROM users"}'

Token Structure

JWT Access Token Payload

substringUsername (subject)
rolestringUser role (admin, read_write, read_only)
iatintegerIssued-at timestamp (Unix epoch)
expintegerExpiration timestamp (Unix epoch)
typestring"access" or "refresh"

🛡 Security

Encryption at rest, audit logging, and input sanitization. Module: edb.security

Encryption

EncryptionManager EncryptionManager(key: bytes)

Initialize AES-256-GCM encryption engine. All data at rest is encrypted with this key. The key must be exactly 32 bytes.

Parameters
keybytes32-byte encryption key. Generate with os.urandom(32) or derive from passphrase with PBKDF2.
Example
from edb.security import EncryptionManager
import os

key = os.urandom(32)
enc = EncryptionManager(key)

ciphertext = enc.encrypt(b"sensitive data")
plaintext = enc.decrypt(ciphertext)  # b"sensitive data"
bytes enc.encrypt(plaintext: bytes)

Encrypt data using AES-256-GCM. Returns nonce + ciphertext + tag (authenticated encryption).

Parameters
plaintextbytesData to encrypt.
Returns

Encrypted bytes (12-byte nonce + ciphertext + 16-byte auth tag).

bytes enc.decrypt(ciphertext: bytes)

Decrypt and verify AES-256-GCM ciphertext. Raises InvalidTag if data has been tampered with.

Parameters
ciphertextbytesEncrypted data from encrypt().
Returns

Decrypted plaintext bytes.

Audit Logging

AuditLogger AuditLogger(db_path: str)

Initialize tamper-resistant audit log. Each entry is hash-chained to the previous entry using SHA-256, forming an immutable ledger.

Parameters
db_pathstrPath to audit log database (separate from main DB).
Example
from edb.security import AuditLogger

audit = AuditLogger("audit.sqlite")
audit.log("alice", "QUERY", "SELECT * FROM users")
audit.log("bob", "INSERT", "INSERT INTO orders VALUES (...)")

# Verify chain integrity
is_valid = audit.verify_chain()  # True if untampered
None audit.log(user: str, action: str, detail: str)

Record an audit event with timestamp, user, action type, and detail. Auto-computes hash chain.

Parameters
userstrUsername performing the action.
actionstrAction type: QUERY, INSERT, UPDATE, DELETE, LOGIN, REGISTER, ADMIN.
detailstrSQL statement or action description.
bool audit.verify_chain()

Verify the integrity of the entire audit chain. Returns False if any entry has been modified or deleted.

Returns

True if chain is intact.

list[dict] audit.query(user: str = None, action: str = None, limit: int = 100)

Query audit log entries with optional filters.

Parameters
userstrFilter by username (optional).
actionstrFilter by action type (optional).
limitintMaximum entries to return (default 100).
Returns

List of audit entries with id, timestamp, user, action, detail, hash.

Input Sanitization

Built-in Protections

SQL InjectionPreventionParameterized queries enforced; raw string interpolation rejected
NoSQL InjectionPreventionQuery operators validated against whitelist ($eq, $gt, etc.)
Prompt InjectionPreventioneBot input sanitized; dangerous SQL keywords blocked from NL input
XSSPreventionAll REST API outputs JSON-encoded; no HTML rendering
Path TraversalPreventionDatabase paths validated and sandboxed

🤖 eBot AI

Natural language to SQL/NoSQL query translation. Module: edb.ebot

How eBot Works

eBot uses rule-based NLP to translate natural language questions into SQL or NoSQL queries. It parses intent, extracts entities (table names, column names, operators), and generates safe parameterized queries. No external LLM required — runs entirely offline.

Python API

EBot EBot(db: RelationalDB, doc_store: DocumentStore = None)

Initialize eBot with database references. Introspects table schemas to understand column names and types.

Parameters
dbRelationalDBSQL engine instance for schema introspection.
doc_storeDocumentStoreOptional document store for NoSQL queries.
Example
from edb.ebot import EBot

bot = EBot(db, doc_store=docs)
result = bot.query("show me all admin users")
# Translates to: SELECT * FROM users WHERE role = 'admin'
print(result)
dict bot.query(question: str)

Translate a natural language question into a query and execute it. Returns both the generated query and results.

Parameters
questionstrNatural language question (English).
Returns

Dict with query (generated SQL/NoSQL), query_type ("sql" or "nosql"), results (data), and row_count.

Example
# SQL translation examples
bot.query("how many users are there?")
# → SELECT COUNT(*) AS count FROM users

bot.query("show the top 5 orders by amount")
# → SELECT * FROM orders ORDER BY amount DESC LIMIT 5

bot.query("what is the average temperature reading?")
# → SELECT AVG(value) AS average FROM sensors WHERE type = 'temperature'

# NoSQL translation
bot.query("find all temperature sensors in lab-1")
# → collection("sensors").find({"type": "temperature", "location": "lab-1"})
str bot.explain(question: str)

Translate without executing — returns the generated query string for review.

Parameters
questionstrNatural language question.
Returns

Generated SQL or NoSQL query string.

REST Endpoint

POST /api/ebot/query

Send a natural language query via the REST API.

Request Body
questionstringNatural language question.
explain_onlybooleanIf true, return the generated query without executing.
Example
curl -X POST http://localhost:8080/api/ebot/query \
  -H "Content-Type: application/json" \
  -d '{"question": "show me all users who registered this week"}'

# Response:
{
  "question": "show me all users who registered this week",
  "query_type": "sql",
  "generated_query": "SELECT * FROM users WHERE created_at >= date('now', '-7 days')",
  "results": [...],
  "row_count": 3
}

🔍 Query Engine

Full-text search, graph queries, and advanced query features. Module: edb.query

Full-Text Search

None db.create_fts_index(table: str, columns: list[str])

Create a full-text search index on specified columns using SQLite FTS5. Enables fast text search with ranking.

Parameters
tablestrTable to index.
columnslist[str]Columns to include in the FTS index.
Example
db.create_fts_index("articles", ["title", "body"])

# Search with ranking
results = db.search("articles", "embedded operating system", limit=10)
for r in results:
    print(f"[{r['rank']:.2f}] {r['title']}")
list[dict] db.search(table: str, query: str, limit: int = 20)

Execute a full-text search query with BM25 ranking. Returns results sorted by relevance.

Parameters
tablestrTable with FTS index.
querystrSearch query (supports AND, OR, NOT, phrase matching with quotes).
limitintMaximum results to return (default 20).
Returns

List of matching rows with rank score field (lower = more relevant).

Example
# Boolean search
results = db.search("articles", "embedded AND system NOT windows")

# Phrase search
results = db.search("articles", '"real-time operating system"')

# Prefix matching
results = db.search("articles", "micro*")

REST Search Endpoint

POST /api/search/{table}

Full-text search via REST API.

Request Body
querystringSearch query string.
limitintegerMax results (default 20).
Example
curl -X POST http://localhost:8080/api/search/articles \
  -H "Content-Type: application/json" \
  -d '{"query": "embedded database", "limit": 5}'

Planned Features

Roadmap

Graph Data Model (v0.2.0): Vertex/edge storage with Cypher-like traversal queries — db.graph.add_vertex(), db.graph.traverse().
LLM-Powered eBot (v0.3.0): Optional LLM backend for complex natural language queries, multi-table joins, and conversational context.
Vector Search (v0.3.0): Embedding storage and approximate nearest-neighbor search for AI/ML workloads.
Replication (v0.4.0): Primary-replica replication for high availability on edge clusters.

💻 CLI Reference

edb Commands

edb init <path>Create a new database file at the given path
edb shellOpen interactive SQL shell (supports .tables, .schema, .quit)
edb serveStart the REST API server (FastAPI + Uvicorn)
edb migrateRun pending schema migrations from migrations/ directory

edb serve Options

--hoststringBind address (default: 127.0.0.1)
--portintegerListen port (default: 8080)
--dbstringDatabase file path (default: ./edb.sqlite)
--authflagEnable JWT authentication
--encryptflagEnable AES-256-GCM encryption at rest
--auditflagEnable audit logging
--corsstringCORS allowed origins (default: *)
--workersintegerNumber of Uvicorn workers (default: 1)

edb shell Commands

.tablesList all tables
.schema <table>Show CREATE statement for a table
.collectionsList all document collections
.kvList all key-value keys
.export <table> <file>Export table to CSV
.import <file> <table>Import CSV into table
.quitExit the shell