# KalamDB: full documentation corpus > Single-file Markdown corpus of current KalamDB and KoldStore documentation for > LLM context windows, AI search engines, and developer agents. Prefer this file > when you need dense technical detail in one request. Prefer > [https://kalamdb.org/llms.txt](https://kalamdb.org/llms.txt) for a short index. - Site: https://kalamdb.org - Index: https://kalamdb.org/llms.txt - Sitemap: https://kalamdb.org/sitemap.xml - Generated: 2026-07-31 - Scope: current docs under `content/` (archived version folders omitted) - License: Apache 2.0 for KalamDB / KoldStore product docs on this site ## How to use this file - Read the index at `/llms.txt` first for routing. - Use this corpus for install steps, SQL APIs, architecture, and SDK reference. - Cite canonical page URLs included under each section header. - KoldStore (PostgreSQL tiered storage) lives under `/docs/pg-koldstore`. - pg_kalam (FDW to KalamDB) lives under `/docs/pg-kalam` — different product. ================================================================================ # Documentation Home URL: https://kalamdb.org/docs Source: content/index.mdx > Browse KalamDB documentation by product area: server, pg_kalam, pg Koldstore, TypeScript SDK, Rust SDK, Dart SDK, and use cases. # KalamDB Documentation KalamDB docs are organized by the part of the stack you are working with. Start with the server docs for running KalamDB and SQL, then jump into an SDK, a PostgreSQL extension, or implementation patterns. ## Choose A Docs Area - [Server](https://kalamdb.org/docs/server): run KalamDB, configure auth, use SQL, operate storage, and deploy production services. - [CLI](https://kalamdb.org/docs/cli): install `kalam`, scaffold projects with `kalam init`, run `kalam dev`, manage migrations, and use the SQL client. - [pg_kalam PostgreSQL Extension](https://kalamdb.org/docs/pg-kalam): connect PostgreSQL to KalamDB through the pg_kalam extension. - [pg Koldstore](https://kalamdb.org/docs/pg-koldstore): tier hot PostgreSQL rows and cold Parquet on one table — install, manage, flush, merge scan, and `changes_since`. - [TypeScript SDK](https://kalamdb.org/docs/ts-sdk): build browser apps, services, workers, and agents with the TypeScript packages. - [Rust SDK](https://kalamdb.org/docs/rust-sdk): native async client for services and workers — install [`kalam-client`](https://crates.io/crates/kalam-client) from crates.io. - [Dart / Flutter SDK](https://kalamdb.org/docs/dart-sdk): connect Flutter and Dart apps to KalamDB with realtime subscriptions. - [Use Cases](https://kalamdb.org/docs/use-cases): runnable TypeScript example apps from the `examples/` folder. ## Start Here 1. [Run KalamDB locally in minutes](https://kalamdb.org/docs/server/getting-started) 2. [Create tables and query data with SQL](https://kalamdb.org/docs/server/sql-reference) 3. [Connect a TypeScript app or service](https://kalamdb.org/docs/ts-sdk/setup) 4. [Install the Rust SDK from crates.io](https://kalamdb.org/docs/rust-sdk/setup) and try the [Quickstart example](https://kalamdb.org/docs/rust-sdk/examples/quickstart) 5. [Use pg_kalam for PostgreSQL-facing workflows](https://kalamdb.org/docs/pg-kalam/getting-started) 6. [Try pg Koldstore for tiered hot/cold tables](https://kalamdb.org/docs/pg-koldstore/getting-started) 7. [Review production security settings](https://kalamdb.org/docs/server/security) ================================================================================ # Benchmarks URL: https://kalamdb.org/docs/pg-koldstore/benchmarks Source: content/pg-koldstore/benchmarks.mdx > Published KoldStore storage and VACUUM FULL wins on a 10M-row sample, plus contextual DML and PK lookup trade-offs. # Benchmarks KoldStore is a **storage lifecycle** tool — not a universal query accelerator. The primary published wins are footprint and whole-table maintenance after flush. DML and cold-path lookups are contextual single samples, not release guarantees. Source sample: 10M wide rows, `hot_row_limit = 100000`, local PostgreSQL 16.13, 2026-07-31, single wiped pgrx instance per side (draft publication methodology in the [koldstore repo](https://github.com/kalamdb/koldstore/tree/main/docs/benchmarks)). ## Storage and maintenance wins | Result | Before → after flush | Trade-off | |--------|----------------------|-----------| | Total footprint (hot + cold) | 5.85 GiB → 671 MiB | **89%** smaller | | Hot in PostgreSQL (heap + `__cl`) | 5.85 GiB → 72 MiB | **99%** smaller | | Cold Parquet | — → ~599 MiB | Outside the database | | Indexes (hot + `__cl`) | 415 MiB → 11.5 MiB | **97%** smaller | | `VACUUM (FULL, ANALYZE)` | 227.02 s → 3.21 s | **~71×** faster | The VACUUM timing is specifically `VACUUM (FULL, ANALYZE)` — a whole-table rewrite. Routine autovacuum was disabled for the benchmark and is not covered by that number. ## DML and query path (contextual) Foreground DML and PK lookups are **not** guaranteed to improve. | Operation | PostgreSQL only | KoldStore (WAL) | Notes | |-----------|-----------------|-----------------|-------| | INSERT | 47,202 ops/s | 101,325 ops/s | Noise / order — not a product win | | UPDATE | 63,453 ops/s | 55,350 ops/s | Single sample; ~13% lower | | DELETE | 36,905 ops/s | 140,874 ops/s | Do not claim faster from one sample | | Hot-only PK lookup | 3,990 ops/s | 3,818 ops/s | ≈ same | | Hot+cold PK lookup | 4,204 ops/s | 1,055 ops/s | ~75% slower vs full-heap baseline | | Cold-only PK lookup | 4,182 ops/s | 653 ops/s | ~84% slower vs full-heap baseline | Cold-path lookups compare Parquet merge against a full-heap PostgreSQL baseline. Treat them as indicative. ## How to read the numbers - Lead with storage + VACUUM when explaining the product - Do not market single-sample INSERT/DELETE “speedups” - Expect cold PK latency until cold lookup work lands on the roadmap - Results vary by schema, hardware, storage backend, and workload Product page summary: [/koldstore](https://kalamdb.org/koldstore). ================================================================================ # Change Feed URL: https://kalamdb.org/docs/pg-koldstore/change-feed Source: content/pg-koldstore/change-feed.mdx > Use koldstore.changes_since for exclusive seq catch-up across hot and cold, including last_rows newest-N rewind. # Change Feed `koldstore.changes_since` is the packaged SQL change API for managed tables. It merges the hot change-log mirror and cold Parquet into one **latest-state** stream ordered by `seq`. This is catch-up for clients (and aligns with KalamDB subscribe semantics for `from` / newest-N). It is **not** a full temporal audit log of every historical event. ## Signature ```sql koldstore.changes_since( table_name regclass, since_seq bigint DEFAULT 0, -- exclusive resume (seq > since_seq) limit_rows integer DEFAULT 1000, -- forward page size last_rows integer DEFAULT NULL -- newest-N rewind when since_seq = 0 ) RETURNS TABLE ( seq bigint, op smallint, -- 1=INSERT, 2=UPDATE, 3=DELETE pk jsonb, deleted boolean, row_image jsonb, source text -- 'hot' | 'cold' ) ``` ## Modes | Mode | Arguments | Behavior | |------|-----------|----------| | Resume | `since_seq > 0` | Rows with `seq > since_seq`, ascending, capped by `limit_rows`. **`last_rows` is ignored.** | | Rewind | `since_seq = 0` and `last_rows` set | Newest N retained changes, delivered **oldest → newest** | | From start | `since_seq = 0`, no `last_rows` | Start of retained history | Precedence rule: a positive `since_seq` always wins over `last_rows`. ## Examples Fence first when you need a strong boundary against the heap: ```sql SELECT koldstore.wait_for_async_mirror(); ``` ### Page from the beginning ```sql SELECT seq, op, pk, deleted, source FROM koldstore.changes_since( table_name => 'app.messages'::regclass, since_seq => 0, limit_rows => 100 ); ``` ### Resume after the last consumed seq ```sql SELECT seq, op, pk, deleted, source FROM koldstore.changes_since( table_name => 'app.messages'::regclass, since_seq => 184467440737095516, -- last seq you already applied limit_rows => 500 ); ``` Advance your cursor from the **highest** `seq` you successfully processed. ### Newest N (subscribe-style rewind) ```sql SELECT seq, op, pk, deleted, source FROM koldstore.changes_since( table_name => 'app.messages'::regclass, since_seq => 0, limit_rows => 1000, last_rows => 50 ); ``` `last_rows` must be `<= limit_rows`. ### After flush The same cursor continues to return flushed rows from cold (`source = 'cold'`). Do not treat “mirror empty” as “caught up” if cold still holds retained history. ## Semantics and caveats - **Exclusive** cursor: `seq > since_seq`, never inclusive. - Authoritative `seq` values come only from the serialized WAL applier. - Latest-state per PK — updates replace prior images; deletes appear as tombstones until pruned by flush retention rules. - `since_seq = 0` means “start of retained history,” not “I fell behind retention.” - A **positive** `since_seq` older than the retained cold floor raises a **retention-gap error** — do not treat an empty page as success in that case. - Hot + cold merge; `source` tells you which tier contributed the row image. ## Direct mirror SQL (advanced) You can inspect the hot mirror for debugging: ```sql SELECT id, seq, op FROM koldstore.messages__cl WHERE seq > $1 ORDER BY seq LIMIT 100; ``` Prefer `changes_since` in application code so cold history stays visible after flush and retention-gap errors stay consistent. ## Next - [SQL API Reference](https://kalamdb.org/docs/pg-koldstore/sql-api) - [Under the Hood](https://kalamdb.org/docs/pg-koldstore/concepts) ================================================================================ # Under the Hood URL: https://kalamdb.org/docs/pg-koldstore/concepts Source: content/pg-koldstore/concepts.mdx > How KoldStore captures committed WAL into a latest-state mirror, flushes to Parquet, and merges hot and cold with KoldMergeScan. # Under the Hood KoldStore keeps PostgreSQL authoritative for the hot tier and stores flushed history as open Parquet plus catalog metadata. This page explains the moving parts so API behavior makes sense. ## End-to-end path ```text heap commit → logical slot (PK-only pgoutput) → database worker → koldstore.__cl (latest-state mirror) → flush (policy or force) → Parquet segments + manifest → SELECT via KoldMergeScan (newest visible wins) ``` There is **no** trigger/strict capture path today. Foreground DML writes the heap only; the serialized WAL applier allocates authoritative `seq` values. ## Hot tier - Ordinary heap table with a primary key - Application columns only (clean schema — no KoldStore columns on the user table) - Native indexes cover **hot rows only** after flush - Transactions, locking, MVCC, RLS, and permissions stay PostgreSQL-native ## Change-log mirror (`koldstore.
__cl`) One row per primary key (latest state), not an append-only event log: | Column | Meaning | |--------|---------| | PK columns | Same as the heap | | `seq` | Snowflake effect id — ordering / flush cutoffs / change cursor | | `op` | `1` = INSERT, `2` = UPDATE, `3` = DELETE | | `commit_lsn` | Diagnostics only — not the public resume cursor | Tombstones (`op = 3`) remain until flush prunes them. `seq` is **not** a commit LSN and is not a commit-order guarantee beyond “allocated by the serialized applier.” ## WAL capture consistency | Mode | Behavior | |------|----------| | Normal | Worker polls (~100 ms) and applies available WAL | | Strong fence | `koldstore.wait_for_async_mirror()` drains to a boundary | | Flush | Calls the same fence automatically | Lag is expected. Call the fence before comparing heap state to mirror/`seq` cursors when you need a strong boundary. Other capture rules: - Primary-key `UPDATE`s are rejected (BEFORE UPDATE OF pk guard) - `TRUNCATE` on async managed tables is rejected - Publication `koldstore_async_mirror` + one deterministic logical slot per database are auto-managed ## Cold tier - Parquet segments under a registered storage backend - Manifest metadata in PostgreSQL catalogs (`koldstore.manifest`, `koldstore.cold_segments`, …) - Object storage is **not** WAL-protected — back up PostgreSQL **and** the cold prefix together Default path templates: - Shared: `{namespace}/{tableName}/` - User-scoped (intended): `{namespace}/{tableName}/{scopeId}/` Scoped cold folders and full per-`user_id` physical layout are still hardening; session `koldstore.user_id` is required for user-typed tables today. ## Flush policy (intuition) Example: `hot_row_limit = 10000`, `min_flush_rows = 1000`, `max_rows_per_file = 1000` | Mirror rows | Result | |-------------|--------| | 10,505 | No flush (505 excess < min) | | 11,000 | Flush 1,000 → 1 file | | 11,250 | Flush 1,000, keep 10,250 hot | Age policy uses `koldstore_move_after` (for example `'90 days'`) measured from mirror `seq`. It is mutually exclusive with `hot_row_limit`. `koldstore_move_when` is reserved and fails closed. ## KoldMergeScan ```text KoldMergeScan ├── native hot child (Index / Bitmap / Seq Scan) ├── KoldParquetScan └── MirrorOverlay (inserts / updates / tombstones) ``` Contracts that matter for operators: - Cold-capable predicates → `KoldMergeScan` with hot child + planned cold access - Hot-only / cold-proven-empty PK lookups may keep a **native** plan with **no** wrapper (intentional performance path) - `koldstore.enable_merge_scan = off` → **ERROR** at execution (never silent hot-only) - Unavailable cold storage → query **fails** (no partial hot-only results) ## Catalog touchpoints Useful relations/functions while debugging: - `koldstore.schemas` — managed table options - `koldstore.jobs` — migrate / flush progress - `koldstore.describe_table(...)` — hot/mirror/cold counts, sizes, pending jobs - `koldstore.async_mirror_status()` — slot lag, retained bytes, health ## Next - [Manage & Flush](https://kalamdb.org/docs/pg-koldstore/manage-and-flush) — how to drive policy and jobs - [Change Feed](https://kalamdb.org/docs/pg-koldstore/change-feed) — exclusive `seq` catch-up - [SQL API Reference](https://kalamdb.org/docs/pg-koldstore/sql-api) — full function list ================================================================================ # Getting Started URL: https://kalamdb.org/docs/pg-koldstore/getting-started Source: content/pg-koldstore/getting-started.mdx > Register storage, manage a heap table, fence the async mirror, flush to Parquet, query one table, and page the change feed. # Getting Started This walkthrough creates a managed table on local filesystem storage, flushes excess rows to Parquet, and shows both merge-scan reads and the change feed. Prerequisites: [Installation](https://kalamdb.org/docs/pg-koldstore/installation) completed — `shared_preload_libraries` includes `koldstore`, `wal_level=logical`, and `CREATE EXTENSION koldstore` succeeded. ## 1. Register storage ```sql SELECT koldstore.register_storage( name => 'local-dev', storage_type => 'filesystem', base_path => '/tmp/koldstore-demo', credentials => '{}'::jsonb, config => '{}'::jsonb ); ``` `storage_type` may also be `s3`, `gcs`, or `azure`. For MinIO-style S3: ```sql SELECT koldstore.register_storage( name => 'minio', storage_type => 's3', base_path => 'koldstore-bucket', credentials => '{"access_key_id":"minioadmin","secret_access_key":"minioadmin"}'::jsonb, config => '{"endpoint":"http://127.0.0.1:9000","region":"us-east-1","path_style":true}'::jsonb ); ``` ## 2. Create a normal heap table Managed tables need a **primary key**. Supported types today include `boolean`, integers, `real`, `double precision`, `text`, `varchar`, `uuid`, `jsonb`, and `timestamptz`. ```sql CREATE SCHEMA IF NOT EXISTS app; CREATE TABLE app.messages ( id bigint PRIMARY KEY, body text NOT NULL, created_at timestamptz NOT NULL DEFAULT now() ); INSERT INTO app.messages (id, body) SELECT gs, 'row ' || gs FROM generate_series(1, 1012) AS gs; ``` ## 3. Manage the table Two equivalent styles: **A. `manage_table` (structured options)** ```sql SELECT koldstore.manage_table( table_name => 'app.messages', storage => 'local-dev', hot_row_limit => 1000, min_flush_rows => 1, max_rows_per_file => 500, migration_order_by => 'id' ); ``` **B. `ALTER TABLE` options** ```sql ALTER TABLE app.messages SET ( koldstore_enabled = true, koldstore_storage = 'local-dev', koldstore_hot_row_limit = 1000, koldstore_min_flush_rows = 1, koldstore_max_rows_per_file = 500 ); ``` Do **not** turn management off with `koldstore_enabled = false` — use `koldstore.unmanage_table(...)` instead. Storage cannot be changed after manage. With `hot_row_limit` set, `manage_table` rejects non-PK `UNIQUE` constraints and foreign keys (those stay hot-only after flush). See [Status & Limits](https://kalamdb.org/docs/pg-koldstore/limitations). ## 4. Fence the async mirror Foreground DML writes the heap only. A database worker applies committed WAL into the latest-state mirror (`koldstore.messages__cl`) with short lag. Before work that must observe every commit visible at the start of the call: ```sql SELECT koldstore.wait_for_async_mirror(); ``` `flush_table` runs this fence automatically. ## 5. Page the change feed ```sql SELECT seq, op, pk, deleted, source FROM koldstore.changes_since( table_name => 'app.messages'::regclass, since_seq => 0, limit_rows => 100 ); ``` Resume with an exclusive cursor (`seq > since_seq`). Or rewind to the newest N rows (delivered oldest → newest) when `since_seq = 0`: ```sql SELECT seq, op, pk, deleted, source FROM koldstore.changes_since( table_name => 'app.messages'::regclass, since_seq => 0, limit_rows => 1000, last_rows => 50 ); ``` Full semantics: [Change Feed](https://kalamdb.org/docs/pg-koldstore/change-feed). ## 6. Flush excess rows to cold storage ```sql SELECT koldstore.flush_table(table_name => 'app.messages'::regclass); ``` With `hot_row_limit = 1000` and more than 1000 mirror keys, the oldest excess rows (by mirror `seq`) move to Parquet and leave the hot heap. Inspect status: ```sql SELECT jsonb_pretty( koldstore.describe_table(table_name => 'app.messages'::regclass) ); ``` ## 7. Query one table ```sql SELECT count(*) FROM app.messages; -- hot + cold via KoldMergeScan SELECT * FROM app.messages WHERE id = 1; ``` After flush, cold-capable plans show `KoldMergeScan`. Hot-only PK lookups that cannot hit cold may keep a native Index/Seq/Bitmap Scan with no wrapper — that path is intentional and performance-critical. ## What just happened 1. PostgreSQL still owns transactions, MVCC, permissions, and the hot heap 2. WAL capture wrote latest-state rows into `koldstore.messages__cl` 3. Flush published Parquet segments and pruned flushed hot/mirror rows 4. `SELECT` merges hot + cold; `changes_since` pages the same latest-state stream Next: [Under the Hood](https://kalamdb.org/docs/pg-koldstore/concepts) for the architecture, or [Manage & Flush](https://kalamdb.org/docs/pg-koldstore/manage-and-flush) for policy details. ================================================================================ # pg Koldstore URL: https://kalamdb.org/docs/pg-koldstore Source: content/pg-koldstore/index.mdx > Keep hot rows in PostgreSQL, flush history to open Parquet, and query one table with KoldStore — install, manage, flush, merge scan, and changes_since. # pg Koldstore **KoldStore** is an open-source PostgreSQL extension for forever-growing app tables. It keeps the active working set in the PostgreSQL heap, moves older rows to compressed Parquet you control, and still lets you query **one table** with normal SQL. This is different from [pg_kalam](https://kalamdb.org/docs/pg-kalam): pg_kalam is a foreign-data wrapper that talks to a KalamDB server. KoldStore runs **inside** your PostgreSQL instance and tiers storage for local heap tables. ## What you get | Tier | Where it lives | Optimized for | |------|----------------|---------------| | Hot | PostgreSQL heap + native indexes | Active data, low-latency reads, transactional writes | | Cold | Open Parquet (filesystem, S3/MinIO, GCS, Azure) | History, cost, retention | Placement is **policy-driven** (`hot_row_limit` or age via `move_after`), not access-frequency measurement. Reads go through `KoldMergeScan` so the newest visible row wins across both tiers. Catch-up clients use `koldstore.changes_since` over an exclusive `seq` cursor. ## Good fit today - Messages and chat history - AI memory / conversation transcripts - Audit logs and append-heavy events - Notifications, activity feeds, IoT telemetry ## Not a good fit yet - Payment ledgers that need global uniqueness across hot and cold - Workloads that need cold point lookups as fast as a B-tree - Schema evolution, compaction, and backup/restore that are still hardening ## Read this section in order 1. [Installation](https://kalamdb.org/docs/pg-koldstore/installation) — preload, Docker, from source 2. [Getting Started](https://kalamdb.org/docs/pg-koldstore/getting-started) — first managed table 3. [Under the Hood](https://kalamdb.org/docs/pg-koldstore/concepts) — WAL capture, mirror, merge scan 4. [Manage & Flush](https://kalamdb.org/docs/pg-koldstore/manage-and-flush) — policies and jobs 5. [Change Feed](https://kalamdb.org/docs/pg-koldstore/change-feed) — `changes_since` / `last_rows` 6. [SQL API Reference](https://kalamdb.org/docs/pg-koldstore/sql-api) — every shipped function and GUC 7. [Operations](https://kalamdb.org/docs/pg-koldstore/operations) — auto-flush, mirror health 8. [Benchmarks](https://kalamdb.org/docs/pg-koldstore/benchmarks) — storage and VACUUM wins 9. [Status & Limits](https://kalamdb.org/docs/pg-koldstore/limitations) — honest beta boundaries ## Source and packaging - Repository: [github.com/kalamdb/koldstore](https://github.com/kalamdb/koldstore) - License: Apache 2.0 - PostgreSQL: **15–18** (published Docker image targets PG 16) - Product page: [/koldstore](https://kalamdb.org/koldstore) ================================================================================ # Installation URL: https://kalamdb.org/docs/pg-koldstore/installation Source: content/pg-koldstore/installation.mdx > Install KoldStore with Docker or from source, set shared_preload_libraries, enable wal_level=logical, and verify preload_status. # Installation KoldStore must load at **postmaster start**. Without `shared_preload_libraries = 'koldstore'`, planner hooks never register and managed `SELECT`s can silently miss cold rows after flush. ## Fastest path: Docker Published images preload the extension and create it on first init: ```bash docker pull jamals86/pg-koldstore:latest docker run --rm \ -e POSTGRES_PASSWORD=postgres \ -p 5432:5432 \ jamals86/pg-koldstore:latest ``` Connect: ```bash psql postgres://postgres:postgres@127.0.0.1:5432/koldstoredb ``` | Detail | Value | |--------|-------| | Default database | `koldstoredb` | | PostgreSQL major | 16 | | Tags | `latest`, `-pg16` (for example `0.1.5-beta.0-pg16`) | | Architectures | amd64, arm64 | Confirm: ```sql SHOW shared_preload_libraries; -- must include koldstore CREATE EXTENSION IF NOT EXISTS koldstore; SELECT koldstore.preload_status(); -- loaded_via_shared_preload = true ``` `pg_cron` may be packaged in the image but is **not** preloaded. Enable it yourself if you want cron-based flush scheduling. ## Shared preload (required on every install) Install order: 1. Install extension package files 2. Set `shared_preload_libraries = 'koldstore'` (merge with any existing list) 3. **Restart** PostgreSQL (`reload` is not enough) 4. `CREATE EXTENSION koldstore` Ubuntu / Debian example: ```bash echo "shared_preload_libraries = 'koldstore'" | \ sudo tee /etc/postgresql/16/main/conf.d/koldstore.conf sudo systemctl restart postgresql@16-main ``` Rules: - `session_preload_libraries` is **not** sufficient - Removing preload after `manage_table` is **unsupported** - Check with `SELECT koldstore.preload_status();` ## Logical WAL (required for managed tables) Committed-WAL capture needs: ```conf wal_level = logical ``` Restart after changing `wal_level`. Do **not** create the publication or logical slot yourself: `CREATE EXTENSION` ensures the empty publication `koldstore_async_mirror`, and the first `manage_table` creates the database’s deterministic slot. Provisioning is idempotent. ## Recommended production GUC baseline Prefer `ALTER SYSTEM` / `ALTER DATABASE` for settings the background worker reads (session `SET` does not always reach the worker): | Setting | Baseline | |---------|----------| | `shared_preload_libraries` | include `koldstore` | | `wal_level` | `logical` | | `koldstore.async_mirror_max_retained_bytes` | `1073741824` (1 GiB health alert) | | `koldstore.flush_check_interval_seconds` | `30` | | `koldstore.async_apply_poll_interval_ms` | `100` | ## From source (developers) Build and install with pgrx against a local PostgreSQL (typical loop for PG 16): ```bash cargo pgrx install -p pg_koldstore \ --no-default-features \ --features "pg16 s3" \ --pg-config "$(cargo pgrx info pg-config 16)" ``` Then set preload, restart, and `CREATE EXTENSION`. See the repository [quickstart](https://github.com/kalamdb/koldstore/blob/main/docs/quickstart.md) and [Docker](https://github.com/kalamdb/koldstore/tree/main/docker) folder for compose + MinIO layouts. ## Upgrades (beta) There are **no** packaged `ALTER EXTENSION … UPDATE` edges yet while the product is in development. Local installs reinstall or resync when the bootstrap SQL changes. Treat in-place upgrade and `pg_upgrade` as future ops work — see [Status & Limits](https://kalamdb.org/docs/pg-koldstore/limitations). ## Next step [Getting Started](https://kalamdb.org/docs/pg-koldstore/getting-started) — register storage, manage a table, flush, and read with `changes_since`. ================================================================================ # Status & Limits URL: https://kalamdb.org/docs/pg-koldstore/limitations Source: content/pg-koldstore/limitations.mdx > Honest KoldStore beta boundaries: preload correctness, hot-only UNIQUE/FK, cold storage failure modes, and unfinished recovery work. # Status & Limits KoldStore is in **early development** and is **not production-ready**. Manage, flush, auto-flush, hot/cold query, and `changes_since` work. Recovery, backup/restore, compaction, schema evolution, PK changes, and export/import are still hardening. ## Shared preload is mandatory `shared_preload_libraries` must include `koldstore`. Without it, backends can silently run ordinary heap scans and return **hot-only** rows after flush. - Install → set preload → **restart** → `CREATE EXTENSION` - `session_preload_libraries` is not enough - Removing preload after `manage_table` is unsupported Check: `SELECT koldstore.preload_status();` ## UNIQUE and foreign keys PostgreSQL `UNIQUE` and FK constraints on managed tables are enforced on the **hot heap only**. After flush, cold values live in Parquet without a global hot+cold constraint layer. | Constraint | Hot | Cold | Normal DML checks cold? | |------------|-----|------|-------------------------| | Primary key | Yes | Logical winner via merge | Cold presence via segment index + Parquet | | Non-PK `UNIQUE` | Yes | No | No | | Foreign keys | Yes | No | No | When `hot_row_limit` is set, `manage_table` **rejects** tables that already have non-PK `UNIQUE` constraints or foreign keys. ## Indexes and extensions PostgreSQL indexes (including pgvector HNSW/IVFFlat and similar) cover **hot rows only**. Flushed rows lose heap index entries. There is no automatic Parquet index translation yet. The `vector` type is not in the v0.1 supported type matrix. ## Cold storage availability If cold storage is unavailable, managed queries that need cold segments **fail**. KoldStore does not silently return hot-only results. `koldstore.enable_merge_scan = off` also errors rather than allowing an incorrect heap-only read. ## Capture limits - WAL-only capture (no trigger mode) - Primary-key updates rejected - `TRUNCATE` on async managed tables rejected - Bounded mirror lag unless you call `wait_for_async_mirror()` - Scoped physical cold folders still incomplete vs registration templates ## Change feed limits - Latest-state catch-up, not full event history - Positive cursors older than retained cold floor → retention-gap **error** - `last_rows` ignored when `since_seq > 0` - `last_rows` must be `<= limit_rows` ## Unmanage, not disable Use `koldstore.unmanage_table(...)`. Do not set `koldstore_enabled = false`. Storage binding is immutable after manage. ## Not shipped - Packaged extension upgrade edges (`ALTER EXTENSION … UPDATE`) - Cold DML helpers and cold-only `UPDATE`/`DELETE` that mutate Parquet in place - `backup_manifest` / `validate_cold_storage` / polished export-import - Compaction and schema evolution tooling ## When to wait Prefer waiting if you need global uniqueness across tiers, cold lookups as fast as B-trees, or turnkey PITR that spans heap + object storage out of the box. ## When to try Local or staging workloads that grow forever (messages, audit, AI memory) where shrinking the hot heap and indexes is the main goal, and you can tolerate beta ops boundaries. ================================================================================ # Manage & Flush URL: https://kalamdb.org/docs/pg-koldstore/manage-and-flush Source: content/pg-koldstore/manage-and-flush.mdx > Enable KoldStore on a heap table, choose hot_row_limit or move_after, run flush_table, and inspect jobs with describe_table. # Manage & Flush Managing a table attaches KoldStore policy and WAL mirror capture. Flushing moves eligible latest-state rows from the hot heap into Parquet and prunes them from PostgreSQL. ## Enable management Prefer named arguments. `hot_row_limit` is required in the `manage_table` call — pass `NULL` for hot-only tables that should not flush. ```sql SELECT koldstore.manage_table( table_name => 'chat.messages', storage => 's3_archive', hot_row_limit => 10000, min_flush_rows => 1000, max_rows_per_file => 1000, auto_flush => true, migration_order_by => 'created_at' ); ``` | Parameter | Default | Meaning | |-----------|---------|---------| | `table_name` | required | Table to manage (`regclass`) | | `storage` | required | Registered storage backend name | | `hot_row_limit` | required (`NULL` allowed) | Max mirror rows to keep hot | | `min_flush_rows` | `1000` | Minimum excess before a policy flush moves data | | `max_rows_per_file` | `1000` | Max rows per Parquet segment (floor via `koldstore.min_max_rows_per_file`) | | `table_type` | `'shared'` | `shared` or `user` (+ `scope_column`) | | `compression` | `NULL` | Optional Parquet codec (`snappy`, `zstd`, `uncompressed`) | | `target_file_size_mb` | `NULL` | Stored for future size-aware flush | | `auto_flush` | `true` | Let the DB worker evaluate and run needed flushes | ### ALTER TABLE style ```sql ALTER TABLE chat.messages SET ( koldstore_enabled = true, koldstore_storage = 's3_archive', koldstore_hot_row_limit = 10000, koldstore_min_flush_rows = 1000, koldstore_max_rows_per_file = 1000 ); ``` Age-based eviction (mutually exclusive with `hot_row_limit`): ```sql ALTER TABLE chat.messages SET ( koldstore_enabled = true, koldstore_storage = 's3_archive', koldstore_move_after = '90 days', koldstore_min_flush_rows = 1000, koldstore_max_rows_per_file = 10000 ); ``` Age is measured from the latest captured mirror mutation encoded in `seq`. ## Flush ### Policy flush ```sql SELECT koldstore.flush_table(table_name => 'chat.messages'::regclass); ``` Keeps the newest rows hot by mirror `seq` and flushes oldest eligible excess first, honoring `hot_row_limit` / `min_flush_rows` / `max_rows_per_file`. ### Force flush ```sql SELECT koldstore.flush_table( table_name => 'chat.messages'::regclass, force => true ); ``` Flushes **all** pending mirror rows. Use carefully on large tables. ### Enqueue without running ```sql SELECT koldstore.enqueue_flush_job( table_name => 'chat.messages'::regclass, force => false ); ``` Returns `1` if a job was inserted, `0` if an active flush job already exists. `auto_flush` does **not** gate manual flush / enqueue. ## Inspect progress ```sql SELECT jsonb_pretty( koldstore.describe_table(table_name => 'chat.messages'::regclass) ); SELECT koldstore.list_jobs( statuses => '["running","pending"]'::jsonb, job_types => '["flush"]'::jsonb, table_name => 'chat.messages'::regclass ); ``` Useful `describe_table` fields: `hot_rows`, `mirror_rows`, `cold_row_count`, `cold_segment_count`, heap/index sizes, `manifest_state`, `pending_jobs`, `last_error`. ## Cancel ```sql SELECT koldstore.cancel_job(job_id => '…'::uuid); SELECT koldstore.cancel_table_jobs(table_name => 'chat.messages'::regclass); ``` Pending jobs cancel immediately when unlocked. Running jobs stop cooperatively at the next wave boundary. If cancel lands after cold publish already committed, the job may finish `completed` with `payload.cancel_requested_after_publish = true` (data is not unpublished). ## Auto-flush toggle ```sql SELECT koldstore.set_table_auto_flush( table_name => 'chat.messages'::regclass, enabled => false ); ``` Manual `flush_table` still works. See [Operations](https://kalamdb.org/docs/pg-koldstore/operations) for the built-in worker tick and pg_cron fallback. ## Unmanage ```sql SELECT koldstore.unmanage_table( table_name => 'chat.messages'::regclass, rehydrate => true, drop_cold => false ); ``` Use this instead of `koldstore_enabled = false`. Optional `rehydrate` / `drop_cold` control whether cold rows return to the heap and whether cold objects are deleted. ## Next - [Change Feed](https://kalamdb.org/docs/pg-koldstore/change-feed) - [SQL API Reference](https://kalamdb.org/docs/pg-koldstore/sql-api) ================================================================================ # Operations URL: https://kalamdb.org/docs/pg-koldstore/operations Source: content/pg-koldstore/operations.mdx > Run KoldStore auto-flush, monitor async mirror health, schedule pg_cron fallbacks, and understand DROP TABLE cold cleanup. # Operations Day-2 concerns for a managed database: auto-flush, mirror health, optional cron, and drop/cleanup behavior. ## Built-in auto-flush The same database worker that applies WAL also evaluates `auto_flush` tables on each `koldstore.flush_check_interval_seconds` tick (default `30`): 1. Apply available WAL 2. Evaluate tables with `auto_flush = true` 3. Run at most one needed `flush_table` Failed auto-flush skips that table for about **60 seconds**. Opt out per table: ```sql SELECT koldstore.set_table_auto_flush( table_name => 'app.messages'::regclass, enabled => false ); ``` Or pass `auto_flush => false` to `manage_table`. Manual flush still works. Prefer `ALTER DATABASE` / `ALTER SYSTEM` for worker-facing GUCs — session `SET` does not always reach the background worker. ## pg_cron fallback Useful when you want wall-clock schedules or `auto_flush = false`: ```sql SELECT cron.schedule( 'koldstore-flush-messages', '*/5 * * * *', $$SELECT koldstore.flush_table(table_name => 'app.messages'::regclass)$$ ); ``` A policy flush with nothing eligible returns safely (`rows_flushed = 0`). ## Mirror health ```sql SELECT koldstore.async_mirror_status(); SELECT koldstore.async_mirror_slot_name(); ``` Alert on: - `healthy` / retention flags in `async_mirror_status()` - Retained WAL bytes vs `koldstore.async_mirror_max_retained_bytes` (default 1 GiB) - Stale `updated_at` / apply rates Crossing the retained-bytes threshold marks retention unhealthy but **never stops** the applier from draining WAL. Separately configure PostgreSQL disk guards and `max_slot_wal_keep_size` (an invalidated slot may require rebuild). Strong application fence: ```sql SELECT koldstore.wait_for_async_mirror(); ``` ## Disable capture infrastructure Only after every managed table is unmanaged: ```sql SELECT koldstore.disable_async_mirror(); ``` Idempotent. A later `manage_table` recreates compatible infrastructure. ## DROP TABLE Dropping a managed table cancels jobs, waits for locks, deactivates catalogs, deletes cold objects under the table prefix (when deletion is enabled for that path), drops `__cl`, and records a `drop_table_cleanup` job. ## Backup (honest beta) - Back up PostgreSQL **and** cold object prefixes together - Cold storage is not WAL-protected - `recover_segments(..., dry_run => true|false)` exists for orphan discovery - Packaged `backup_manifest` / `validate_cold_storage` are **not** shipped yet - `pg_dump` / `COPY TO` see the logical merged view; physical Parquet is outside plain dumps See [Status & Limits](https://kalamdb.org/docs/pg-koldstore/limitations). ## Suggested monitoring checklist 1. `SHOW shared_preload_libraries` still includes `koldstore` 2. `koldstore.preload_status()` → `loaded_via_shared_preload = true` 3. `async_mirror_status()` healthy; retained bytes under threshold 4. `describe_table` for hot/cold growth and `last_error` 5. Flush job backlog (`list_jobs` pending/running) 6. Disk for PostgreSQL data directory **and** cold backend ## Next - [Benchmarks](https://kalamdb.org/docs/pg-koldstore/benchmarks) - [Status & Limits](https://kalamdb.org/docs/pg-koldstore/limitations) ================================================================================ # SQL API Reference URL: https://kalamdb.org/docs/pg-koldstore/sql-api Source: content/pg-koldstore/sql-api.mdx > Complete KoldStore SQL surface: session helpers, GUCs, storage, manage/flush, async mirror, changes_since, jobs, and describe_table. # SQL API Reference Signatures match the installed extension SQL today. Prefer **named arguments** so optional parameters stay readable. Positional calls remain valid. ```sql -- Preferred SELECT koldstore.register_storage( name => 'local-dev', storage_type => 'filesystem', base_path => '/tmp/koldstore-demo', credentials => '{}'::jsonb, config => '{}'::jsonb ); ``` ## Session helpers | Function | Returns | Meaning | |----------|---------|---------| | `snowflake_id()` | `bigint` | Monotonic Snowflake-like id | | `koldstore_version()` | `text` | Extension version | | `koldstore_user_id()` | `text` | Active `koldstore.user_id`, or `NULL` | | `koldstore.preload_status()` | `jsonb` | Preload / merge-scan status | ## Public GUCs | GUC | Default | Meaning | |-----|---------|---------| | `koldstore.user_id` | empty | Scope id for user-typed managed tables | | `koldstore.cold_reads` | `auto` | `auto` / `on` / `off` (`off` errors when cold is required) | | `koldstore.enable_merge_scan` | `on` | `off` → ERROR at exec (never silent hot-only) | | `koldstore.max_open_parquet_readers` | `32` | Per-backend open reader cap (`1..=1024`) | | `koldstore.max_merge_seen_keys` | `1000000` | Per-scan PK identity cap; `0` disables | | `koldstore.log_level` | `info` | `error` / `warn` / `info` / `debug` / `trace` | | `koldstore.min_max_rows_per_file` | `1000` | Floor for `max_rows_per_file` | | `koldstore.flush_check_interval_seconds` | `30` | Auto-flush worker tick (`1..=86400`) | | `koldstore.async_apply_poll_interval_ms` | `100` | Apply loop latch poll (`50..=5000`) | | `koldstore.async_apply_max_rows_per_tick` | `0` | Max source changes per tick (`0` = unlimited) | | `koldstore.async_apply_max_ms_per_tick` | `0` | Max ms per tick (`0` = unlimited) | | `koldstore.flush_prelock_max_passes` | `3` | Flush phase-5.5 pre-lock apply passes | | `koldstore.flush_prelock_max_ms` | `5000` | Flush phase-5.5 wall budget | | `koldstore.async_mirror_max_retained_bytes` | `1 GiB` | Health threshold only — never blocks apply | Internal GUCs (`internal_system_write`, `internal_flush_cleanup`) are reserved for extension maintenance paths and cannot be set by application roles. Configure durable settings with `ALTER SYSTEM` / `ALTER DATABASE` / `ALTER ROLE` and follow PostgreSQL reload rules. Session `SET` does not always reach the background worker. ## Function catalog | Function | Returns | |----------|---------| | `koldstore.register_storage(...)` | `uuid` | | `koldstore.alter_storage_credentials(...)` | `void` | | `koldstore.alter_storage_location(...)` | `uuid` | | `koldstore.manage_table(...)` | `uuid` (migration job id) | | `koldstore.set_table_auto_flush(...)` | `boolean` | | `koldstore.unmanage_table(...)` | `bigint` | | `koldstore.wait_for_async_mirror()` | `bigint` | | `koldstore.async_mirror_status()` | `jsonb` | | `koldstore.async_mirror_slot_name()` | `text` | | `koldstore.disable_async_mirror()` | `boolean` | | `koldstore.enqueue_flush_job(...)` | `bigint` (`1` / `0`) | | `koldstore.flush_table(...)` | `uuid` | | `koldstore.list_jobs(...)` | `jsonb` array | | `koldstore.cancel_job(...)` / `cancel_table_jobs(...)` | cancel status | | `koldstore.describe_table(...)` | `jsonb` | | `koldstore.recover_segments(...)` | `bigint` | | `koldstore.changes_since(...)` | set of change rows | ## Storage ```sql SELECT koldstore.register_storage( name => 'local-dev', storage_type => 'filesystem', -- filesystem | s3 | gcs | azure base_path => '/tmp/koldstore-demo', credentials => '{}'::jsonb, config => '{}'::jsonb -- optional: regular_path_tmpl, scoped_path_tmpl ); SELECT koldstore.alter_storage_credentials( name => 'local-dev', credentials => '{"access_key_id":"...","secret_access_key":"..."}'::jsonb ); SELECT koldstore.alter_storage_location( name => 'local-dev', base_path => '/var/lib/koldstore', config => '{}'::jsonb ); ``` Default path templates: `{namespace}/{tableName}/` and `{namespace}/{tableName}/{scopeId}/`. ## Manage / unmanage See [Manage & Flush](https://kalamdb.org/docs/pg-koldstore/manage-and-flush) for parameters and policy examples. Quick reference: ```sql SELECT koldstore.manage_table( table_name => 'chat.messages', storage => 's3_archive', hot_row_limit => 10000 ); SELECT koldstore.set_table_auto_flush('chat.messages'::regclass, false); SELECT koldstore.unmanage_table( table_name => 'chat.messages'::regclass, rehydrate => true, drop_cold => false ); ``` ## Async mirror ```sql SELECT koldstore.wait_for_async_mirror(); -- strong fence; 0 ≠ disabled SELECT koldstore.async_mirror_status(); SELECT koldstore.async_mirror_slot_name(); -- After unmanaging every table: SELECT koldstore.disable_async_mirror(); ``` ## Flush and jobs ```sql SELECT koldstore.enqueue_flush_job(table_name => 'chat.messages'::regclass); SELECT koldstore.flush_table(table_name => 'chat.messages'::regclass, force => false); SELECT koldstore.list_jobs(); SELECT koldstore.describe_table(table_name => 'chat.messages'::regclass); SELECT koldstore.recover_segments(table_name => 'chat.messages'::regclass, dry_run => true); ``` ## Change feed ```sql SELECT * FROM koldstore.changes_since( table_name => 'chat.messages'::regclass, since_seq => 0, limit_rows => 1000, last_rows => NULL ); ``` Full semantics: [Change Feed](https://kalamdb.org/docs/pg-koldstore/change-feed). ## Not shipped yet (do not call) Documented as roadmap / ops intent only: - Cold DML helpers (`hydrate_pk`, cold `update_row` / `delete_row`) - `backup_manifest`, `validate_cold_storage`, packaged export/import - Packaged `ALTER EXTENSION … UPDATE` edges Standard SQL `UPDATE`/`DELETE` that only matches cold rows affects **zero** hot rows in the current MVP — use unmanage/rehydrate paths when you need cold rows back in the heap. ## Next - [Operations](https://kalamdb.org/docs/pg-koldstore/operations) - [Status & Limits](https://kalamdb.org/docs/pg-koldstore/limitations) ================================================================================ # Architecture URL: https://kalamdb.org/docs/pg-kalam/architecture Source: content/pg-kalam/architecture.mdx > Understand how pg_kalam uses FDW callbacks, a ProcessUtility DDL hook, gRPC sessions, and Arrow batches to bridge PostgreSQL to KalamDB. # PostgreSQL Extension Architecture The current PostgreSQL extension is remote-only. PostgreSQL hosts the extension entrypoints, the FDW callbacks, and the local relation surface, while a running KalamDB server performs the actual execution over gRPC. ## Main Components 1. A PostgreSQL backend process: owns SQL parsing, planning, and the visible PostgreSQL-side relation surface. 2. The `pg_kalam` extension: registers `_PG_init`, the FDW callbacks, the DDL `ProcessUtility` hook, and the helper SQL functions. 3. A foreign server definition: stores remote connection settings such as `host`, `port`, timeout, TLS material, and optional auth metadata. 4. A per-backend remote state cache: holds a reusable tonic channel, Tokio runtime, and a logical remote session id. 5. KalamDB's shared RPC listener: exposes the `PgService` surface for scans, mutations, transactions, and direct SQL execution. ## Transport and Session Model `pg_kalam` talks to KalamDB through the shared RPC listener, not through the HTTP SQL API. Current server options: - `host` - `port` - `timeout` - `auth_header` - `ca_cert` - `client_cert` - `client_key` On first use in a PostgreSQL backend and for a specific remote-server config, the extension: 1. parses the foreign-server options, 2. creates a current-thread Tokio runtime, 3. opens a tonic channel, 4. computes a logical session id in the form `pg--`, 5. sends `OpenSession`, and 6. caches that state for reuse across later statements in the same backend process. `on_proc_exit` then attempts to send `CloseSession` for every cached remote state owned by that PostgreSQL backend. Two important consequences follow from the current code: - The transport is reused per PostgreSQL backend and per remote config instead of reconnecting for every statement. - PostgreSQL current schema or `search_path` is not currently pushed into the remote session automatically, so direct `kalam_exec(...)` statements should be schema-qualified. ## DDL Path The extension registers a `ProcessUtility` hook from `_PG_init()`. That hook is what powers the current DDL surface. The hook intercepts: - `CREATE TABLE ... USING kalamdb` - `CREATE FOREIGN TABLE` - `ALTER FOREIGN TABLE` - `DROP FOREIGN TABLE` - top-level explicit transaction statements so the extension can coordinate remote transaction lifecycle There are two DDL entry points: 1. `CREATE TABLE ... USING kalamdb` This is the shorthand authoring path. It is currently bound to the default foreign server name `kalam_server`. 2. `CREATE FOREIGN TABLE ... SERVER ` This is the explicit path for non-default server names and manual mirroring. In both paths, the current relation identity is what matters: - the PostgreSQL schema becomes the remote KalamDB namespace, - the PostgreSQL relation name becomes the remote KalamDB table name. The runtime scan and modify paths do not treat `OPTIONS (namespace, table)` as the canonical mapping source. ### What the DDL hook does For `CREATE TABLE ... USING kalamdb`, the extension: 1. validates that `_seq`, `_userid`, and `_deleted` were not declared explicitly, 2. strips the PostgreSQL `USING kalamdb` access-method marker before forwarding SQL to KalamDB, 3. normalizes `SERIAL`, `BIGSERIAL`, `SMALLSERIAL`, and identity columns into explicit integer columns with `DEFAULT SNOWFLAKE_ID()`, 4. forwards the user-column DDL and `WITH (...)` table options to KalamDB, 5. creates a local foreign table, and 6. injects `_seq BIGINT` on all tables plus `_userid TEXT` on user tables. The local object becomes a foreign table after interception, which is why follow-up DDL should use `ALTER FOREIGN TABLE` and `DROP FOREIGN TABLE`. ## Read Path For `SELECT` statements, the FDW scan flow is: 1. PostgreSQL plans the extension-managed relation scan. 2. `pg_kalam` resolves the relation identity from the local relation metadata and parses the foreign-server options. 3. The extension reads the current `kalam.user_id` value, if present. 4. Any pending buffered writes for that table are flushed first so reads see local writes. 5. The FDW builds a `ScanRequest` with physical-column projection and simple equality filters that are safe to push down. 6. A remote scan request is sent to KalamDB over `PgService`. 7. The returned Arrow IPC batches are converted into PostgreSQL tuples. Current pushdown behavior is intentionally partial: - projection pushdown is used for physical columns, - simple `column = constant` or `column = $param` filters can be pushed, - PostgreSQL still reapplies all quals locally, - `ORDER BY` and `LIMIT` are not pushed down today. `_userid` is synthesized locally from the current session user id. `_seq` comes back from the remote Arrow schema. ## Write Path For `INSERT`, `UPDATE`, and `DELETE` statements: 1. PostgreSQL routes the operation through FDW modify callbacks. 2. `pg_kalam` resolves the target table and tenant context. 3. The extension converts PostgreSQL datums into DataFusion `ScalarValue` payloads. 4. Single-row inserts are buffered for better batching behavior inside explicit transactions. 5. Multi-row inserts go through the batch insert path directly. 6. Updates and deletes flush pending inserts first, then send remote mutation requests. Single-row inserts are buffered so PostgreSQL transactions can batch work more efficiently. Pending writes are flushed before scans and before transaction completion so read-your-writes behavior stays predictable inside the PostgreSQL session. Current user-id behavior: - `_userid` is only treated as an explicit tenant override for `INSERT`. - `UPDATE` and `DELETE` use the session `kalam.user_id` scope. - In a multi-row insert, all rows must use the same `_userid` value. Current row-identity behavior: - `UPDATE` and `DELETE` use the first non-system column as the row-identity heuristic. - In practice, keep the primary key as the first user column in the table definition. ## Explicit Transaction Flow When a PostgreSQL session opens an explicit transaction with `BEGIN` or `START TRANSACTION`, the extension lazily opens a remote KalamDB transaction on the first FDW operation in that block. The extension uses PostgreSQL xact callbacks to coordinate remote `BeginTransaction`, `CommitTransaction`, and `RollbackTransaction` calls. That transaction state is observable through two server views: - `system.sessions` for the live pg bridge session and its current remote transaction metadata. - `system.transactions` for the active explicit transaction itself alongside other active transaction origins. This makes PostgreSQL transaction behavior visible without materializing extra persisted bookkeeping on the server. Current transaction limits come from the backend coordinator: - DDL is rejected inside explicit transactions. - Stream-table writes are rejected inside explicit transactions. - The documented surface is top-level `BEGIN`, `COMMIT`, and `ROLLBACK` blocks. ## Direct Statement Passthrough The extension also exposes a direct SQL bridge: ```sql SELECT kalam_exec('SELECT * FROM system.tables'); ``` Use `kalam_exec(...)` when you want to send a statement directly to KalamDB without wrapping it in PostgreSQL relation syntax first. Current behavior: - it uses the default foreign server `kalam_server`, - it reuses the same per-backend remote session cache as the FDW path, - it returns a JSON array string for query results, - it returns a JSON string status message for DDL and DML, - it is revoked from `PUBLIC`. ## Tenant Context `kalam.user_id` is a PostgreSQL session setting registered by the extension. ```sql SET kalam.user_id = 'user-alice'; SELECT kalam_user_id(); ``` The session value is attached to remote requests and is the main way user-scoped reads, updates, and deletes are isolated. `kalam.user_id` is registered as a PostgreSQL `SUSET` GUC, so your PostgreSQL role needs permission to set `SUSET` parameters. ## Related - [Getting Started](https://kalamdb.org/docs/pg-kalam/getting-started) - [SQL Syntax](https://kalamdb.org/docs/pg-kalam/sql-syntax) - [Data Type Conversions](https://kalamdb.org/docs/pg-kalam/type-conversions) - [Status & Limits](https://kalamdb.org/docs/pg-kalam/limitations) ================================================================================ # Getting Started URL: https://kalamdb.org/docs/pg-kalam/getting-started Source: content/pg-kalam/getting-started.mdx > Set up pg_kalam by preloading the extension, exposing KalamDB's RPC port, creating the default foreign server, and using the current authoring paths correctly. # PostgreSQL Extension Getting Started This page covers both the release-download path and the current usage path after the `pg_kalam` binaries are installed. If you still need to build or package the extension itself, use the repository guide: [pg/README.md](https://github.com/kalamdb/KalamDB/tree/main/pg). If you want PostgreSQL with the extension already installed, use the published Docker image: [jamals86/pg-kalam](https://hub.docker.com/r/jamals86/pg-kalam). ## Download a Release Package KalamDB publishes a prebuilt PostgreSQL extension package in each GitHub release: - Releases page: [github.com/kalamdb/KalamDB/releases](https://github.com/kalamdb/KalamDB/releases) - Current asset pattern: - Check the matching `SHA256SUMS` asset before installing in production The install example below automatically uses the latest published GitHub release tag. Important constraints: - The published release package currently targets Linux x86_64. - The asset name includes the PostgreSQL feature it was built for, such as `pg16`. - The binary must match the PostgreSQL major version of the server where you install it. - If you need another platform or PostgreSQL major, build from source with `pgrx`. Example install from a release asset: ## Run with Docker For local or CI usage, you can run PostgreSQL with `pg_kalam` preinstalled directly from Docker Hub: ```bash docker run --name pg-kalam \ -e POSTGRES_USER=kalamdb \ -e POSTGRES_PASSWORD=kalamdb123 \ -e POSTGRES_DB=kalamdb \ -p 5433:5432 \ -d jamals86/pg-kalam:latest-pg16 ``` Then connect with `psql` and install the extension in the database if your init SQL has not already done it: ```sql CREATE EXTENSION IF NOT EXISTS pg_kalam; ``` ## Prerequisites - PostgreSQL with `pg_kalam` installed into that exact PostgreSQL instance. - A running KalamDB server reachable from PostgreSQL. - A stable KalamDB RPC endpoint host and port. The default repository examples use `2910`. - An auth header if your KalamDB server requires one. ## 1. Preload and Create the Extension `pg_kalam` installs SQL objects with `CREATE EXTENSION`, but the DDL mirroring hook is registered from `_PG_init()`. For the shorthand `CREATE TABLE ... USING kalamdb` path, add the extension to PostgreSQL's preload list and restart PostgreSQL first: ```conf shared_preload_libraries = 'pg_kalam' ``` Then connect as a superuser and install the extension into the target database: ```sql CREATE EXTENSION IF NOT EXISTS pg_kalam; ``` Verify that PostgreSQL sees the extension functions: ```sql SELECT kalam_version(), kalam_compiled_mode(); ``` `kalam_compiled_mode()` should currently return `remote`. ## 2. Give KalamDB a Stable RPC Address Point PostgreSQL at KalamDB's shared gRPC/RPC listener, not at the HTTP API port. For local source runs, configure a fixed RPC address instead of relying on a random ephemeral port: ```toml [cluster] rpc_addr = "127.0.0.1:2910" api_addr = "127.0.0.1:2900" ``` If PostgreSQL runs in Docker on macOS while KalamDB runs on the host machine, use `host.docker.internal` instead of `127.0.0.1` in the foreign-server definition. ## 3. Register the Default KalamDB Foreign Server Create the default foreign server that the shorthand authoring path expects: ```sql CREATE SERVER IF NOT EXISTS kalam_server FOREIGN DATA WRAPPER pg_kalam OPTIONS ( host '127.0.0.1', port '2910', auth_header 'Bearer ' ); ``` Supported server options today: - `host` and `port` are required. - `auth_header`, `timeout`, `ca_cert`, `client_cert`, and `client_key` are optional. - If you set `client_cert` or `client_key`, you must set both. ## 4. Choose the Right Authoring Path There are two current SQL entry points: | Goal | Use | |---|---| | Create a KalamDB table from PostgreSQL on the default server | `CREATE TABLE ... USING kalamdb` | | Use a non-default foreign server or mirror a relation explicitly | `CREATE FOREIGN TABLE ... SERVER ` | The shorthand `USING kalamdb` path is convenient, but it is hard-wired to the default server name `kalam_server`. ## 5. Create Your First Shared Table The primary authoring syntax is `CREATE TABLE ... USING kalamdb`: ```sql CREATE SCHEMA IF NOT EXISTS app; CREATE TABLE app.shared_items ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), title TEXT NOT NULL, value INTEGER, created_at TIMESTAMP DEFAULT NOW() ) USING kalamdb WITH ( type = 'shared', flush_policy = 'rows:1000,interval:60' ); ``` That statement is intercepted by the DDL hook, creates the remote namespace and table in KalamDB, then creates a local foreign table and injects the PostgreSQL-visible system columns that the extension manages locally. Any KalamDB table shape that the server accepts can be authored this way from PostgreSQL. The same option names you would use in KalamDB are forwarded through the PostgreSQL `WITH (...)` clause. Important rules for new tables: - Do not declare `_seq`, `_userid`, or `_deleted` in the `CREATE TABLE ... USING kalamdb` column list. `pg_kalam` rejects that DDL because those columns are managed by the extension. - `_seq BIGINT` is added to every PostgreSQL table surface that `pg_kalam` creates. - `_userid TEXT` is added only to user tables. - `_deleted` is reserved, but it is not auto-injected into the current PostgreSQL table surface. - Keep the primary key as the first user column. The current `UPDATE` and `DELETE` FDW path uses the first non-system column as its row-identity heuristic. - The local object is a foreign table after interception, so use `ALTER FOREIGN TABLE` and `DROP FOREIGN TABLE` for follow-up DDL. ## 6. Query and Mutate Through PostgreSQL ```sql INSERT INTO app.shared_items (id, title, value) VALUES (SNOWFLAKE_ID(), 'Alpha', 10), (SNOWFLAKE_ID(), 'Beta', 20); SELECT id, title, value FROM app.shared_items ORDER BY id; UPDATE app.shared_items SET value = 25 WHERE id = 2; DELETE FROM app.shared_items WHERE id = 1; ``` Autocommit statements flush at statement end. Top-level explicit transactions are covered later in this section. ## 7. Use Session Identity for User Tables User tables are tenant-scoped. Set `kalam.user_id` in each PostgreSQL session before querying or mutating them: ```sql SET kalam.user_id = 'user-alice'; CREATE TABLE app.profiles ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), name TEXT, age INTEGER ) USING kalamdb WITH ( type = 'user' ); INSERT INTO app.profiles (id, name, age) VALUES (SNOWFLAKE_ID(), 'Alice', 30); INSERT INTO app.profiles (id, name, age, _userid) VALUES (SNOWFLAKE_ID(), 'Bob', 41, 'user-bob'); SELECT id, name, age, _userid, _seq FROM app.profiles ORDER BY _seq; ``` The tenant scope comes from the PostgreSQL session value unless you explicitly provide `_userid` in an `INSERT`. `kalam.user_id` is registered as a PostgreSQL `SUSET` parameter. In practice, the role running these statements must be allowed by PostgreSQL to change `SUSET` settings. Current `_userid` rules: - On user tables, omitting `_userid` uses the current `kalam.user_id` session value. - On user tables, an explicit `_userid` in `INSERT` overrides the session value for that inserted row batch. - In a multi-row `INSERT`, every row must use the same `_userid` value. - `UPDATE` and `DELETE` still use the session's `kalam.user_id` scope. Changing row ownership with `UPDATE ... SET _userid = ...` is not part of the supported surface. ## 8. Create a Stream Table Stream tables use the same authoring path, but they must supply `ttl_seconds`: ```sql CREATE TABLE app.events ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), event_type TEXT NOT NULL, payload JSONB, created_at TIMESTAMP DEFAULT NOW() ) USING kalamdb WITH ( type = 'stream', ttl_seconds = '3600' ); ``` Normal FDW access uses the stream table type, but explicit PostgreSQL transactions reject stream-table writes. ## 9. Reuse KalamDB Table Options with `WITH (...)` The `WITH (...)` clause is the main place to express KalamDB table options from PostgreSQL: ```sql CREATE TABLE app.orders ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), customer_id TEXT NOT NULL, status TEXT DEFAULT 'pending', created_at TIMESTAMP DEFAULT NOW() ) USING kalamdb WITH ( type = 'shared', flush_policy = 'rows:1000,interval:60' ); ``` Use the same option names and values that KalamDB expects. The extension forwards them to the server when it constructs the real KalamDB `CREATE TABLE` statement. ## 10. Work with `FILE` Columns `FILE` is the PostgreSQL extension's main KalamDB-specific type shortcut. ```sql CREATE TABLE app.assets ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), attachment FILE ) USING kalamdb WITH ( type = 'shared' ); SELECT attachment->>'name', attachment->>'mime' FROM app.assets; ``` Current behavior: - The remote KalamDB column stays `FILE`. - The local PostgreSQL column is rewritten to `JSONB`. - PostgreSQL sees a serialized `FileRef` payload with fields such as `id`, `sub`, `name`, `size`, `mime`, and `sha256`. - Binary upload itself usually happens through KalamLink or another KalamDB file-aware client. PostgreSQL sees the resulting `FileRef` JSON representation. ## 11. Send Direct SQL to KalamDB When you want to run a KalamDB statement directly, use `kalam_exec(...)`: ```sql SELECT kalam_exec('ALTER TABLE app.orders ADD COLUMN priority INTEGER DEFAULT 0'); SELECT kalam_exec('DROP TABLE app.orders'); SELECT kalam_exec('SELECT * FROM system.tables'); ``` `kalam_exec(...)` forwards the statement to the KalamDB server as-is. - For `SELECT` statements, it returns a JSON array string. - For DDL and DML statements, it returns a JSON string status message. - It currently uses the default foreign server `kalam_server`. - It is revoked from `PUBLIC`, so use a superuser or grant `EXECUTE` explicitly. - It does not currently synchronize PostgreSQL `search_path` or current schema to the remote session automatically, so schema-qualify direct SQL. ## 12. Use a Non-Default Server Name If you need a different foreign server name, create it and use the explicit `CREATE FOREIGN TABLE` path: ```sql CREATE SERVER kalam_eu FOREIGN DATA WRAPPER pg_kalam OPTIONS ( host '10.0.0.25', port '2910', auth_header 'Bearer ' ); CREATE FOREIGN TABLE app.audit_log ( id BIGINT, action TEXT, payload JSONB ) SERVER kalam_eu OPTIONS ( table_type 'shared' ); ``` Current relation-mapping rule: the local PostgreSQL schema and relation name are the remote namespace and table. `OPTIONS (namespace, table)` are not the runtime source of truth for scans and writes. If you mirror a remote `FILE` column manually, declare the local PostgreSQL column as `JSONB`. ## 13. Maintain the Server Definition Update server options with standard PostgreSQL syntax: ```sql ALTER SERVER kalam_server OPTIONS (SET host 'kalamdb.internal', SET port '2910'); ALTER SERVER kalam_server OPTIONS (ADD auth_header 'Bearer '); ``` Remove the bridge when you are done: ```sql DROP SERVER kalam_server CASCADE; ``` ## Next Steps - [Architecture](https://kalamdb.org/docs/pg-kalam/architecture) - [SQL Syntax](https://kalamdb.org/docs/pg-kalam/sql-syntax) - [Data Type Conversions](https://kalamdb.org/docs/pg-kalam/type-conversions) - [Status & Limits](https://kalamdb.org/docs/pg-kalam/limitations) ================================================================================ # PostgreSQL Extension URL: https://kalamdb.org/docs/pg-kalam Source: content/pg-kalam/index.mdx > Use the remote-only pg_kalam PostgreSQL extension to expose KalamDB tables in PostgreSQL and understand the current FDW, DDL, and type-conversion surface. # PostgreSQL Extension `pg_kalam` is a remote-only PostgreSQL foreign data wrapper extension. PostgreSQL hosts the FDW callbacks, DDL hook, and local relation surface, while KalamDB executes scans, writes, and mirrored DDL over gRPC on the shared RPC listener. Prebuilt release assets are published on the [KalamDB GitHub Releases page](https://github.com/kalamdb/KalamDB/releases) for the current Linux x86_64 PostgreSQL extension target. Other platforms or PostgreSQL majors can still be built from source with `pgrx`. KalamDB also publishes a ready-to-run Docker image with PostgreSQL and `pg_kalam` preinstalled at [jamals86/pg-kalam on Docker Hub](https://hub.docker.com/r/jamals86/pg-kalam). This chapter documents the current supported remote-mode surface. The codebase no longer contains an embedded-mode PostgreSQL runtime. Keep these rules in mind from the start: - Point `CREATE SERVER ... OPTIONS (host, port)` at KalamDB's RPC listener, not the HTTP API port. - Add `shared_preload_libraries = 'pg_kalam'` and restart PostgreSQL if you want `CREATE TABLE ... USING kalamdb` and the DDL mirroring hook to work. - `CREATE TABLE ... USING kalamdb` and `kalam_exec(...)` currently assume a default foreign server named `kalam_server`. - Use `CREATE FOREIGN TABLE ... SERVER ` when you need a non-default server or you want to mirror a relation explicitly. ## What You Can Do Today - Install the extension into PostgreSQL 13 through 18, with `pg16` as the default build target. - Create remote server definitions with host, port, optional auth header, timeout, and TLS material. - Define KalamDB tables from PostgreSQL with `CREATE TABLE ... USING kalamdb`. - Expose relations explicitly with `CREATE FOREIGN TABLE ... SERVER ...` when you need a non-default server name. - Run `SELECT`, `INSERT`, `UPDATE`, and `DELETE` through FDW callbacks. - Use top-level `BEGIN`, `COMMIT`, and `ROLLBACK` blocks for explicit transactions on shared and user tables. - Observe bridge sessions and explicit transactions with `system.sessions` and `system.transactions`. - Use `kalam_exec(...)` for direct remote SQL when you want to bypass the relation wrapper. - Work with `FILE` columns as `JSONB` `FileRef` payloads on the PostgreSQL side. ## Current Capability Map | Area | Status | Notes | |---|---|---| | Remote bridge | Available | `kalam_compiled_mode()` returns `remote` | | `CREATE TABLE ... USING kalamdb` | Available with limits | Requires `shared_preload_libraries = 'pg_kalam'` and the default server `kalam_server` | | `CREATE FOREIGN TABLE ... SERVER ...` | Available | Use this path for non-default server names and explicit mirroring | | Shared and user table DML | Available | `SELECT`, `INSERT`, `UPDATE`, and `DELETE` run through FDW callbacks | | Stream tables | Available with limits | Stream-table DDL works; explicit transactions reject stream-table writes | | Explicit transactions | Available with limits | Top-level `BEGIN`, `COMMIT`, and `ROLLBACK`; no DDL inside explicit transactions | | Pushdown | Partial | Projection plus simple equality filters; PostgreSQL still reapplies quals locally | | Direct statement passthrough | Available with limits | `kalam_exec(...)` uses the default server and is revoked from `PUBLIC` | | Helper SQL functions | Available | `kalam_version`, `kalam_compiled_mode`, `kalam_user_id`, `kalam_user_id_guc_name`, `snowflake_id`, `kalam_exec` | | `IMPORT FOREIGN SCHEMA` | Not available | Remote mode currently returns an unsupported error | | Data type conversions | Mixed | See [Data Type Conversions](https://kalamdb.org/docs/pg-kalam/type-conversions) for the exact current mapping and runtime caveats | ## Read This Section In Order 1. [Getting Started](https://kalamdb.org/docs/pg-kalam/getting-started) 2. [Architecture](https://kalamdb.org/docs/pg-kalam/architecture) 3. [SQL Syntax](https://kalamdb.org/docs/pg-kalam/sql-syntax) 4. [Data Type Conversions](https://kalamdb.org/docs/pg-kalam/type-conversions) 5. [Status & Limits](https://kalamdb.org/docs/pg-kalam/limitations) ## Recommended Workflow Use the PostgreSQL extension when you want PostgreSQL tooling, SQL clients, or migrations to operate against KalamDB tables without switching your operators to the HTTP API or an SDK. In most cases the flow is: 1. Download the matching `pg_kalam` release asset for your PostgreSQL major, or build it from source. 2. Install the extension into the exact PostgreSQL instance you will use and add it to `shared_preload_libraries`. 3. Give KalamDB a stable RPC address and point the default `kalam_server` foreign server at that endpoint. 4. Define tables with `CREATE TABLE ... USING kalamdb`, or use `CREATE FOREIGN TABLE ... SERVER ...` for non-default servers. 5. Set `kalam.user_id` in sessions that work with user-scoped tables. 6. Use top-level `BEGIN ... COMMIT` when you need explicit transaction batching on shared or user tables. 7. Use `kalam_exec(...)` only when you need direct remote SQL and can satisfy its privilege model. ## Related - [SQL Reference](https://kalamdb.org/docs/server/sql-reference) - [Data Types Reference](https://kalamdb.org/docs/server/architecture/datatypes) - [Docker Deployment](https://kalamdb.org/docs/server/getting-started/docker) - [API Reference](https://kalamdb.org/docs/server/api) - [KalamDB PostgreSQL extension source guide](https://github.com/kalamdb/KalamDB/tree/main/pg) ================================================================================ # Status and Limits URL: https://kalamdb.org/docs/pg-kalam/limitations Source: content/pg-kalam/limitations.mdx > Review the current remote-only scope, hard limits, and troubleshooting guidance for pg_kalam so the PostgreSQL extension is used within its actual code-backed surface. # PostgreSQL Extension Status and Limits The PostgreSQL extension is usable, but it is still an active development surface. Plan for incremental feature growth rather than assuming complete PostgreSQL FDW parity. ## Supported Today - Remote-mode PostgreSQL bridge to a running KalamDB server - Shared and user table reads and writes through FDW callbacks - Stream-table authoring and normal FDW access outside explicit transactions - Explicit `BEGIN`, `COMMIT`, and `ROLLBACK` transaction blocks for Kalam-backed tables - `CREATE TABLE ... USING kalamdb` as the public PostgreSQL-side authoring syntax - Auto-injected system columns: `_seq` on all tables and `_userid` on user tables - Forwarding KalamDB table options from PostgreSQL `WITH (...)` - Direct pass-through execution with `kalam_exec(...)` - Session-scoped tenant routing with `kalam.user_id` - Extension helper functions including `kalam_version()` and `SNOWFLAKE_ID()` ## Hard Limits and Current Constraints | Area | Current behavior | |---|---| | Compile mode | `kalam_compiled_mode()` currently reports `remote`; this chapter documents that mode only | | DDL hook | `CREATE TABLE ... USING kalamdb` requires `shared_preload_libraries = 'pg_kalam'` and a PostgreSQL restart | | Default server binding | `CREATE TABLE ... USING kalamdb` and `kalam_exec(...)` currently assume the default foreign server name `kalam_server` | | Schema import | `IMPORT FOREIGN SCHEMA` is not available in remote mode | | Runtime mapping source | Relation schema and relation name are the runtime namespace and table; `OPTIONS (namespace, table)` are not the canonical scan/write mapping | | Filter pushdown | Only simple equality filters are pushed down; PostgreSQL still reapplies quals locally | | Order and limit pushdown | `ORDER BY` and `LIMIT` are not pushed down today | | User scoping | User-table operations should set `kalam.user_id`; `_userid` is only an explicit `INSERT` override | | `kalam.user_id` privileges | `kalam.user_id` is registered as a PostgreSQL `SUSET` parameter | | Row identity for `UPDATE`/`DELETE` | The current FDW path uses the first non-system column as its row-identity heuristic | | Explicit transactions | The documented surface is top-level `BEGIN`, `COMMIT`, and `ROLLBACK` blocks | | DDL in transactions | DDL is rejected inside explicit transactions | | Stream tables in transactions | Stream-table writes are rejected inside explicit transactions | | Direct SQL helper | `kalam_exec(text)` is revoked from `PUBLIC` and does not automatically mirror PostgreSQL `search_path` or current schema | | Type conversions | The logical mapping is broader than the extension's dedicated PostgreSQL runtime branches; see [Data Type Conversions](https://kalamdb.org/docs/pg-kalam/type-conversions) | | Reserved columns | `_seq`, `_userid`, and `_deleted` are reserved names; only `_seq` and `_userid` are auto-injected into the current PostgreSQL relation surface | Additional practical limits: - `_seq` is read-only metadata projected from KalamDB. Values supplied in `INSERT` or `UPDATE` are not forwarded. - Multi-row user-table `INSERT` statements must use one `_userid` value consistently. - `UPDATE` and `DELETE` on user tables use the session `kalam.user_id` scope. Reassigning ownership through `_userid` updates is not supported. - The server and table validators are permissive. Some bad option combinations fail on first use rather than at `CREATE SERVER` or `CREATE FOREIGN TABLE` time. ## Prototype Syntax vs Supported Syntax This docs section focuses on the current remote-mode flow. If you see older prototype scripts that mention experimental helpers such as schema opt-in functions, treat those as development artifacts unless they are covered in this chapter. The supported documentation surface today is: - `CREATE EXTENSION` - `CREATE SERVER` / `ALTER SERVER` / `DROP SERVER` - `CREATE TABLE ... USING kalamdb` - `BEGIN` / `COMMIT` / `ROLLBACK` - `kalam_exec(...)` - `SELECT`, `INSERT`, `UPDATE`, `DELETE` - extension helper functions and `kalam.user_id` ## Troubleshooting | Symptom | Likely cause | What to check | |---|---|---| | `extension "pg_kalam" is not available` | Extension files are not installed into the active PostgreSQL instance | Reinstall the extension into that PostgreSQL 16 installation | | `could not access file "$libdir/pg_kalam"` | PostgreSQL cannot find the shared library | Verify the extension was installed into the correct PostgreSQL library directory | | `CREATE TABLE ... USING kalamdb` does not mirror DDL | The preload hook is not active | Check `SHOW shared_preload_libraries` and confirm it includes `pg_kalam`, then restart PostgreSQL | | Remote connection failure | The foreign-server host or port is wrong, the HTTP API port was used, or KalamDB is down | Verify the foreign server options and point them at the KalamDB RPC endpoint | | Auth failures | `auth_header` is missing or stale | Update the foreign server definition with the right bearer token or shared secret | | User-table reads or writes fail | `kalam.user_id` was not set, or the role cannot set it | Set `kalam.user_id` in the PostgreSQL session and verify the PostgreSQL role can change `SUSET` parameters | | `permission denied for function kalam_exec` | `kalam_exec(text)` is revoked from `PUBLIC` | Use a superuser or `GRANT EXECUTE ON FUNCTION kalam_exec(text)` to the desired role | | `UPDATE` or `DELETE` targets the wrong row | The table's primary key is not the first user column | Recreate the table with the primary key first or use a carefully controlled direct SQL path | | `IMPORT FOREIGN SCHEMA` fails | Feature gap in current remote mode | Use `CREATE TABLE ... USING kalamdb` or `kalam_exec(...)` instead | ## Related - [Getting Started](https://kalamdb.org/docs/pg-kalam/getting-started) - [SQL Syntax](https://kalamdb.org/docs/pg-kalam/sql-syntax) - [Data Type Conversions](https://kalamdb.org/docs/pg-kalam/type-conversions) - [KalamDB PostgreSQL extension source guide](https://github.com/kalamdb/KalamDB/tree/main/pg) ================================================================================ # SQL Syntax URL: https://kalamdb.org/docs/pg-kalam/sql-syntax Source: content/pg-kalam/sql-syntax.mdx > Reference the current pg_kalam SQL surface, including CREATE TABLE ... USING kalamdb, explicit foreign tables, system columns, direct SQL passthrough, and transaction constraints. # PostgreSQL Extension SQL Syntax This page documents the current SQL surface for the `pg_kalam` PostgreSQL extension. The key implementation detail is that the extension exposes two different table-definition paths: 1. `CREATE TABLE ... USING kalamdb` The shorthand authoring path backed by the DDL hook. This currently assumes the default server name `kalam_server`. 2. `CREATE FOREIGN TABLE ... SERVER ` The explicit PostgreSQL FDW path. Use this when you need a non-default server name or want to mirror a relation directly. ## Extension Metadata Install and verify the extension: ```sql CREATE EXTENSION IF NOT EXISTS pg_kalam; SELECT kalam_version(), kalam_compiled_mode(); ``` Current helper functions: ```sql SELECT kalam_version(); SELECT kalam_compiled_mode(); SELECT kalam_user_id(); SELECT kalam_user_id_guc_name(); SELECT SNOWFLAKE_ID(); ``` `kalam_compiled_mode()` should currently return `remote`. ## Foreign Server Syntax Create the PostgreSQL foreign server that points at KalamDB: ```sql CREATE SERVER IF NOT EXISTS kalam_server FOREIGN DATA WRAPPER pg_kalam OPTIONS ( host '127.0.0.1', port '2910', auth_header 'Bearer ' ); ``` Update it with standard PostgreSQL FDW server syntax: ```sql ALTER SERVER kalam_server OPTIONS (SET host 'kalamdb.internal', SET port '2910'); ALTER SERVER kalam_server OPTIONS (ADD auth_header 'Bearer '); ALTER SERVER kalam_server OPTIONS (SET timeout '5000'); ALTER SERVER kalam_server OPTIONS (DROP auth_header); ``` Drop it when you no longer need the bridge: ```sql DROP SERVER kalam_server CASCADE; ``` ### Supported Server Options | Option | Required | Purpose | |---|---|---| | `host` | Yes | KalamDB host name or IP | | `port` | Yes | KalamDB gRPC port | | `auth_header` | No | Authorization header sent to KalamDB | | `timeout` | No | Request timeout in milliseconds | | `ca_cert` | No | PEM CA bundle for TLS | | `client_cert` | No | PEM client certificate for mTLS | | `client_key` | No | PEM client private key for mTLS | If you set either `client_cert` or `client_key`, you must set both. Point `host` and `port` at KalamDB's shared RPC listener, not the HTTP API port. ## `CREATE TABLE ... USING kalamdb` The public table-definition syntax is `CREATE TABLE ... USING kalamdb`: ```sql CREATE TABLE app.orders ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), customer_id TEXT NOT NULL, status TEXT DEFAULT 'pending', created_at TIMESTAMP DEFAULT NOW() ) USING kalamdb WITH ( type = 'shared', flush_policy = 'rows:1000,interval:60' ); ``` This does three things: 1. resolves the PostgreSQL schema and table name into the mirrored KalamDB namespace and table, 2. creates the target namespace and KalamDB table if needed, 3. creates the local foreign table surface and injects the PostgreSQL-visible system columns that `pg_kalam` manages locally. ### Shared table example ```sql CREATE TABLE app.shared_items ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), title TEXT NOT NULL, value INTEGER, created_at TIMESTAMP DEFAULT NOW() ) USING kalamdb WITH ( type = 'shared', flush_policy = 'rows:1000,interval:60' ); ``` ### User table example ```sql SET kalam.user_id = 'user-alice'; CREATE TABLE app.profiles ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), name TEXT, age INTEGER ) USING kalamdb WITH ( type = 'user' ); ``` ### Stream table example ```sql CREATE TABLE app.events ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), event_type TEXT NOT NULL, payload TEXT, created_at TIMESTAMP DEFAULT NOW() ) USING kalamdb WITH ( type = 'stream', ttl_seconds = '3600' ); ``` ### Notes for `USING kalamdb` - `USING kalamdb` selects the extension-managed PostgreSQL authoring path. - `WITH (type = 'shared' | 'user' | 'stream')` selects the KalamDB table type. - `PRIMARY KEY` and `DEFAULT SNOWFLAKE_ID()` are preserved in KalamDB. - `SERIAL`, `BIGSERIAL`, `SMALLSERIAL`, and `GENERATED ... AS IDENTITY` are normalized into integer columns with `DEFAULT SNOWFLAKE_ID()` before the KalamDB DDL is sent. - Additional `WITH (...)` options are forwarded to the mirrored KalamDB `CREATE TABLE` statement. - This shorthand path currently depends on the DDL preload hook and the default server name `kalam_server`. - The resulting PostgreSQL object is a foreign table, so follow-up DDL should use `ALTER FOREIGN TABLE` and `DROP FOREIGN TABLE`. ## `CREATE FOREIGN TABLE ... SERVER ...` Use explicit foreign tables when you need a non-default server or want to mirror a remote relation directly: ```sql CREATE SERVER kalam_eu FOREIGN DATA WRAPPER pg_kalam OPTIONS ( host '10.0.0.25', port '2910', auth_header 'Bearer ' ); CREATE FOREIGN TABLE app.audit_log ( id BIGINT, action TEXT, payload JSONB ) SERVER kalam_eu OPTIONS ( table_type 'shared' ); ``` Current mapping rules: - The local PostgreSQL schema is the remote namespace. - The local PostgreSQL relation name is the remote table name. - `OPTIONS (namespace, table)` are not the runtime source of truth for the current scan and modify paths. - If a remote column is `FILE`, declare the local PostgreSQL column as `JSONB`. ## JSON and JSONB `pg_kalam` maps PostgreSQL `JSON` and `JSONB` authoring types to KalamDB `JSON`. On the `CREATE TABLE ... USING kalamdb` path, the local PostgreSQL column keeps the type you declared, so use `JSONB` when you want PostgreSQL's richer local JSON operator surface. `FILE` is the special case: it is always mirrored locally as `JSONB`. ```sql CREATE TABLE app.events ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), payload JSONB NOT NULL ) USING kalamdb WITH ( type = 'shared' ); SELECT payload->>'kind' AS kind FROM app.events WHERE payload ? 'kind'; ``` That syntax compatibility is the contract; PostgreSQL may still evaluate the JSONB expression locally on the mirrored value unless you explicitly route the statement through `kalam_exec(...)`. When you do send SQL through `kalam_exec(...)`, KalamDB itself supports the same common PostgreSQL-style JSON operators: ```sql SELECT kalam_exec($$ SELECT payload->>'kind' AS kind, payload->'metadata' AS metadata FROM app.events WHERE payload ? 'kind' $$); ``` KalamDB-side JSON helper functions are also available, including `json_get`, `json_as_text`, `json_contains`, `json_length`, `json_object_keys`, and typed extractors such as `json_get_int`, `json_get_float`, and `json_get_bool`. Current scope: - KalamDB directly plans `->`, `->>`, and `?` today. - Do not assume full PostgreSQL `jsonb` operator parity for operators such as `#>`, `#>>`, or `@>` yet. ## `FILE` `FILE` is the main PostgreSQL extension-specific datatype shortcut. ```sql CREATE TABLE app.assets ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), attachment FILE ) USING kalamdb WITH ( type = 'shared' ); SELECT attachment->>'name', attachment->>'mime' FROM app.assets; ``` Current `FILE` behavior: - The remote KalamDB schema keeps the `FILE` logical type. - The local PostgreSQL surface rewrites that column to `JSONB`. - PostgreSQL reads and writes the serialized `FileRef` JSON representation. - Binary upload and file lifecycle usually happen through KalamLink or another file-aware KalamDB client, not through a raw PostgreSQL `BYTEA` upload path. ## System Columns `pg_kalam` manages system columns itself. Do not declare them explicitly in `CREATE TABLE ... USING kalamdb`. | Column | Where it appears | Rules | |---|---|---| | `_seq` | All `pg_kalam` PostgreSQL tables | Auto-injected as `BIGINT`, populated from KalamDB read results, read-only from PostgreSQL, and not forwarded when supplied in `INSERT` or `UPDATE` | | `_userid` | User tables only | Auto-injected as `TEXT`, exposed on reads, and usable as an explicit tenant override only for `INSERT` | Current rules: - `CREATE TABLE ... USING kalamdb (..., _seq BIGINT, ...)` is rejected. The same applies to `_userid` and `_deleted`. - `_seq` is a read-only metadata column. Query it, sort by it, or return it to clients, but do not treat it as an application-managed field. - For user tables, omitting `_userid` on `INSERT` uses the current `kalam.user_id` session value. - For user tables, including `_userid` on `INSERT` overrides the session value for that inserted row batch. - In a multi-row `INSERT`, all rows must use the same `_userid` value. - `UPDATE` and `DELETE` continue to use the session's `kalam.user_id` scope. Reassigning ownership with `UPDATE ... SET _userid = ...` is not in the supported surface. - `_deleted` is reserved but is not auto-injected into the current PostgreSQL relation surface. Example: ```sql SET kalam.user_id = 'user-alice'; INSERT INTO app.profiles (id, name, age) VALUES (SNOWFLAKE_ID(), 'Alice', 30); INSERT INTO app.profiles (id, name, age, _userid) VALUES (SNOWFLAKE_ID(), 'Bob', 41, 'user-bob'); SELECT id, name, _userid, _seq FROM app.profiles ORDER BY _seq; ``` ## Forwarded Table Options Any supported KalamDB table option can be authored from PostgreSQL with the table `WITH (...)` clause. That means PostgreSQL can act as the entry point for KalamDB-native table definitions as long as the resulting table options are valid for KalamDB once the extension forwards them. ### Common forwarded `WITH (...)` values | Option | Purpose | |---|---| | `type` | Table type: `shared`, `user`, or `stream` | | `flush_policy` | Hot-path flush behavior | | `storage_id` | Target storage backend | | `access_level` | Shared-table access policy | | `ttl_seconds` | Stream retention TTL | | `deleted_retention_hours` | Soft-delete retention window | ## Direct Statement Execution To pass any statement directly to KalamDB, use `kalam_exec(...)`: ```sql SELECT kalam_exec('ALTER TABLE app.orders ADD COLUMN priority INTEGER DEFAULT 0'); SELECT kalam_exec('DROP TABLE app.orders'); SELECT kalam_exec('SELECT * FROM system.tables'); ``` `kalam_exec(...)` sends the SQL string to the KalamDB server as-is. - For `SELECT` statements, it returns a JSON array string. - For DDL and DML statements, it returns a JSON string message. - It uses the default foreign server `kalam_server`. - It is revoked from `PUBLIC`. - It does not currently mirror PostgreSQL `search_path` or current schema automatically, so schema-qualify the statement. If you were expecting a schema-qualified helper such as `kalam.exec(...)`, the currently exposed PostgreSQL function name is `kalam_exec(...)`. ## Query and Mutation Syntax ### Select ```sql SELECT id, title, value FROM app.shared_items WHERE value >= 10 ORDER BY id; ``` ### Insert ```sql INSERT INTO app.shared_items (id, title, value) VALUES (SNOWFLAKE_ID(), 'Alpha', 10), (SNOWFLAKE_ID(), 'Beta', 20); ``` `_seq` is visible for reads but is not a writable application column. For PostgreSQL-style upsert and `RETURNING`, send the statement through `kalam_exec(...)` so KalamDB executes it on the server: ```sql SELECT kalam_exec($$ INSERT INTO app.shared_items (id, title, value) VALUES (1, 'Beta', 20) ON CONFLICT (id) DO UPDATE SET title = EXCLUDED.title, value = EXCLUDED.value RETURNING id, title, value $$); ``` See [/docs/server/sql-reference/dml](https://kalamdb.org/docs/server/sql-reference/dml) for upsert and `RETURNING` rules and limits. ### Update ```sql UPDATE app.shared_items SET value = 25 WHERE id = 2; ``` ### Delete ```sql DELETE FROM app.shared_items WHERE id = 1; ``` ### User-scoped DML ```sql SET kalam.user_id = 'user-alice'; INSERT INTO app.profiles (id, name, age) VALUES ('p1', 'Alice', 30); INSERT INTO app.profiles (id, name, age, _userid) VALUES ('p2', 'Bob', 41, 'user-bob'); SELECT id, name, age, _userid, _seq FROM app.profiles; UPDATE app.profiles SET age = 31 WHERE id = 'p1'; DELETE FROM app.profiles WHERE id = 'p1'; RESET kalam.user_id; ``` In practice, `kalam.user_id` is a PostgreSQL `SUSET` setting, so the executing role must be allowed to change `SUSET` parameters. ## Explicit Transaction Syntax The PostgreSQL extension supports explicit transaction control for Kalam-backed tables: ```sql BEGIN; START TRANSACTION; COMMIT; COMMIT WORK; ROLLBACK; ROLLBACK WORK; ``` ### Commit example ```sql BEGIN; INSERT INTO app.shared_items (id, title, value) VALUES (SNOWFLAKE_ID(), 'Alpha', 10); UPDATE app.shared_items SET value = 25 WHERE title = 'Alpha'; COMMIT; ``` ### Read-your-writes and rollback example ```sql BEGIN; INSERT INTO app.shared_items (id, title, value) VALUES (SNOWFLAKE_ID(), 'Draft', 99); SELECT id, title, value FROM app.shared_items WHERE title = 'Draft'; ROLLBACK; ``` Within the same PostgreSQL transaction, reads see the staged writes from that transaction. On `COMMIT`, the staged writes are made durable in KalamDB. On `ROLLBACK`, they are discarded. ### Transaction observability Use direct KalamDB SQL or `kalam_exec(...)` when you want to inspect the server-side session and transaction views from PostgreSQL. Example via `kalam_exec(...)` for active pg bridge sessions: ```sql SELECT kalam_exec( 'SELECT session_id, transaction_id, transaction_state, transaction_has_writes FROM system.sessions WHERE transaction_id IS NOT NULL ORDER BY last_seen_at DESC, session_id' ); ``` Example via `kalam_exec(...)` for active explicit transactions across pg and native KalamDB SQL: ```sql SELECT kalam_exec( 'SELECT transaction_id, owner_id, origin, state, write_count FROM system.transactions ORDER BY origin, transaction_id' ); ``` ### Current limits inside explicit transactions - Shared and user table DML are supported. - Stream-table writes are rejected inside explicit transactions. - DDL inside explicit transactions is not supported. - Savepoints and PostgreSQL subtransactions are not yet supported. ## Row Identity for `UPDATE` and `DELETE` The current FDW modify path uses the first non-system column as its row-identity heuristic. Practical guidance: - Keep the primary key as the first user column in the table definition. - If you are mirroring an existing remote table and expect heavy `UPDATE` or `DELETE` use, validate the behavior against your exact schema before depending on it in production. ## Not Yet Supported `IMPORT FOREIGN SCHEMA` is not yet supported in the current remote mode: ```sql IMPORT FOREIGN SCHEMA app FROM SERVER kalam_server INTO app; ``` For now, use `CREATE TABLE ... USING kalamdb` for PostgreSQL-side authoring or `kalam_exec(...)` for direct server execution. ## Related - [Getting Started](https://kalamdb.org/docs/pg-kalam/getting-started) - [Architecture](https://kalamdb.org/docs/pg-kalam/architecture) - [Data Type Conversions](https://kalamdb.org/docs/pg-kalam/type-conversions) - [Status & Limits](https://kalamdb.org/docs/pg-kalam/limitations) ================================================================================ # Data Type Conversions URL: https://kalamdb.org/docs/pg-kalam/type-conversions Source: content/pg-kalam/type-conversions.mdx > See how pg_kalam maps PostgreSQL types to KalamDB logical types, Arrow payloads, and back into PostgreSQL, including FILE and current runtime caveats. # PostgreSQL Extension Data Type Conversions This page documents the current conversion behavior in `pg_kalam`. There are three distinct layers to keep straight: 1. PostgreSQL-side DDL authoring and logical schema mapping. 2. PostgreSQL datum conversion on writes. 3. Arrow batch materialization on reads. The important constraint is that the canonical KalamDB logical type system is broader than the extension's dedicated PostgreSQL runtime branches. The logical mapping exists for more types than the extension can currently materialize natively on the PostgreSQL side. If this page and the code diverge, the code wins. ## 1. PostgreSQL Authoring to KalamDB Logical Types These are the common PostgreSQL-facing type names that map into KalamDB's logical schema model today. | PostgreSQL-side authoring type | KalamDB logical type | Arrow or storage representation | Notes | |---|---|---|---| | `BOOLEAN`, `BOOL` | `Boolean` | `Boolean` | Dedicated native read and write branches exist | | `SMALLINT`, `INT2` | `SmallInt` | `Int16` | Dedicated native read and write branches exist | | `INTEGER`, `INT`, `INT4`, `MEDIUMINT` | `Int` | `Int32` | Dedicated native read and write branches exist | | `BIGINT`, `INT8` | `BigInt` | `Int64` | Dedicated native read and write branches exist | | `REAL`, `FLOAT`, `FLOAT4` | `Float` | `Float32` | Dedicated native read and write branches exist | | `DOUBLE PRECISION`, `DOUBLE`, `FLOAT8`, `FLOAT64` | `Double` | `Float64` | Dedicated native read and write branches exist | | `TEXT`, `VARCHAR`, `CHAR`, `STRING`, `NVARCHAR` | `Text` | `Utf8` | `TEXT` and `VARCHAR` have dedicated write fast paths; `CHAR` currently uses the generic text-output fallback on writes | | `BYTEA`, `BYTES`, `BINARY`, `VARBINARY`, `BLOB` | `Bytes` | `Binary` | Read path has a dedicated `BYTEA` materializer; write path currently falls back to PostgreSQL text output | | `JSON`, `JSONB` | `Json` | `Utf8` containing JSON text | JSON and JSONB are first-class in the extension on both read and write | | `DATE` | `Date` | `Date32` | Read path has a dedicated `DATE` materializer; write path currently falls back to PostgreSQL text output | | `TIME` | `Time` | `Time64(Microsecond)` | Logical mapping exists, but read and write still rely on generic conversion fallback | | `TIMESTAMP` | `Timestamp` | `Timestamp(Microsecond, None)` | Read path has a dedicated timestamp materializer; write path currently falls back to PostgreSQL text output | | `NUMERIC(p,s)`, `DECIMAL(p,s)` | `Decimal { precision, scale }` | `Decimal128(p, s)` | Logical mapping exists; PostgreSQL numeric materialization is not yet a dedicated branch | | `UUID` | `Uuid` | `FixedSizeBinary(16)` | Logical mapping exists; PostgreSQL UUID materialization is not yet a dedicated branch | | `FILE` | `File` | `Utf8` containing serialized `FileRef` JSON | The remote KalamDB column is `FILE`; the local PostgreSQL column is rewritten to `JSONB` on the `USING kalamdb` path | Two special cases need separate callouts: - `KalamDataType::DateTime` is the timezone-aware logical type and stores as `Timestamp(Microsecond, Some("UTC"))`. Its reverse PostgreSQL type name is `TIMESTAMPTZ`, but PostgreSQL-side timezone-aware authoring spellings should be validated against your exact build. - `KalamDataType::Embedding(n)` is the logical embedding type. Its reverse PostgreSQL type name is `VECTOR(n)`, but the KalamDB logical DDL spelling is still `EMBEDDING(n)`, so PostgreSQL-side authoring should be validated carefully instead of assuming full shorthand parity. ## 2. KalamDB Logical Types Back to PostgreSQL When the extension needs to generate or mirror PostgreSQL column type names from a KalamDB logical schema, it uses the following canonical mapping. | KalamDB logical type | Generated or mirrored PostgreSQL type name | |---|---| | `Boolean` | `BOOLEAN` | | `SmallInt` | `SMALLINT` | | `Int` | `INTEGER` | | `BigInt` | `BIGINT` | | `Float` | `REAL` | | `Double` | `DOUBLE PRECISION` | | `Text` | `TEXT` | | `Bytes` | `BYTEA` | | `Date` | `DATE` | | `Time` | `TIME` | | `Timestamp` | `TIMESTAMP` | | `DateTime` | `TIMESTAMPTZ` | | `Uuid` | `UUID` | | `Json` | `JSONB` | | `File` | `JSONB` | | `Decimal { precision, scale }` | `NUMERIC(p, s)` | | `Embedding(n)` | `VECTOR(n)` | Current local-surface rules modify that canonical reverse mapping in a few places: - On `CREATE TABLE ... USING kalamdb`, most local PostgreSQL columns keep the type you declared. - `JSON` stays `JSON` locally if you declared `JSON`. - `JSONB` stays `JSONB` locally if you declared `JSONB`. - `FILE` is the special case: it is always rewritten locally to `JSONB`. If you mirror a remote `FILE` column manually with `CREATE FOREIGN TABLE`, declare the local PostgreSQL column as `JSONB`. ## 3. DDL Normalizations Before Remote Create The DDL hook performs a small set of PostgreSQL-specific normalizations before sending the user-column definition to KalamDB. | PostgreSQL authoring form | Remote KalamDB DDL result | |---|---| | `SERIAL` | Integer column plus `DEFAULT SNOWFLAKE_ID()` | | `BIGSERIAL` | Bigint column plus `DEFAULT SNOWFLAKE_ID()` | | `SMALLSERIAL` | Smallint column plus `DEFAULT SNOWFLAKE_ID()` | | `GENERATED ... AS IDENTITY` | Explicit integer type plus `DEFAULT SNOWFLAKE_ID()` | This normalization happens before the remote `CREATE TABLE` is sent. ## 4. Runtime Read and Write Behavior Today The table above describes the logical schema contract. The next table describes the actual extension implementation status at runtime. | Type family | PostgreSQL -> KalamDB write path | KalamDB -> PostgreSQL read path | Current status | |---|---|---|---| | `BOOLEAN`, `SMALLINT`, `INTEGER`, `BIGINT`, `REAL`, `DOUBLE PRECISION` | Dedicated native `Datum -> ScalarValue` branches | Dedicated native Arrow array branches | Best-covered native round-trip surface | | `TEXT`, `VARCHAR` | Dedicated text fast path | Dedicated `Utf8` and `LargeUtf8` branches | Best-covered string surface | | `CHAR`, `CHARACTER` | Generic PostgreSQL type-output fallback to `ScalarValue::Utf8` | Reads through the normal `Utf8` text path once the backend returns text | Logical mapping exists, but writes are not a dedicated branch | | `JSON`, `JSONB` | Dedicated JSON and JSONB normalization to JSON text | Dedicated PostgreSQL type-input path for JSON and JSONB | Best-covered document surface | | `FILE` | PostgreSQL sees the `FileRef` JSON representation, usually as `JSONB`; binary upload itself typically happens through KalamLink or another file-aware client | Dedicated JSONB materialization from UTF-8 `FileRef` JSON | Stable local representation, special-case local `JSONB` surface | | `BYTEA` | Generic PostgreSQL type-output fallback to `ScalarValue::Utf8` | Dedicated Arrow `Binary -> bytea` branch | Read path is native; write path relies on generic coercion | | `DATE` | Generic PostgreSQL type-output fallback to `ScalarValue::Utf8` | Dedicated Arrow `Date32 -> DATE` branch | Read path is native; write path relies on generic coercion | | `TIMESTAMP` and generated `TIMESTAMPTZ` surface | Generic PostgreSQL type-output fallback to `ScalarValue::Utf8` | Dedicated Arrow timestamp branches reuse PostgreSQL's internal timestamp representation | Read path is native; write path relies on generic coercion | | `TIME` | Generic PostgreSQL type-output fallback to `ScalarValue::Utf8` | Generic fallback string rendering | Not yet a dedicated PostgreSQL round-trip branch | | `UUID` | Generic PostgreSQL type-output fallback to `ScalarValue::Utf8` | No dedicated UUID materialization branch in `arrow_to_pg` | Logical mapping exists; validate against your build before treating UUID as a stable PG round-trip surface | | `NUMERIC(p,s)` | Generic PostgreSQL type-output fallback to `ScalarValue::Utf8` | No dedicated numeric materialization branch in `arrow_to_pg` | Logical mapping exists; native PostgreSQL numeric round-trip is not implemented yet | | `VECTOR(n)` or `Embedding(n)` | PostgreSQL-side authoring depends on local type availability and current DDL spelling | No dedicated vector materialization branch in `arrow_to_pg` | Reverse mapping exists, but PostgreSQL vector round-trip is not yet a native branch | ## 5. Under the Hood ### Write path For `INSERT`, `UPDATE`, and `DELETE`, the extension does the following: 1. PostgreSQL exposes a `TupleTableSlot` to the FDW modify callbacks. 2. `slot_to_row()` walks the local columns, skipping `_seq` and `_deleted`. 3. `_userid` is captured separately and only used as an explicit tenant override for `INSERT`. 4. `pg_to_kalam::datum_to_scalar()` converts each remaining datum into a DataFusion `ScalarValue`. 5. Supported primitive OIDs use dedicated branches. 6. Everything else currently falls back to PostgreSQL's type output function and becomes `ScalarValue::Utf8`. 7. The client serializes mutations as JSON row payloads in the gRPC request. That is why the write path is broader than the dedicated fast-path branches but still depends on server-side coercion for several PostgreSQL type families. ### Read path For `SELECT`, the extension does the following: 1. Builds a `ScanRequest` with relation identity, table type, tenant context, and simple equality filters. 2. Sends the request over gRPC. 3. Receives Arrow IPC batches from KalamDB. 4. `arrow_to_pg::arrow_value_to_datum()` materializes supported Arrow types directly into PostgreSQL datums. 5. JSON and JSONB use PostgreSQL type-input functions so PostgreSQL sees real document datums, not plain text. 6. Unsupported Arrow types fall back to `ScalarValue::to_string()`. That final fallback is why logical schema support is currently broader than the stable PostgreSQL materialization surface. ## 6. What Is Safest Today If you need the most predictable PostgreSQL behavior today, prefer these surfaces first: - `BOOLEAN`, `SMALLINT`, `INTEGER`, `BIGINT`, `REAL`, `DOUBLE PRECISION` - `TEXT` and `VARCHAR` - `JSON`, `JSONB`, and `FILE` as local `JSONB` - `DATE`, `TIMESTAMP`, and the timestamp-like generated `TIMESTAMPTZ` read path Use extra care and validate against your target build for: - `TIME` - `UUID` - `NUMERIC(p,s)` - `VECTOR(n)` or other embedding-heavy PostgreSQL surfaces The strongest current automated coverage in the extension repo is around JSON, JSONB, and `FILE` behavior. The rest of the mapping surface is primarily code-backed rather than broad end-to-end type-matrix coverage. ## Related - [SQL Syntax](https://kalamdb.org/docs/pg-kalam/sql-syntax) - [Status & Limits](https://kalamdb.org/docs/pg-kalam/limitations) - [KalamDB Data Types Reference](https://kalamdb.org/docs/server/architecture/datatypes) ================================================================================ # Create a Table in Admin UI URL: https://kalamdb.org/docs/server/admin-ui/create-table Source: content/server/admin-ui/create-table.mdx > Create a KalamDB table from the Admin UI table editor, define fields, choose storage options, and verify the schema in SQL Studio. # Create a Table in the KalamDB Admin UI Create a table in the KalamDB Admin UI with the visual table editor, where you define the namespace, table type, table name, storage options, flush policy, compression, and fields before reviewing the generated schema change. ## Open The Table Editor Open SQL Studio, choose the target namespace, then select `New table` from the explorer toolbar. The editor starts with: - namespace context - table type selector - table name - storage ID and optional user-storage toggle - flush policy and compression controls - column rows with primary key, type, nullable, unique, and default controls - `Discard` and `Review & Create` actions ## Define Fields For a starter `agent_events` table: 1. Set `Table name` to `agent_events`. 2. Keep the default `id` primary key unless your schema needs a different key. 3. Use `Add column` for each additional field. 4. Set field names such as `agent_id` and `event_type`. 5. Choose each field type from the type selector. 6. Adjust nullable, unique, and default values before review. Use `Review & Create` to inspect the schema change, then confirm creation. The action is disabled until required fields are valid. ## Verify The Table After creation, the table appears in the namespace explorer. Open SQL Studio and run: ```sql SELECT id, agent_id, event_type FROM default.agent_events LIMIT 20; ``` If you create the table in another namespace, replace `default` with that namespace. ## Related SQL Reference - [Tables](https://kalamdb.org/docs/server/sql-reference/tables) - [Data Manipulation](https://kalamdb.org/docs/server/sql-reference/dml) - [Query Data](https://kalamdb.org/docs/server/sql-reference/query-data) ================================================================================ # Admin UI Dashboard URL: https://kalamdb.org/docs/server/admin-ui/dashboard Source: content/server/admin-ui/dashboard.mdx > Read the KalamDB Admin UI dashboard to inspect server health, activity summaries, active connections, and operational pressure. # KalamDB Admin UI Dashboard The KalamDB Admin UI dashboard is the first operational page for checking server health, recent activity, workload pressure, storage usage, and cluster status. ## What The Dashboard Shows Use the dashboard before deeper inspection. It includes: - session greeting and quick access to the statistics SQL view - time-range selector and manual refresh - summary cards for uptime, tables, namespaces, connections, subscriptions, pub/sub, total queries, query rate, and average latency - charts for SQL query activity, connections and subscriptions, pub/sub consumers, resource usage, and manifest/parquet rates - slow query table with timestamp, SQL statement, duration, priority, and pagination - storage usage card with backend selector, used/free capacity, path, usage percentage, and health - cluster node health with node count, leader status, active/joining state, memory, CPU, uptime, and replication lag ## When To Use It Start here when you need to decide where to drill in next: SQL Studio for query behavior, Live Queries for subscription pressure, Streaming for topics and consumers, Logs & Analytics for slow queries and jobs, or Settings for runtime configuration. ================================================================================ # Admin UI URL: https://kalamdb.org/docs/server/admin-ui Source: content/server/admin-ui/index.mdx > Use the KalamDB Admin UI to inspect server state, run SQL, manage users, monitor live queries, and operate realtime workloads. # KalamDB Admin UI The KalamDB Admin UI is the browser workspace for authentication-aware server operations, SQL Studio work, schema editing, user management, realtime monitoring, logging, and runtime configuration review. ## Admin UI Navigation Open the Admin UI at: ```text http://localhost:2900/ui ``` The sidebar groups the primary operator pages: - [Login](https://kalamdb.org/docs/server/admin-ui/login) for local and OIDC browser authentication - [Dashboard](https://kalamdb.org/docs/server/admin-ui/dashboard) for health cards, metric charts, slow queries, storage usage, and cluster node status - [SQL Studio](https://kalamdb.org/docs/server/admin-ui/sql-studio) for namespace browsing, table inspection, saved query tabs, SQL execution, and visual schema editing - [Create a Table](https://kalamdb.org/docs/server/admin-ui/create-table) for the visual table editor workflow - [Streaming](https://kalamdb.org/docs/server/admin-ui/streaming) for topic inventory, topic inspection, consumer offsets, and topic-to-SQL navigation - [Users](https://kalamdb.org/docs/server/admin-ui/users) for users, roles, pending invites, pagination, edits, and account lifecycle actions - [Live Queries](https://kalamdb.org/docs/server/admin-ui/live-queries) for active WebSocket subscriptions, filters, change counts, and kill actions - [Logging](https://kalamdb.org/docs/server/admin-ui/logging) for audit events, server logs, background jobs, and slow query analytics - [Settings](https://kalamdb.org/docs/server/admin-ui/settings) for current-user context and grouped server, storage, cluster, backup, and security settings ## Shared Layout Every authenticated page keeps the same shell: - top bar with connection action, global search, WebSocket status, notifications, and current-user menu - icon sidebar for Dashboard, SQL Studio, Streaming, Users, Live Queries, Logs, and Settings - authenticated session context from the same login and token flow used by the HTTP and WebSocket APIs ## Authentication Context The Admin UI discovers login modes from `/v1/api/auth/login-options` and uses the same local and OIDC authentication surface described in [Authentication & Bootstrap](https://kalamdb.org/docs/server/getting-started/authentication). For OIDC deployments, register the browser redirect URI documented in [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc). ================================================================================ # Admin UI Jobs URL: https://kalamdb.org/docs/server/admin-ui/jobs Source: content/server/admin-ui/jobs.mdx > Inspect KalamDB background jobs in Logs & Analytics, including status, job type, namespace, duration, node, and job details. # KalamDB Admin UI Jobs Jobs are shown inside the Admin UI [Logs & Analytics](https://kalamdb.org/docs/server/admin-ui/logging) workspace. The Jobs section lists background work and maintenance activity that can explain server load, storage behavior, and delayed operations. ## What Jobs Shows The Jobs section includes: - filters panel - page-size selector and pagination - refresh action - status column - job type column such as cleanup or stream eviction - namespace/table context when the job applies to a data object - created timestamp, duration, and node - detail action for opening a specific job record Pair this section with Server logs when a job needs event-level detail. ================================================================================ # Admin UI Live Queries URL: https://kalamdb.org/docs/server/admin-ui/live-queries Source: content/server/admin-ui/live-queries.mdx > Inspect active KalamDB live queries in the Admin UI, including subscriptions, realtime workload behavior, and query activity. # KalamDB Admin UI Live Queries The Live Queries page shows active realtime subscriptions and WebSocket query activity that affect server load and user-facing updates. ## What Live Queries Shows The page includes: - active query count and total query count - auto-refresh state - filters for user ID, namespace, table, and status - table columns for subscription ID, user, namespace, table, status, duration, change count, SQL query, serving node, and actions - `Kill` action for terminating a problematic subscription ## When To Use It Use this page to identify unexpectedly broad realtime queries, confirm client cleanup during disconnects, compare live query behavior with stream activity and logs, or stop a runaway subscription. For the architecture model, see [Live Query Architecture](https://kalamdb.org/docs/server/architecture/live-query). ================================================================================ # Admin UI Logging URL: https://kalamdb.org/docs/server/admin-ui/logging Source: content/server/admin-ui/logging.mdx > Use the KalamDB Admin UI Logging page to inspect server, job, authentication, and operational events during troubleshooting. # KalamDB Admin UI Logging The Logging page is the Logs & Analytics workspace for event-level troubleshooting across audit events, server logs, background jobs, and slow SQL. ## What Logs & Analytics Shows The page is split into four sections: - `Audit`: security and administrative activity - `Server`: query-backed view of `system.server_logs` with log frequency graph, pause/refresh controls, text filter, case-sensitivity toggle, level filter, row limit selector, timestamp, level, target, and message - `Jobs`: background job table with status, job type, namespace/table, created time, duration, node, filters, pagination, refresh, and detail action - `Slow Queries`: slow query analytics for statements that exceed the configured threshold ## When To Use It Use logs to confirm login/setup behavior, trace failed SQL or maintenance operations, correlate jobs with streaming and live-query events, and collect evidence before changing production configuration. For log configuration, see [Logging Configuration](https://kalamdb.org/docs/server/configurations/logging). ================================================================================ # Admin UI Login URL: https://kalamdb.org/docs/server/admin-ui/login Source: content/server/admin-ui/login.mdx > Sign in to the KalamDB Admin UI with local credentials or OpenID Connect, and verify the login methods exposed by the server. # KalamDB Admin UI Login The KalamDB Admin UI login page uses the server authentication configuration to show local password login, OpenID Connect login, setup routing, or a combination of those options. ## What Login Shows The browser reads available methods from: ```bash curl http://127.0.0.1:2900/v1/api/auth/login-options ``` Depending on server configuration, the page can show: - local username/password fields - provider button such as `Continue with Dex` - setup link for an unconfigured node - explanatory note for local cluster scripts and root credentials - invalid-credentials feedback after failed local login If local auth is enabled, sign in with the DBA account created during bootstrap. If OIDC is enabled, use the configured provider button and complete the provider flow. ## Related Authentication Pages - [Authentication & Bootstrap](https://kalamdb.org/docs/server/getting-started/authentication) - [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc) - [Token Auth](https://kalamdb.org/docs/server/auth/token-auth) ================================================================================ # Admin UI Settings URL: https://kalamdb.org/docs/server/admin-ui/settings Source: content/server/admin-ui/settings.mdx > Review KalamDB Admin UI settings to inspect exposed runtime configuration, authentication state, and operational defaults. # KalamDB Admin UI Settings The Settings page shows current-user context, runtime configuration, and operational defaults exposed by the server to authenticated administrators. ## What Settings Shows The Settings page includes a left-side settings section menu: - `All Settings` - `General` - `Cluster` - `Storages` - `Backup` - `Security` The main view starts with current admin session details: username, role, email, and user ID. It then groups configuration values such as server host/port/workers, public origin, storage paths and RocksDB profiles, cluster ID, DataFusion limits, query limits, logging format/path, flush defaults, manifest cache, stream TTLs, topic retention, WebSocket timeouts, and rate limits. ## When To Use It Use this page to confirm the server origin and Admin UI paths match deployment expectations, verify authentication and security posture, check storage and backup configuration, and review exposed runtime defaults before production rollout. For the full config matrix, see [Advanced Configuration](https://kalamdb.org/docs/server/configurations/advanced). ================================================================================ # Admin UI SQL Studio URL: https://kalamdb.org/docs/server/admin-ui/sql-studio Source: content/server/admin-ui/sql-studio.mdx > Use KalamDB SQL Studio to browse namespaces and tables, run SQL statements, inspect results, and validate schema changes. # KalamDB Admin UI SQL Studio SQL Studio is the Admin UI workspace for exploring namespaces, writing SQL, checking table metadata, editing schemas, and validating query results. ## What SQL Studio Contains SQL Studio has two main work areas: - `Explorer`: namespace picker, namespace create/drop actions, table filter, favorites, table list, table expansion, and table drop actions - `Editor`: table editor and query editor modes, saved/draft query tabs, Monaco SQL editor, results/log tabs, live toggle, save/run controls, execute options, and query actions When a table is selected, the right pane can show metadata and editable schema fields. For new tables, the same area becomes the visual table editor described in [Create a Table](https://kalamdb.org/docs/server/admin-ui/create-table). ## Run SQL Start with a system query: ```sql SELECT * FROM system.namespaces LIMIT 100; ``` Then inspect the result grid and execution details. ## Table And Query Workflows Use this page to: - inspect namespaces and table columns without leaving the browser - save and reopen common query tabs - run SQL and review results or execution logs - turn live mode on for realtime result updates where supported - switch from table inspection into the visual table editor Use [Create a Table](https://kalamdb.org/docs/server/admin-ui/create-table) for the first schema workflow, then return to SQL Studio to query and verify inserted rows. ================================================================================ # Admin UI Streaming URL: https://kalamdb.org/docs/server/admin-ui/streaming Source: content/server/admin-ui/streaming.mdx > Monitor KalamDB streaming topics from the Admin UI, inspect topic activity, and connect realtime stream behavior to SQL topics. # KalamDB Admin UI Streaming The Streaming page shows topic-oriented realtime activity so operators can inspect topic inventory, consumer state, and stream behavior from the Admin UI. ## What Streaming Shows The Streaming area has separate views for topics and consumers: - `Topics`: searchable topic inventory with topic name, partitions, routes, retention window, last update time, and actions - `Consumers`: consumer and offset monitoring for active consumer groups and claims - topic actions to open the relevant SQL view or inspect a topic in place - refresh controls for pulling current topic and consumer state ## When To Use It Use this page to confirm expected topics are visible, check retention and routing metadata, inspect producer/consumer behavior, and correlate stream activity with Jobs and Logs. For SQL topic setup, see [SQL Topics](https://kalamdb.org/docs/server/topic-pubsub/sql-topics). ================================================================================ # Admin UI Users URL: https://kalamdb.org/docs/server/admin-ui/users Source: content/server/admin-ui/users.mdx > Use the KalamDB Admin UI Users page to inspect local users, roles, OIDC identities, and account state during auth operations. # KalamDB Admin UI Users The Users page is the Admin UI surface for reviewing KalamDB identities, roles, pending invites, and authentication state. ## What Users Shows The Users page includes: - search box for filtering users - page-size selector and previous/next pagination controls - refresh action - `Create User` action - `Pending Invites` table with invite ID, email, role, expiration, creation time, reinvite action, and delete action - `Users` table with name, user ID, role, email, creation time, edit action, and delete action - protected handling for system/root accounts where destructive actions may be disabled ## When To Use It Use this page after bootstrap, local-user changes, or OIDC rollout to confirm the DBA account exists, roles match expectations, invites are still valid, and service/system accounts are not accidentally removed. For SQL user commands, see [Users SQL Reference](https://kalamdb.org/docs/server/sql-reference/users). ================================================================================ # HTTP API Reference URL: https://kalamdb.org/docs/server/api/http-reference Source: content/server/api/http-reference.mdx > Complete HTTP API reference for KalamDB. Explore route maps, auth rules, request/response payloads, error codes, and endpoint contracts. # HTTP API Reference Base URL: `http://:2900` Version prefix: `/v1` ## Route Map ### Health and status - `GET /health` (localhost-only) - `GET /v1/api/healthcheck` (localhost-only) - `GET /v1/api/cluster/health` (localhost-only) ### SQL and files - `POST /v1/api/sql` - `GET /v1/files/{namespace}/{table_name}/{subfolder}/{file_id}` ### WebSocket - `GET /v1/ws` ### Auth - `POST /v1/api/auth/login` - `POST /v1/api/auth/refresh` - `POST /v1/api/auth/logout` - `GET /v1/api/auth/me` - `POST /v1/api/auth/setup` - `GET /v1/api/auth/status` ### Topic HTTP API - `POST /v1/api/topics/consume` - `POST /v1/api/topics/ack` ## Authentication Rules ### Bearer token required ```http Authorization: Bearer ``` - `POST /v1/api/sql` - `GET /v1/files/...` - `POST /v1/api/topics/consume` - `POST /v1/api/topics/ack` Basic auth is rejected on these endpoints. ### Cookie or bearer accepted - `POST /v1/api/auth/refresh` - `GET /v1/api/auth/me` ### Public endpoints - `POST /v1/api/auth/login` - `POST /v1/api/auth/logout` - `POST /v1/api/auth/setup` (localhost-only unless `auth.allow_remote_setup = true`) - `GET /v1/api/auth/status` (localhost-only unless remote setup enabled) - `GET /health` and `GET /v1/api/healthcheck` (localhost-only) - `GET /v1/api/cluster/health` (localhost-only) ## SQL Endpoint ### `POST /v1/api/sql` Headers: - `Authorization: Bearer ` - `Content-Type: application/json` or `multipart/form-data` ### JSON body ```json { "sql": "SELECT * FROM app.messages WHERE id = $1", "params": [123], "namespace_id": "app" } ``` `namespace_id` applies only to that request. Interactive clients such as the CLI can store the namespace locally after a successful `USE namespace` and send it again on later requests. ### Multipart body for `FILE(...)` Parts: - `sql` - `params` (optional JSON array string) - `namespace_id` (optional request-scoped default namespace) - file parts named `file:` SQL: ```sql INSERT INTO app.docs (id, attachment) VALUES ('d1', FILE("contract")); ``` Multipart file key must be `file:contract`. ### Success shape `SELECT` statements and `INSERT ... ON CONFLICT ... DO UPDATE ... RETURNING` statements return query rows in `results[]`: ```json { "status": "success", "results": [ { "schema": [{"name":"id","data_type":"BigInt","index":0}], "rows": [[1]], "row_count": 1, "as_user": "alice" } ], "took": 12.3 } ``` Plain `INSERT`, `UPDATE`, and `DELETE` statements without `RETURNING` return an affected-row count instead of data rows. Upsert without `RETURNING` follows the insert result shape. Example upsert that returns rows: ```sql INSERT INTO app.items (id, name) VALUES (1, 'beta') ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name RETURNING id, name; ``` ### Error shape ```json { "status": "error", "results": [], "took": 1.2, "error": { "code": "INVALID_SQL", "message": "...", "details": null } } ``` ### Common SQL error codes - `INVALID_SQL` - `PERMISSION_DENIED` - `TABLE_NOT_FOUND` - `RATE_LIMIT_EXCEEDED` - `NOT_LEADER` - `CLUSTER_UNAVAILABLE` - `FILE_TOO_LARGE` - `TOO_MANY_FILES` - `INVALID_MIME_TYPE` ## File Download ### `GET /v1/files/{namespace}/{table_name}/{subfolder}/{file_id}` - bearer token required - optional `user_id` query parameter for user-table scope resolution - `SYSTEM` and `STREAM` tables are rejected for file paths Response: - `200` with binary content - `400/403/404` for validation, permission, or not-found failures ## Auth Endpoints ### `POST /v1/api/auth/login` ```json { "user": "alice", "password": "Secret123!" } ``` Returns user profile + access/refresh tokens and sets auth cookie. ### `POST /v1/api/auth/refresh` Accepts bearer token or cookie. Returns rotated token pair. ### `POST /v1/api/auth/logout` Clears auth cookie. ### `GET /v1/api/auth/me` Returns current authenticated user info. ### `POST /v1/api/auth/setup` Initial bootstrap only, when root has no password yet. ```json { "user": "admin", "password": "AdminPass123!", "root_password": "RootPass123!", "email": "admin@example.com" } ``` Sets root password and creates the DBA user. Does not auto-login. ### `GET /v1/api/auth/status` Returns setup status: ```json { "needs_setup": true, "message": "Server requires initial setup..." } ``` ## Topic Endpoints Both require bearer auth and role in `{service, dba, system}`. ### `POST /v1/api/topics/consume` ```json { "topic_id": "orders_topic", "group_id": "worker_group", "start": "Latest", "limit": 100, "partition_id": 0, "timeout_seconds": 10 } ``` Accepted `start` formats: - `"Latest"` - `"Earliest"` - `{ "Offset": 123 }` ### `POST /v1/api/topics/ack` ```json { "topic_id": "orders_topic", "group_id": "worker_group", "partition_id": 0, "upto_offset": 10 } ``` ## Health Endpoints `GET /health` and `GET /v1/api/healthcheck` return the same payload and are localhost-only: ```json { "status": "healthy", "version": "...", "api_version": "v1", "build_date": "..." } ``` For live protocol details, continue to [WebSocket Protocol](https://kalamdb.org/docs/server/api/websocket-protocol). ================================================================================ # API Reference URL: https://kalamdb.org/docs/server/api Source: content/server/api/index.mdx > Complete HTTP and WebSocket API reference for KalamDB. Learn how to integrate backend services, build custom clients, and use realtime streaming endpoints. # API Reference This root chapter contains transport-level contracts for backend integrations, services, and custom clients. - [HTTP API Reference](https://kalamdb.org/docs/server/api/http-reference): Routes, auth rules, request/response payloads, and error codes. - [WebSocket Protocol](https://kalamdb.org/docs/server/api/websocket-protocol): Auth handshake, subscription lifecycle, message schema, limits, and protocol errors. For SQL command syntax, use [SQL Reference](https://kalamdb.org/docs/server/sql-reference). For first-time server setup/login endpoint usage, see [Authentication & Bootstrap](https://kalamdb.org/docs/server/getting-started/authentication). ================================================================================ # WebSocket Protocol URL: https://kalamdb.org/docs/server/api/websocket-protocol Source: content/server/api/websocket-protocol.mdx > KalamDB WebSocket protocol reference. Learn about connection handshakes, auth flows, message contracts, rate limits, and realtime stream behavior. # WebSocket Protocol Endpoint: `ws://:2900/v1/ws` Transport: RFC 6455 Message format: JSON text frames (server may send gzip-compressed binary frames for larger payloads) ## Connection And Authentication ### Upgrade Client opens `GET /v1/ws`. Server can reject pre-upgrade for: - shutdown mode - origin policy failure - strict origin check with missing `Origin` header ### Origin policy Origin allow-list resolution: 1. `security.allowed_ws_origins` if non-empty 2. otherwise `security.cors.allowed_origins` ### Post-connect auth (required) ```json { "type": "authenticate", "method": "jwt", "token": "" } ``` Success: ```json { "type": "auth_success", "user_id": "u_...", "role": "Dba" } ``` Failure: ```json { "type": "auth_error", "message": "Invalid username or password" } ``` ## Client Messages - `authenticate` - `subscribe` - `next_batch` - `unsubscribe` ### `subscribe` ```json { "type": "subscribe", "subscription": { "id": "orders_live", "sql": "SELECT * FROM app.orders WHERE status = 'open'", "options": { "batch_size": 200, "last_rows": 100, "from": 12345 } } } ``` ### `next_batch` ```json { "type": "next_batch", "subscription_id": "orders_live", "last_seq_id": 12345 } ``` ### `unsubscribe` ```json { "type": "unsubscribe", "subscription_id": "orders_live" } ``` ## Server Messages - `auth_success` / `auth_error` - `subscription_ack` - `initial_data_batch` - `change` - `error` ### `subscription_ack` ```json { "type": "subscription_ack", "subscription_id": "orders_live", "total_rows": 0, "batch_control": { "batch_num": 0, "has_more": true, "status": "loading", "last_seq_id": 12345, "snapshot_end_seq": 13000 }, "schema": [ {"name":"id","data_type":"BigInt","index":0} ] } ``` ### `initial_data_batch` ```json { "type": "initial_data_batch", "subscription_id": "orders_live", "rows": [{"id":1,"status":"open","_seq":12346}], "batch_control": { "batch_num": 0, "has_more": true, "status": "loading", "last_seq_id": 12346, "snapshot_end_seq": 13000 } } ``` ### `change` ```json { "type": "change", "subscription_id": "orders_live", "change_type": "insert", "rows": [{"id":2,"status":"open","_seq":13001}] } ``` `change_type` values: - `insert` - `update` - `delete` ### `error` ```json { "type": "error", "subscription_id": "orders_live", "code": "INVALID_SQL", "message": "..." } ``` ## Limits - `rate_limit.max_subscriptions_per_user` (default `10`) - hard cap per connection: `100` - `security.max_ws_message_size` (default `1MB`) - `rate_limit.max_messages_per_sec` (default `50`) - auth timeout: `websocket.auth_timeout_secs` (default `3`) ## Error Codes Common WebSocket protocol/error codes include: - `AUTH_REQUIRED` - `INVALID_SUBSCRIPTION_ID` - `SUBSCRIPTION_LIMIT_EXCEEDED` - `INVALID_SQL` - `RATE_LIMIT_EXCEEDED` - `MESSAGE_TOO_LARGE` - `UNSUPPORTED_DATA` - `PROTOCOL` For endpoint auth matrix and SQL payload contracts, see [HTTP API Reference](https://kalamdb.org/docs/server/api/http-reference). ================================================================================ # Clustering & HA URL: https://kalamdb.org/docs/server/architecture/clustering Source: content/server/architecture/clustering.mdx > Learn how KalamDB clustering works with Raft consensus for high availability, leader election, log replication, and automatic failover. # Clustering & High Availability KalamDB uses a **Multi-Raft** architecture to scale reads, writes, and subscriptions across nodes while keeping data strongly replicated per shard. Cluster routing depends on table type: `USER` and `STREAM` data route by effective `user_id`, while `SHARED` data currently routes through the shared data group. For table-type semantics, see [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types). ## How the Cluster Is Structured KalamDB does not run a single Raft group for everything. It runs multiple groups: - `Meta`: cluster/system metadata - `DataUserShard(N)`: user-data shards - `DataSharedShard(N)`: shared-data shards Each group has its own leader and followers. This means leadership is distributed across the cluster, not centralized to a single node for all workloads. Sharding is handled by a router that hashes user/table identifiers to shard groups, so users are naturally distributed across shards. This is how KalamDB scales tenant load and isolates shard-level hotspots. A single node participates in multiple Raft groups simultaneously. One node is not limited to one group; instead, each node can be a leader for some groups and a follower for others at the same time. ## Multi-Raft Cluster Diagram ```mermaid flowchart TB subgraph Cluster["KalamDB Cluster"] %% Nodes N1["Node 1"] N2["Node 2"] N3["Node 3"] %% Meta Group subgraph Meta["Meta Raft Group"] M1["Leader (Node 1)"] M2["Follower (Node 2)"] M3["Follower (Node 3)"] end %% User Shard Example subgraph UserShard7["DataUserShard(7)"] U1["Leader (Node 2)"] U2["Follower (Node 1)"] U3["Follower (Node 3)"] end %% Another User Shard subgraph UserShard12["DataUserShard(12)"] U4["Leader (Node 3)"] U5["Follower (Node 1)"] U6["Follower (Node 2)"] end %% Shared Shard subgraph SharedShard["DataSharedShard(0)"] S1["Leader (Node 1)"] S2["Follower (Node 2)"] S3["Follower (Node 3)"] end end %% Routing Client["Client Request"] Router["Shard Router (hash user_id / table)"] Client --> Router Router --> UserShard7 Router --> UserShard12 Router --> SharedShard %% Raft replication arrows U1 --> U2 U1 --> U3 U4 --> U5 U4 --> U6 S1 --> S2 S1 --> S3 M1 --> M2 M1 --> M3 ``` ### How to read this diagram - The cluster has multiple Raft groups, not one. - Each group such as `Meta`, `DataUserShard(N)`, or `DataSharedShard(N)` runs its own Raft consensus. - Each group has one leader that handles writes and followers that replicate data and can serve follower-side access patterns. - A single node participates in many groups at once, which is why Node 1, Node 2, and Node 3 appear repeatedly across the diagram. Example: - `DataUserShard(7)` has its leader on Node 2. - `DataUserShard(12)` has its leader on Node 3. So: - load is spread across nodes - there is no single leader bottleneck - multiple writes can happen in parallel across different groups ### Why this matters Instead of one cluster having one leader and turning that leader into a bottleneck, one cluster has many leaders, one per active Raft group. That is the core scaling property of Multi-Raft in KalamDB. ### Tiny mental model ```text user-1 -> shard 7 -> leader on Node 2 user-2 -> shard 12 -> leader on Node 3 user-3 -> shard 7 -> leader on Node 2 ``` So: - the same user stays on the same shard for consistent writes - different users can land on different leaders for parallel scaling ## High Concurrent Connection Model KalamDB accepts WebSocket clients on any node and manages them in a lock-free connection registry: - `ConnectionsManager` uses `DashMap` indexes for fast concurrent connection/subscription access. - Connection lifecycle, authentication timeout, heartbeat timeout, and subscription routing are all centralized there. - The default concurrent connection guard is high (`10,000`), with configurable limits. This allows high fan-in of realtime clients while keeping routing overhead low. ## Leader/Follower Access Pattern Clients can connect to either leader or follower nodes. - **Reads/subscription bootstrap** can be served on the node the client is connected to. - **Writes** are always leader-authoritative per Raft group. - If a request lands on a follower for a write path, KalamDB forwards it to the current leader. - If leadership changed mid-request, typed `NotLeader` handling retries through the known leader. This gives you flexible client routing without sacrificing consistency for writes. ## Notification Forwarding Across Nodes Live subscribers do not need to be connected to the leader node for their shard. When data changes: 1. The shard leader processes the change notification. 2. The leader broadcasts forwarded notifications to follower nodes over cluster RPC. 3. Each follower dispatches to subscriptions connected locally on that follower. This is the key reason you can attach users to any node and still receive correct realtime updates for their shard data. ```mermaid flowchart LR A["Client connected to Follower"] --> B["Follower stores local WS subscription"] C["Write hits shard leader"] --> D["Leader commits via Raft"] D --> E["Leader emits change notification"] E --> F["Leader broadcasts notify_followers RPC"] F --> G["Follower receives forwarded event"] G --> H["Follower pushes update to local client"] ``` ## Sharding + HA in Practice KalamDB shards users across multiple Raft data groups. Each shard group has its own: - leader election - replication log - follower set - failover boundary Operationally, this gives you: - better parallelism (many active leaders across groups) - shard-level fault isolation - horizontal scaling by increasing cluster nodes and shard count - high availability from follower replicas per shard When a node fails, Raft elects new leaders for affected groups and clients can reconnect to any healthy node. ## Supported Cluster Commands Cluster operations are surfaced through CLI meta-commands. See [Interactive Commands — cluster](https://kalamdb.org/docs/cli/interactive-commands) for the operator command list. For raw inspection without CLI formatting, query `system.cluster` and `system.cluster_groups` directly. Old `CLUSTER LIST` / `STATUS` / `LS` SQL forms are replaced by the CLI and system views. `CLUSTER LEAVE` remains unsupported. `CLUSTER TRANSFER LEADER` is still routed by the backend, but some builds can report it as unsupported at runtime depending on the OpenRaft version in use. ## How user data is divided across groups KalamDB uses Multi-Raft rather than one Raft group for the whole cluster. - One group handles metadata. - Multiple user data groups handle user and stream data. - The target user group is chosen by hashing `user_id` into one of `cluster.user_shards` groups. That means all writes for a specific user are coordinated by the same user-data group leader at a given time. The data is still replicated to that group's followers, but the active write path for one user's workload is not scattered across many leaders. This improves locality and reduces cross-group coordination, which is one of the reasons the cluster can stay fast as concurrency grows. ## Follower insert example Suppose `user-42` is mapped to `DataUserShard(7)`, and the client sends an insert to node 2: ```bash kalam --url http://node-2:2900 --command "INSERT INTO app.messages (id, body) VALUES (101, 'hello')" ``` If node 2 is a follower for `DataUserShard(7)`, KalamDB: 1. prepares and classifies the SQL once, 2. resolves the target group from the table type and effective `user_id`, 3. forwards the original SQL, params, auth header, and request id to the leader of `DataUserShard(7)`, 4. lets that leader append and replicate the write through Raft, 5. returns the committed result back through the follower to the client. So clients can write through followers, but the actual commit still happens on the correct group leader. ## Shared tables today Shared tables are currently not truly sharded. Today they route to a single shared data group. More flexible shared-table sharding is a work in progress. The planned direction is partition-by-key so each shared table can define how rows are partitioned and which group should own them. ## Configuration Knobs In cluster mode, tune shard topology from cluster config: - `cluster.user_shards` - `cluster.shared_shards` These values determine how many independent Raft data groups exist for user and shared workloads. To view all the configurations for the Cluster setup: - [/docs/server/configurations/advanced#cluster](https://kalamdb.org/docs/server/configurations/advanced#cluster) ## Deploying a 3-Node Cluster The quickest way to start a cluster is with Docker: ```bash KALAMDB_JWT_SECRET="$(openssl rand -base64 32)" \ curl -sSL https://raw.githubusercontent.com/kalamdb/KalamDB/main/docker/run/cluster/docker-compose.yml | docker-compose -f - up -d ``` ## Backup & Restore ```sql -- Backup a namespace BACKUP DATABASE myapp TO '/backups/myapp-2026-02-18'; -- Restore from backup RESTORE DATABASE myapp FROM '/backups/myapp-2026-02-18'; -- View backup history SHOW BACKUPS FOR DATABASE myapp; ``` ## Related Docs - [/docs/server/sql-reference/cluster](https://kalamdb.org/docs/server/sql-reference/cluster) - [Interactive Commands — cluster](https://kalamdb.org/docs/cli/interactive-commands) - [/docs/server/configurations/advanced#cluster](https://kalamdb.org/docs/server/configurations/advanced#cluster) - [/docs/server/architecture/live-query](https://kalamdb.org/docs/server/architecture/live-query) - [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types) ================================================================================ # Data Types URL: https://kalamdb.org/docs/server/architecture/datatypes Source: content/server/architecture/datatypes.mdx > Practical guide to choosing and using KalamDB data types in SQL. Covers BIGINT, TEXT, BOOLEAN, TIMESTAMP, FILE, EMBEDDING, and more with examples. # Data Types This page is a practical guide for **using data types in SQL** when building with KalamDB. Data types are independent from table types, but some types have table-type-specific behavior. `FILE` columns are supported on `USER` and `SHARED` tables, and `EMBEDDING(n)` columns become most useful with vector search. See [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types) for the table boundary. ## Type Selection Flow ```mermaid flowchart TD A["Data you need to store"] --> B{"Structured document?"} B -- "Yes" --> C["JSON"] B -- "No" --> D{"Time-based value?"} D -- "Timestamped event" --> E["TIMESTAMP"] D -- "Calendar date only" --> F["DATE"] D -- "Not time" --> G{"Exact arithmetic needed?"} G -- "Yes (money/rates)" --> H["DECIMAL(p,s)"] G -- "No" --> I{"Whole number?"} I -- "Small/medium" --> J["INT"] I -- "Large / IDs" --> K["BIGINT"] I -- "No" --> L{"Boolean?"} L -- "Yes" --> M["BOOLEAN"] L -- "No" --> N["TEXT"] ``` ## Quick Type Selection Use this table when deciding a column type: | You store... | Use | Why | |---|---|---| | IDs from `SNOWFLAKE_ID()` | `BIGINT` | 64-bit integer ID generation | | Small whole numbers (status, counters) | `INT` | Compact and fast | | Large counters | `BIGINT` | Higher range | | User-facing text | `TEXT` | Flexible string storage | | True/false values | `BOOLEAN` | Clear semantics | | Decimal money-like values | `DECIMAL(p,s)` | Exact precision | | Timestamps | `TIMESTAMP` | Time-based queries and ordering | | Dates only | `DATE` | Calendar values without time | | Structured document payload | `JSON` | Nested object/array data | | Vector embeddings | `EMBEDDING(n)` | Semantic retrieval and similarity search | ## Common Types in Practice ### Integer Types ```sql CREATE TABLE app.orders ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), user_id BIGINT NOT NULL, item_count INT NOT NULL DEFAULT 1 ) WITH (TYPE = 'USER'); ``` - Prefer `INT` for bounded counts. - Prefer `BIGINT` for IDs and values that can grow large. ### Text and Boolean ```sql CREATE TABLE app.users ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), email TEXT NOT NULL, display_name TEXT, is_active BOOLEAN NOT NULL DEFAULT TRUE ) WITH (TYPE = 'USER'); ``` - Use `TEXT` for variable-length strings. - Use `BOOLEAN` instead of `0/1` integers for feature flags and state. ### Decimal Values ```sql CREATE TABLE billing.invoice_items ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), sku TEXT NOT NULL, amount DECIMAL(12,2) NOT NULL, tax_rate DECIMAL(5,4) NOT NULL DEFAULT 0.0000 ) WITH (TYPE = 'SHARED'); ``` - Use `DECIMAL` for exact arithmetic (pricing, balances, rates). - Pick precision/scale deliberately, e.g. `DECIMAL(12,2)`. ### Time Types ```sql CREATE TABLE chat.messages ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), user_id BIGINT NOT NULL, content TEXT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW() ) WITH (TYPE = 'USER'); SELECT * FROM chat.messages WHERE created_at >= NOW() - INTERVAL '7 days' ORDER BY created_at DESC; ``` - Use `TIMESTAMP` for event records and ordering. - Use `DATE` when time-of-day is not needed. ### JSON Payloads ```sql CREATE TABLE app.events ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), event_name TEXT NOT NULL, payload JSON, created_at TIMESTAMP NOT NULL DEFAULT NOW() ) WITH ( TYPE = 'STREAM', TTL_SECONDS = 300 ); ``` - Use `JSON` for dynamic payloads and schema-flexible metadata. - Keep frequently filtered fields as typed top-level columns. ### Embeddings for Vector Search ```sql CREATE TABLE rag.documents_vectors ( id BIGINT PRIMARY KEY, doc_embedding EMBEDDING(384) ) WITH (TYPE = 'USER'); ALTER TABLE rag.documents_vectors CREATE INDEX doc_embedding USING COSINE; SELECT id FROM rag.documents_vectors ORDER BY COSINE_DISTANCE(doc_embedding, '[0.12,0.03,0.98]') LIMIT 5; ``` - Use `EMBEDDING(n)` for fixed-size vector columns. - Pick `n` to match the embedding model you generate in your app or worker. - Index the embedding column before using it for nearest-neighbor retrieval. ## Type Conversion Use explicit casts when moving between compatible types. ```sql SELECT CAST('42' AS INT) AS count_value; SELECT CAST(amount AS DECIMAL(12,2)) AS normalized_amount FROM billing.invoice_items; ``` Prefer explicit conversion over relying on implicit casting in critical queries. ## Nullability and Defaults Use `NOT NULL` for required fields and `DEFAULT` for stable inserts. ```sql CREATE TABLE app.sessions ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), user_id BIGINT NOT NULL, started_at TIMESTAMP NOT NULL DEFAULT NOW(), ended_at TIMESTAMP, is_valid BOOLEAN NOT NULL DEFAULT TRUE ) WITH (TYPE = 'USER'); ``` ## Recommended Patterns - IDs: `BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID()` - Created timestamps: `TIMESTAMP NOT NULL DEFAULT NOW()` - Money/rates: `DECIMAL` (not floating point) - Optional user text: nullable `TEXT` - Real-time transient events: `STREAM` + `TTL_SECONDS` ## Related Docs - [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types) - [/docs/server/architecture/vector-search](https://kalamdb.org/docs/server/architecture/vector-search) - [/docs/server/architecture/file-upload-datatype](https://kalamdb.org/docs/server/architecture/file-upload-datatype) - [/docs/server/sql-reference/tables](https://kalamdb.org/docs/server/sql-reference/tables) - [/docs/server/sql-reference/file-datatype](https://kalamdb.org/docs/server/sql-reference/file-datatype) ================================================================================ # File - Upload & DataType URL: https://kalamdb.org/docs/server/architecture/file-upload-datatype Source: content/server/architecture/file-upload-datatype.mdx > Understand how FILE upload and read flows map to USER and SHARED tenant storage paths in KalamDB. Covers multipart uploads, FileRef metadata, physical object-store layout, and file retrieval. # File - Upload & DataType KalamDB separates file metadata from file bytes: - SQL rows store a **`FileRef` JSON payload** in `FILE` columns (id, size, sha256, mime, subfolder, original name). - **Binary content is written as a real object** in the configured storage backend — a file on the local filesystem or an object in S3, GCS, or Azure Blob Storage. This is **not** a BLOB-in-the-row model like PostgreSQL `BYTEA`, MySQL `BLOB`, or SQLite `BLOB`. KalamDB never stores file bytes inside RocksDB row values or inside Parquet column pages. Queries return metadata; downloads stream bytes from the storage path the `FileRef` points at. This allows database queries and file transfer to scale independently while preserving table-level security. File storage follows table-type scope. For the full `USER`/`SHARED`/`STREAM` capability matrix, see [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types). ## Upload and Read Flow 1. Client sends SQL using `FILE("placeholder")`. 2. Client attaches multipart file part as `file:`. 3. KalamDB validates auth and table type (`USER` / `SHARED` only). 4. Binary is staged briefly, then **finalized as a standalone object** under the table's storage path (see [Physical storage layout](#physical-storage-layout)). 5. The hot-tier row stores a `FileRef` JSON payload used for future reads/downloads. For endpoint details, see [/docs/server/api/http-reference](https://kalamdb.org/docs/server/api/http-reference). ## Physical storage layout FILE uploads land in the **`base_directory`** of the table's assigned storage (from `system.storages`, default **`local`**). For a typical local dev server that directory is under `storage.data_path`: ```text {data_path}/storage/ # default base_directory for the local storage ├── {namespace}/{tableName}/ # SHARED table root │ └── f0001/1234567890123456789-contract.pdf └── {namespace}/{tableName}/{userId}/ # USER table root (per-user scope) └── f0001/1234567890123456789-profile.md ``` With the default server templates (`shared_tables_template`, `user_tables_template`) and `data_path = "./data"`: | Table type | Template | Example relative path under `storage/` | |------------|----------|--------------------------------------| | `SHARED` | `{namespace}/{tableName}` | `app/documents/f0001/1234567890123456789-contract.pdf` | | `USER` | `{namespace}/{tableName}/{userId}` | `okf_sync/context_files/9876543210987654321/f0001/1234567890123456789-profile.md` | Each upload gets: 1. A **snowflake file id** (used in the on-disk filename). 2. A **subfolder** such as `f0001` (rotates as folders fill; configurable via `max_files_per_folder`). 3. A **stored filename** `{id}-{sanitized-original-name}.{ext}` built from the `FileRef`. The full object key / filesystem path is: ```text {base_directory}/{resolved_template}/{subfolder}/{stored_filename} ``` Remote storages use the same relative layout inside the bucket or container prefix configured in `base_directory` (for example `s3://my-bucket/kalam/`). ### What lives in SQL vs what lives on disk | Layer | Holds | Example | |-------|-------|---------| | RocksDB hot row | `FileRef` JSON in the `FILE` column | `{"id":"123…","sub":"f0001","name":"profile.md","size":142,"mime":"text/markdown","sha256":"a1b2…"}` | | Parquet cold segment (after flush) | Same `FileRef` JSON in the column — **not** the bytes | Metadata only | | Storage object store | **Actual file bytes** | `…/okf_sync/context_files/{userId}/f0001/123…-profile.md` | Clients download through `GET /v1/files/{namespace}/{table}/{sub}/{stored_filename}` (or SDK helpers built on that path). The handler reads the object from storage; it does not decode bytes from a SQL BLOB column. ### End-to-end finalize path (backend) From `kalamdb-filestore` + SQL file handlers: 1. Multipart upload bytes are written to a **temporary staging directory** per request. 2. `finalize_file` generates a `FileRef`, computes sha256/mime, allocates a subfolder, and **`put`s the bytes** to the table's storage backend via the object-store API. 3. The staging directory is removed; only the permanent object and the row's `FileRef` remain. 4. On row delete or FILE replacement, the old object can be garbage-collected separately from MVCC row versions. See the [Live OKF Context Sync](https://kalamdb.org/docs/use-cases/live-okf-context-sync#file-bytes-on-disk-not-in-sql) use case for a concrete walkthrough with `kalam dev` paths you can inspect on disk. ## USER vs SHARED Pathing Model KalamDB routes file bytes according to table type: - **USER tables**: file objects are stored under the authenticated user's tenant scope. - **SHARED tables**: file objects are stored under shared table scope. This matches KalamDB's tenant-aware architecture: - user-private data stays isolated in user-tenant paths - shared assets are accessible through shared-table policy `SYSTEM` and `STREAM` table paths are not valid for file APIs. ### Backend-Verified Path Resolution From backend implementation (`kalamdb-filestore` + API handlers): - For `USER` tables, file operations are resolved with `user_id` context. - For `SHARED` tables, file operations are resolved without `user_id` context. - Download handler enforces this behavior and rejects `STREAM`/`SYSTEM` file access. Default storage templates are: - `shared_tables_template = "{namespace}/{tableName}"` - `user_tables_template = "{namespace}/{tableName}/{userId}"` So for a table `chat.attachments`: - shared object example: `chat/attachments/f0001/1234567890123456789-photo.png` - user object example: `chat/attachments//f0001/1234567890123456789-photo.png` Templates are configurable per storage with `CREATE STORAGE` / `ALTER STORAGE`, but the user-scoped resolution model remains the same. Changing templates affects **new** path resolution; existing objects keep their stored paths via the `FileRef` recorded in each row. ## Architecture Diagram ```mermaid flowchart LR Client[Client] -->|upload request| Api[SQL and File API] Api -->|validate auth and table type| Router{Table Type Router} Router -->|USER table with user_id| UserPath[User Tenant Folder] Router -->|SHARED table without user_id| SharedPath[Shared Table Folder] UserPath --> Ref[Store file reference in row] SharedPath --> Ref UserPath --> ObjStore[(Object store / storage folder)] SharedPath --> ObjStore Ref --> Download[Read via file endpoint] ObjStore --> Download Download --> Client ``` ## Why This Design Helps - Keeps tenant boundaries explicit for uploads and reads. - Avoids mixing user-private files with shared table assets. - Lets applications query metadata with SQL while streaming bytes through file endpoints. - **Object-store friendly:** bytes behave like normal files or S3 keys, so backup, CDN offload, and lifecycle policies apply to file content without rewriting SQL dumps. - **Smaller hot/cold SQL tiers:** Parquet segments and RocksDB hold references and queryable fields, not multi-megabyte blobs per row. ## Related Docs - [/docs/server/sql-reference/file-datatype](https://kalamdb.org/docs/server/sql-reference/file-datatype) - [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types) - [/docs/server/architecture/storage-tiers](https://kalamdb.org/docs/server/architecture/storage-tiers) - [/docs/server/sql-reference/storage-id-usage](https://kalamdb.org/docs/server/sql-reference/storage-id-usage) - [/docs/use-cases/live-okf-context-sync](https://kalamdb.org/docs/use-cases/live-okf-context-sync) — sync worker that uploads Markdown through `FILE` columns; inspect `kalam/server/data/storage/` after `kalam dev` ================================================================================ # Architecture Overview URL: https://kalamdb.org/docs/server/architecture Source: content/server/architecture/index.mdx > Explore KalamDB's architecture: query execution, multi-tier storage (RocksDB/Parquet), realtime delivery, and cluster consensus. # Architecture Overview KalamDB combines SQL execution, realtime delivery, and multi-tier storage in one runtime. It also supports vector search with `EMBEDDING(n)` columns, cosine indexes, and similarity ranking in SQL for semantic retrieval workflows. Use this page as the map. The deeper architecture pages explain one boundary at a time: [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types), [/docs/server/architecture/storage-tiers](https://kalamdb.org/docs/server/architecture/storage-tiers), [/docs/server/architecture/stream-storage](https://kalamdb.org/docs/server/architecture/stream-storage), [/docs/server/architecture/manifests](https://kalamdb.org/docs/server/architecture/manifests), [/docs/server/architecture/live-query](https://kalamdb.org/docs/server/architecture/live-query), [/docs/server/architecture/clustering](https://kalamdb.org/docs/server/architecture/clustering), [/docs/server/architecture/datatypes](https://kalamdb.org/docs/server/architecture/datatypes), [/docs/server/architecture/vector-search](https://kalamdb.org/docs/server/architecture/vector-search), and [/docs/server/architecture/file-upload-datatype](https://kalamdb.org/docs/server/architecture/file-upload-datatype). ## Major Runtime Layers - `kalamdb-api`: HTTP + WebSocket surface (`/v1/api/*`, `/v1/ws`) - `kalamdb-core`: orchestration, SQL execution flow, authorization checks, job dispatch - `kalamdb-sql`: SQL parser/classifier/extensions (`SUBSCRIBE`, `TOPIC`, `STORAGE`, `EXECUTE AS ''`, etc.) - `kalamdb-store` + RocksDB: hot write/read path - `kalamdb-filestore` + Parquet: cold segments and manifests - `kalamdb-raft`: cluster consensus and replication ## Runtime Flow ```mermaid flowchart TD A["Client (HTTP / WebSocket)"] --> B["kalamdb-api"] B --> C["kalamdb-core orchestration"] C --> D["kalamdb-sql parser/classifier"] D --> E["DataFusion execution"] E --> F["Hot Tier (RocksDB)"] F --> G["Flush Jobs"] G --> H["Cold Tier (Parquet + manifest)"] C --> I["Realtime notifications"] C --> J["kalamdb-raft (cluster mode)"] ``` ## Architecture Reading Map ```mermaid flowchart TD A["Start: Architecture Overview"] --> B["Table Types"] B --> C["USER / SHARED / STREAM scope"] C --> D["Storage Tiers"] D --> E["Manifests"] C --> F["Stream Storage"] C --> G["Live Query"] G --> H["Clustering & HA"] C --> I["FILE Upload & DataType"] C --> J["Data Types"] J --> K["Vector Search"] ``` Read [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types) first when you are deciding data ownership or security. Read [/docs/server/architecture/storage-tiers](https://kalamdb.org/docs/server/architecture/storage-tiers) and [/docs/server/architecture/manifests](https://kalamdb.org/docs/server/architecture/manifests) when you are tuning flush, compaction, or storage templates. Read [/docs/server/architecture/stream-storage](https://kalamdb.org/docs/server/architecture/stream-storage) when you are tuning `STREAM` table retention, transient UI state, or TTL cleanup behavior. Read [/docs/server/architecture/live-query](https://kalamdb.org/docs/server/architecture/live-query) and [/docs/server/architecture/clustering](https://kalamdb.org/docs/server/architecture/clustering) when you are scaling realtime connections or multi-node deployments. ## Data Directory Layout Default paths are rooted at `storage.data_path` (usually `./data`): ```text data/ ├── rocksdb/ # hot tier ├── storage/ # cold parquet tier ├── snapshots/ # raft snapshots └── streams/ # stream table logs ``` Table-specific cold paths are generated from templates: - shared tables: `storage.shared_tables_template` - user tables: `storage.user_tables_template` ## Query Path (Simplified) 1. API receives SQL (`POST /v1/api/sql`) with bearer auth. 2. SQL is parsed/classified (standard + KalamDB extensions). 3. Authorization and table-type rules are applied. 4. Query executes against hot tier, cold tier, or both. 5. Response returns normalized JSON schema/rows. 6. Optional change notifications flow to subscriptions/topics. For detailed flush behavior and cold-tier movement, see [/docs/server/architecture/storage-tiers](https://kalamdb.org/docs/server/architecture/storage-tiers). For the file-backed `STREAM` table path, see [/docs/server/architecture/stream-storage](https://kalamdb.org/docs/server/architecture/stream-storage). ## Technology Stack | Component | Technology | Notes | |-----------|-----------|-------| | Language | Rust 1.92+ | concurrency + memory safety | | Query engine | Apache DataFusion 54.0 | SQL planning/execution (DuckDB dialect for lambdas and JSON operators) | | Columnar format | Apache Arrow 58.x | in-memory batches | | Cold storage | Apache Parquet 58.x | compressed columnar files | | Hot storage | RocksDB 0.24 | write-heavy low-latency path | | API runtime | Actix Web 4.12 | HTTP + WebSocket | | Auth | bcrypt + JWT | password + token flows | ## Continue Reading - [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types) - [/docs/server/architecture/storage-tiers](https://kalamdb.org/docs/server/architecture/storage-tiers) - [/docs/server/architecture/stream-storage](https://kalamdb.org/docs/server/architecture/stream-storage) - [/docs/server/architecture/manifests](https://kalamdb.org/docs/server/architecture/manifests) - [/docs/server/architecture/live-query](https://kalamdb.org/docs/server/architecture/live-query) - [/docs/server/architecture/clustering](https://kalamdb.org/docs/server/architecture/clustering) - [/docs/server/architecture/datatypes](https://kalamdb.org/docs/server/architecture/datatypes) - [/docs/server/architecture/vector-search](https://kalamdb.org/docs/server/architecture/vector-search) - [/docs/server/architecture/file-upload-datatype](https://kalamdb.org/docs/server/architecture/file-upload-datatype) ================================================================================ # Live Query URL: https://kalamdb.org/docs/server/architecture/live-query Source: content/server/architecture/live-query.mdx > How KalamDB achieves high-performance real-time subscriptions and offline-first sync using Sequence IDs and tenant-aware routing. # Live Query Architecture KalamDB is designed from the ground up to support real-time, reactive applications. Unlike traditional databases that require complex polling mechanisms or external message brokers (like Kafka or Redis) to stream changes, KalamDB natively supports **Live Queries** via WebSockets. This architecture allows clients to subscribe to changes on `USER`, `SHARED`, and `STREAM` tables and receive instant push notifications whenever data is inserted, updated, or deleted. Live queries inherit the table-type access and scope rules from [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types). `USER` and `STREAM` subscriptions are user-scoped; `SHARED` subscriptions use the shared table's `ACCESS_LEVEL` policy. ## How It Works When a client subscribes to a query (e.g., `SELECT * FROM chat.messages WHERE conversation_id = 123`), KalamDB does not just execute the query once. It registers a **Live Query Subscription** in the cluster. You can subscribe in two common ways: - **Whole-row subscription**: `SELECT * FROM chat.changes WHERE ...` - **Projection subscription**: `SELECT username, status FROM chat.changes WHERE ...` Projection subscriptions are useful when the client needs only a few columns, which reduces payload size and improves UI update performance. ### Tenant-Aware Routing for Performance One of the main reasons KalamDB achieves better performance than other databases for real-time workloads is its **tenant-aware storage architecture**. In traditional databases, a live query often requires the system to scan or index the entire table or maintain complex global replication logs to find relevant changes. In KalamDB, `USER` and `STREAM` tables are physically sharded by `user_id`. When a live query is registered for a specific user, the system doesn't need to scan the whole table. It directly monitors the isolated storage partition for that specific user tenant. This drastically reduces I/O overhead and allows the system to scale horizontally to millions of concurrent connections. In clustered mode, the client may be connected to a follower node while writes are accepted by the leader. KalamDB forwards those changes through Raft replication so the follower applies them locally, evaluates matching subscriptions, and pushes updates to the connected client. ```mermaid flowchart LR Client[Client App] -->|subscribe| Follower[Follower Node] Follower -->|register subscription| Leader[Leader Node] subgraph Cluster Leader -->|replicate subscription metadata| Follower Write[Write Operation] --> Leader Leader --> Router{Tenant Router} Router --> UserStore[User Tenant Storage] Router --> SharedStore[Shared Storage] UserStore --> Matcher[Query Matcher] SharedStore --> Matcher Matcher --> Leader Leader -->|replicate data change| Follower end Follower -->|local notify + push update| Client ``` ## Offline-First and Sequence IDs KalamDB's live queries are built to support **offline-first** applications seamlessly. When subscribing to a table, you don't just get future events. You can also request historical data to synchronize your local state before the live stream begins. This is powered by **Sequence IDs** (`SeqId`), which provide a strict, monotonically increasing order of operations. When initiating a subscription, you can specify: - **`since_seq`**: Pull all changes that happened after a specific Sequence ID. - **`fetch_last`**: Load only the latest *N* number of rows. ### Example: Chat Application Imagine a user opening an old conversation in a chat app. Loading the entire history of the conversation would be slow and consume unnecessary bandwidth. Instead, the client can subscribe to the conversation and request only the **latest 10 messages**: 1. The client sends a subscription request with `fetch_last: 10`. 2. KalamDB immediately returns an `initial_data_batch` containing those 10 messages. 3. The subscription remains open. 4. As new messages are sent in that conversation, KalamDB pushes them to the client in real-time. If the user goes offline and reconnects later, the client can simply resubscribe using the `since_seq` of the last message it received. KalamDB will instantly push any messages missed during the offline period, followed by a seamless transition back to the live stream. ## Cluster-Wide Visibility Active live query subscriptions are node-local runtime state. Each node exposes its current in-memory subscriptions through `system.live`; that view is not persisted to RocksDB and it is not replicated through Raft. Clients can stay connected to follower nodes while writes are processed by the leader. The change stream is still delivered to followers, so followers can fan out updates to their connected clients. This helps increase total concurrent connection capacity because client sessions are distributed across nodes. ## Shared Table Subscriptions at High Scale KalamDB has a dedicated shared-table streaming notification path optimized for high subscriber fan-out. For `SHARED` tables, the runtime keeps a dedicated table-level subscription index so it can resolve shared subscribers quickly without user-partition lookup. When a shared-table change arrives, KalamDB uses a streaming fan-out pipeline: 1. **Pre-compute row JSON once** and share it across workers. 2. **Snapshot subscriber handles** for the target shared table. 3. **Process subscribers in parallel chunks** (chunked dispatch) while still applying per-subscriber filter, projection, flow control, and channel delivery semantics. Why this enables high subscriber counts: - avoids repeating full row conversion for every subscriber - uses shared payload memory for lower CPU and allocation overhead - parallelizes fan-out while preserving subscription-specific behavior - keeps backpressure behavior intact with flow control and non-blocking send paths This is a core part of KalamDB's realtime power for `SHARED` tables, where a single change may need to reach very large subscriber groups with low latency. ## Table-Type Delivery Summary | Table type | Subscription scope | Access policy used | Delivery shape | |---|---|---|---| | `USER` | Effective `user_id` partition | User-table read gate | Tenant-local matching and fan-out | | `SHARED` | Whole shared table scope | Shared `ACCESS_LEVEL` read gate | Shared-table index plus chunked fan-out | | `STREAM` | Effective `user_id` stream partition | User/stream read gate | TTL-oriented transient updates | | `SYSTEM` | Not supported for app live query | DBA/System-only metadata access | No normal live-query stream | ```mermaid sequenceDiagram participant C as Client participant F as Follower Node participant L as Leader Node participant S as Tenant Storage C->>F: WebSocket live subscribe F->>L: Register subscription metadata L-->>F: Subscription replicated L->>S: Apply write (insert/update/delete) S-->>L: Change with SeqId L-->>F: Replicate change event F-->>C: Push live update ``` ## Related Docs - [/docs/server/sql-reference/subscriptions](https://kalamdb.org/docs/server/sql-reference/subscriptions) - [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types) - [/docs/server/architecture/clustering](https://kalamdb.org/docs/server/architecture/clustering) - [/docs/ts-sdk/subscriptions](https://kalamdb.org/docs/ts-sdk/subscriptions) ================================================================================ # Manifests URL: https://kalamdb.org/docs/server/architecture/manifests Source: content/server/architecture/manifests.mdx > How KalamDB tracks cold Parquet segments across memory, RocksDB, and storage manifest.json files, including tail compaction semantics. # Manifests KalamDB uses a manifest as the authoritative index for cold Parquet segments. Every manifest is owned by the manifest service, which decides where the current copy lives, when it is dirty, and when storage `manifest.json` is rewritten. Manifests exist behind the hot/cold flow described in [/docs/server/architecture/storage-tiers](https://kalamdb.org/docs/server/architecture/storage-tiers). They apply to `USER` and `SHARED` cold-storage scopes; see [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types) for the table-type boundary. ## Manifest Tiers | Table type | Hot manifest path | Cold manifest path | |------------|-------------------|--------------------| | `SHARED` | process memory -> `system.manifest` in RocksDB | `manifest.json` in table storage | | `USER` | `system.manifest` in RocksDB | `manifest.json` in the user storage path | This split is intentional: - `SHARED` manifests stay in process memory because they are low-cardinality and widely reused. - `USER` manifests stay out of process memory so millions of user-scoped manifests do not expand server RSS. - Both table types still persist their durable hot copy in RocksDB before storage writes. The canonical read path is `ManifestService::get_or_load()`: 1. check the fastest hot tier for the scope 2. fall back to RocksDB manifest copy 3. fall back to storage `manifest.json` 4. hydrate faster tiers above the answering layer ## Control Flow ```mermaid flowchart TD A["SQL / flush / metadata change"] --> B["ManifestService"] subgraph Shared["SHARED table"] B --> S1["In-memory manifest cache"] S1 <--> S2["RocksDB system.manifest row"] S2 -->|flush or explicit persist| S3["storage manifest.json"] S3 -->|cache miss reload| S2 S2 -->|shared hot reload| S1 end subgraph User["USER table"] B --> U1["RocksDB system.manifest row"] U1 -->|flush or explicit persist| U2["user storage manifest.json"] U2 -->|cache miss reload| U1 end ``` ## What Happens On Normal Writes For normal DML and flush-preparation updates, KalamDB does not rewrite `manifest.json` on every change. 1. The write path updates hot data in RocksDB. 2. The manifest service updates manifest metadata in the hot tier. 3. The manifest is marked `pending_write`. 4. Cold storage is updated later by the flush path. That means manifest bookkeeping stays cheap on the write path while still keeping the latest authoritative manifest state available for reads, flush planning, and primary-key checks. The hot manifest entry also tracks a sync state: - `in_sync` - `pending_write` - `syncing` - `stale` - `error` During a flush, the scope becomes `syncing` while the Parquet temp file is being written. ## When `manifest.json` Is Actually Written `manifest.json` is persisted when KalamDB commits cold-storage state, not on every mutation. - Automatic or manual flushes write Parquet, update manifest segment metadata, and then persist `manifest.json`. - Reloads after cache misses can hydrate the hot tier from the stored manifest. - Vector-index DDL metadata is a deliberate exception: it is persisted immediately as a control-plane metadata write so vector state is not lost when there is no row flush to trigger storage persistence. Flush commits a new segment through one canonical path: append the segment metadata, write `manifest.json`, then refresh RocksDB and shared-scope memory cache. ## Flush-Time Segment Append For a normal cold append, KalamDB writes `batch-N.parquet.tmp`, renames it to `batch-N.parquet`, then appends a new segment entry that includes: - `min_seq` and `max_seq` - `row_count` - `size_bytes` - `schema_version` - `status` - `column_stats` for indexed columns `last_sequence_number` tracks the latest `batch-N` slot used for future flush naming. ## How Manifest Compaction Works Post-flush small-segment compaction does not rebuild the whole manifest. It compactly rewrites only the trailing small-file suffix. The compactor: 1. selects the trailing run of small, committed, same-schema segments 2. rewrites the newest MVCC winners into `compact-.parquet` 3. reacquires the manifest scope lock 4. verifies the selected inputs are still the current trailing suffix 5. truncates that suffix and appends the compacted replacement segment, or appends nothing if the suffix was fully pruned 6. persists the updated `manifest.json` If another flush changed the manifest tail while compaction was running, the swap is skipped and the compacted file is deleted. Compaction filenames do not use the `batch-N` numbering scheme, so they do not consume a new batch sequence slot. ## What A Real `manifest.json` Looks Like The example below is taken from a real user-table manifest written by the KalamDB backend. The values vary by table and flush, but the field names and nesting match the on-disk JSON shape. ```json { "table_id": "flush_manifest_ns_mpczl3q7_s3k_0.user_flush_test_mpczl3q7_s3k_0", "user_id": "admin", "version": 2, "created_at": 1779216537052, "updated_at": 1779216537063, "segments": [ { "min_seq": 315199164996489218, "max_seq": 315199165030043648, "row_count": 20, "size_bytes": 2711, "created_at": 1779216537063, "id": "batch-0.parquet", "path": "batch-0.parquet", "column_stats": { "1": { "min": { "Int64": "315199164994551810" }, "max": { "Int64": "315199165028106240" }, "null_count": 0 } }, "schema_version": 1, "status": "committed" } ], "last_sequence_number": 0, "files": null, "vector_indexes": {} } ``` What these fields mean in practice: - `segments` is the durable list of cold Parquet batches for that exact table scope. - `column_stats` is keyed by stable column ID, not by column name, so renames do not invalidate old segment metadata. - `last_sequence_number` tracks the last `batch-N.parquet` slot used for segment file naming. - `files` stays `null` until the table enables FILE-column subfolder tracking. - `vector_indexes` stays empty until the table has vector index metadata to persist. ## Why Primary-Key Checks Use The Manifest Service Primary-key existence and cold-segment pruning go through the manifest service instead of reopening `manifest.json` on every check. That gives KalamDB one source of truth for: - the freshest shared-table manifest already in memory - the freshest user-table manifest already in RocksDB - fallback reload from storage only when the hot tier does not have the manifest yet This avoids repeated storage reads and keeps pruning decisions aligned with the same manifest state the flush path updates. It also means manifest-aware reads see the same suffix-replacement result after compaction that the flush and compaction jobs committed. ## Shared vs User Storage Layout A `SHARED` table writes one manifest per table path: ```text storage//
/manifest.json ``` A `USER` table writes one manifest per user-scoped table path: ```text storage//
//manifest.json ``` Each manifest tracks the Parquet segments for that exact scope only. ## Related Docs - [/docs/server/architecture/storage-tiers](https://kalamdb.org/docs/server/architecture/storage-tiers) - [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types) - [/docs/server/sql-reference/storage](https://kalamdb.org/docs/server/sql-reference/storage) - [/docs/server/sql-reference/storage-id-usage](https://kalamdb.org/docs/server/sql-reference/storage-id-usage) ================================================================================ # Storage Tiers URL: https://kalamdb.org/docs/server/architecture/storage-tiers Source: content/server/architecture/storage-tiers.mdx > Understand KalamDB's hot RocksDB tier, cold Parquet tier, manifest state, and the current post-flush tail-compaction path. # Storage Tiers KalamDB uses a dual-tier storage architecture that keeps the write path hot in RocksDB while moving queryable historical state into immutable Parquet segments. This page describes how `USER` and `SHARED` tables move from hot to cold storage. For the table-type rules that decide whether a table can flush at all, see [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types). ## Hot Tier (RocksDB) The hot tier handles incoming writes in RocksDB-backed table stores. **Characteristics:** - ⚡ Sub-millisecond write latency - Optimized for point lookups, recent writes, and write-heavy workloads - Holds the newest live rows and the newest delete tombstones - Feeds the flush scheduler through manifest scopes marked `pending_write` ## Cold Tier (Parquet) Flushed data is written to Apache Parquet files for efficient scans, pruning, and long-term storage. **Characteristics:** - 📊 Columnar format for efficient analytics - High compression ratios - Each segment tracked in `manifest.json` and a hot manifest copy - Supports multiple storage backends (local, S3, Azure, GCS) ### Parquet Compression Table `COMPRESSION` is a cold-tier Parquet setting for `USER` and `SHARED` tables. KalamDB currently supports three values: | Value | Meaning | Typical use | |-------|---------|-------------| | `none` | Writes uncompressed Parquet pages. | Debugging, CPU-constrained flushes, or data already compressed at the value level | | `snappy` | Fast Parquet compression and decompression with low CPU overhead. This is the default. | General workloads and latency-sensitive flushes | | `zstd` | Zstandard Parquet compression at level 1 for better storage density with modest extra CPU. | Larger historical datasets or storage-cost-sensitive tables | The selected codec is applied when `USER` and `SHARED` table scopes write `batch-N.parquet.tmp` and when tail compaction writes `compact-.parquet.tmp`. After the atomic rename, readers discover the codec from the Parquet footer, so no manifest-side decompression setting is needed. This setting does not affect RocksDB hot storage, WebSocket gzip, backups, or stream table log files. `STREAM` tables do not accept table Parquet compression because current stream storage does not flush to Parquet cold segments. ## End-to-End Tier Flow ```mermaid flowchart LR A["INSERT / UPDATE / DELETE"] --> B["Hot write in RocksDB"] B --> C["Manifest marked as pending_write"] C --> D["STORAGE FLUSH job"] D --> E["Resolve latest hot versions per scope"] E --> F["Write batch-N.parquet.tmp"] F --> G["Atomic rename to batch-N.parquet"] G --> H["Persist manifest.json + hot manifest copy"] H --> I["Delete flushed hot rows"] I --> J["Optional post-flush small-segment compaction"] J --> K["Reads use hot + cold paths"] ``` ## Flush Policy Tables are configured with a flush policy that determines when data moves from hot to cold tier: ```sql CREATE TABLE app.messages ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), content TEXT NOT NULL, created_at TIMESTAMP DEFAULT NOW() ) WITH ( TYPE = 'USER', FLUSH_POLICY = 'rows:1000,interval:60' ); ``` | Policy | Description | |--------|-------------| | `rows:N` | Flush after N rows accumulated | | `interval:N` | Flush every N seconds | | `rows:N,interval:N` | Flush on whichever threshold is hit first | ## How Flushing Works (Current Engine Path) The flush flow is job-driven and crash-safe: 1. DML writes land in RocksDB first. 2. The manifest scope is marked `pending_write`. 3. `STORAGE FLUSH TABLE` or `STORAGE FLUSH ALL` creates background flush jobs in `system.jobs`. 4. The flush executor resolves the target scope and scans hot rows in bounded batches. 5. The latest `_seq` wins per primary key. Latest tombstones stay hot instead of being written to Parquet. 6. `FlushScopeWriter` writes `batch-N.parquet.tmp`. 7. The temp file is atomically renamed to `batch-N.parquet`. 8. Manifest metadata for the new segment is persisted through the manifest service. 9. Flushed hot rows are deleted from RocksDB in bounded cleanup batches. Notes: - `STREAM` and `SYSTEM` tables are not part of this flush-to-Parquet path. - `STORAGE FLUSH` is asynchronous; monitor progress via `system.jobs`. - Shared scopes flush once per table scope. User scopes flush once per `(table, user)` scope. ## Flush State Machine ```mermaid stateDiagram-v2 [*] --> InSync InSync --> PendingWrite: DML write PendingWrite --> Syncing: flush starts Syncing --> InSync: parquet + manifest committed Syncing --> Error: flush failure Error --> PendingWrite: retry / next flush ``` ## Post-Flush Small-Segment Compaction KalamDB now has an optional post-flush tail-compaction path under `[flush.compaction]`: ```toml [flush.compaction] enabled = false min_eligible_segments = 5 max_segments_per_run = 8 user_max_segment_rows = 10000 shared_max_segment_rows = 25000 ``` This path is separate from the flush critical section. After a successful flush, the leader may enqueue a `segment_compact` job for the scopes that actually wrote Parquet files. The compactor: 1. walks the manifest from newest to oldest 2. selects only the trailing run of small, committed, same-schema segments 3. stops when it hits an already-large segment, a non-readable segment, or a schema-version boundary 4. rewrites only the latest MVCC winners into `compact-.parquet` 5. swaps the manifest tail only if the selected suffix is still unchanged If a newer flush changed the manifest tail while compaction was running, the compacted output is discarded and the existing manifest remains authoritative. Only `USER` and `SHARED` tables participate in this mechanism. ## Manual Flush & Compaction ```sql -- Flush a specific table STORAGE FLUSH TABLE myapp.messages; -- Flush all tables in a namespace STORAGE FLUSH ALL IN myapp; -- Compact cold storage STORAGE COMPACT TABLE myapp.messages; -- Check storage health STORAGE CHECK local EXTENDED; ``` ## S3 User-Data Example Create an S3 storage and bind a `USER` table to it: For MinIO-compatible local S3 setup, see [/docs/server/integrations/minio](https://kalamdb.org/docs/server/integrations/minio). ```sql CREATE STORAGE s3_prod TYPE 's3' BUCKET 'my-kalamdb-prod' REGION 'us-east-1' USER_TABLES_TEMPLATE 'users/{namespace}/{tableName}/{userId}' SHARED_TABLES_TEMPLATE 'shared/{namespace}/{tableName}'; CREATE TABLE chat.messages ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), conversation_id BIGINT NOT NULL, content TEXT NOT NULL, created_at TIMESTAMP DEFAULT NOW() ) WITH ( TYPE = 'USER', STORAGE_ID = 's3_prod', FLUSH_POLICY = 'rows:1000,interval:60' ); ``` For user `u_42`, cold-tier objects are stored under the user template: ```text s3://my-kalamdb-prod/users/chat/messages/u_42/manifest.json s3://my-kalamdb-prod/users/chat/messages/u_42/batch-0.parquet s3://my-kalamdb-prod/users/chat/messages/u_42/batch-1.parquet ``` Illustrative manifest excerpt: ```json { "segments": [ { "id": "batch-1.parquet", "path": "batch-1.parquet", "row_count": 1000, "size_bytes": 184320, "schema_version": 1, "status": "committed" } ], "last_sequence_number": 1 } ``` ## Per-User Storage Isolation ```text data/storage/ ├── //manifest.json # shared table path template ├── //batch-.parquet └── ///manifest.json # user table path template ``` Path layout is controlled by: - `storage.shared_tables_template` (default: `{namespace}/{tableName}`) - `storage.user_tables_template` (default: `{namespace}/{tableName}/{userId}`) Each user's cold-tier data can still live in a separate directory. This enables: - **Trivial data export** — just copy the user's directory - **Instant deletion** — remove the directory for GDPR compliance - **Independent scaling** — no cross-user interference For the manifest-layer details behind this flow, see [/docs/server/architecture/manifests](https://kalamdb.org/docs/server/architecture/manifests). ## Related Docs - [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types) - [/docs/server/architecture/manifests](https://kalamdb.org/docs/server/architecture/manifests) - [/docs/server/sql-reference/storage](https://kalamdb.org/docs/server/sql-reference/storage) - [/docs/server/sql-reference/storage-id-usage](https://kalamdb.org/docs/server/sql-reference/storage-id-usage) - [/docs/server/integrations/minio](https://kalamdb.org/docs/server/integrations/minio) ================================================================================ # Stream Storage URL: https://kalamdb.org/docs/server/architecture/stream-storage Source: content/server/architecture/stream-storage.mdx > Learn how KalamDB stores STREAM table rows in TTL-sized window files, how eviction deletes expired directories, and why stream reads stay user-scoped. # Stream Storage Architecture KalamDB stream storage is the runtime path behind `STREAM` tables. It is optimized for short-lived, user-scoped rows such as presence, typing indicators, cursor positions, and transient events. Unlike `USER` and `SHARED` tables, stream rows do not flush into Parquet or participate in the manifest-driven cold tier. The engine keeps stream data in append-only log files sized for fast TTL eviction. For the SQL creation model and table-type rules, see [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types). ## Stream Storage File Layout With the default data directory, each stream table gets a table-scoped base path under: ```text data/streams//
``` Inside that base path, rows are written into TTL-sized window directories: ```text data/streams//
/w-//.log ``` Example: ```text data/streams/chat/typing_events/ ├── w1748343000000-60000/ │ ├── shard-0/alice.log │ └── shard-2/bob.log └── w1748343060000-60000/ └── shard-0/alice.log ``` The layout is intentionally simple: - one immutable time window directory per retention bucket - one shard subdirectory to distribute active writers - one `.log` file per user inside that shard/window KalamDB uses `.log` directly instead of `/records.log` because the validated `user_id` alphabet is already filename-safe, so the extra directory level would add metadata churn without adding useful structure. ## Why the Window Name Encodes Time Each window directory name encodes two values: - `window_start_ms` - `duration_ms` That lets cleanup decide whether a whole window is expired without opening any log file. If `window_end <= ttl_cutoff`, the entire directory can be removed in one operation. This avoids a row scan during normal retention and keeps cleanup proportional to the number of expired windows, not the number of rows. ## Bucket Sizing From `TTL_SECONDS` The bucket size is derived from the table's `TTL_SECONDS` value: | `TTL_SECONDS` | Window size | |---|---| | `<= 15 minutes` | minute windows | | `<= 1 day` | hour windows | | `<= 1 week` | day windows | | `<= 30 days` | week windows | | `> 30 days` | month windows | Short-lived streams use minute windows so eviction stays close to the actual retention period. Longer-lived streams use coarser windows to avoid creating too many tiny files. ## Write Path When a row is inserted or deleted in a `STREAM` table: 1. the effective `user_id` selects the per-user stream scope 2. the row timestamp selects the current time window 3. the user is routed into a shard directory 4. the record is appended to `.log` as a length-prefixed flexbuffers frame The file-backed stream store keeps a bounded writer cache: - 64 KiB buffered writes per open segment - up to 256 cached segment writers per store - cold writers flushed and closed when the cache exceeds the cap The stream log does not call `fsync` on every append. Stream tables are intended for ephemeral state, so the design favors low write latency and relies on the OS page cache for writeback. ## Stream Storage Cleanup TTL eviction is window-based, not row-by-row. The stream eviction job computes a cutoff timestamp and then: 1. scans window directory names under the table's stream base path 2. identifies windows whose computed `window_end` is older than the cutoff 3. closes any cached writers under those directories 4. removes the expired window directories ```mermaid flowchart LR A["STREAM write"] --> B["Pick TTL-sized window"] B --> C["Route by shard + user_id"] C --> D["Append to .log"] E["Eviction job"] --> F["Read window directory names"] F --> G{"window_end <= cutoff?"} G -- "yes" --> H["Close cached writers"] H --> I["remove_dir_all(window)"] G -- "no" --> J["Keep active window"] ``` This is why the time-window layout matters: cleanup is mostly directory unlink work instead of file parsing or per-row filtering. The pre-validation path also benefits from this layout. `has_logs_before()` can stop as soon as it finds one expired window directory, and the job executor runs that filesystem work on Tokio's blocking pool so async workers are not pinned by directory traversal or removal. ## Read Path Reads remain user-scoped. To serve a range or latest query, the stream store: 1. computes the shard for the target `user_id` 2. lists only that user's candidate window files 3. filters windows by overlap with the requested time range 4. reads only those candidate log files 5. applies delete records so latest state wins for each stream row ID This keeps reads narrow even when many users share the same stream table. ## What Stream Storage Is Not Stream storage is deliberately separate from the Parquet cold tier used by `USER` and `SHARED` tables. - no Parquet flush path - no manifest-driven segment lifecycle - no long-term analytic storage guarantees - no file-column support If you need durable historical records or analytics, use a `USER` or `SHARED` table and the cold storage flow documented in [/docs/server/architecture/storage-tiers](https://kalamdb.org/docs/server/architecture/storage-tiers). If you need short-lived realtime state that should expire automatically, `STREAM` storage is the intended path. ## Related Docs - [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types) - [/docs/server/architecture/storage-tiers](https://kalamdb.org/docs/server/architecture/storage-tiers) - [/docs/server/architecture/live-query](https://kalamdb.org/docs/server/architecture/live-query) - [/docs/server/topic-pubsub](https://kalamdb.org/docs/server/topic-pubsub) - [/docs/server/sql-reference/tables](https://kalamdb.org/docs/server/sql-reference/tables) ================================================================================ # Table Types URL: https://kalamdb.org/docs/server/architecture/table-types Source: content/server/architecture/table-types.mdx > When to use USER, SHARED, STREAM, and SYSTEM table types in KalamDB. Covers isolation behavior, access rules, and backend execution semantics. # Table Types KalamDB uses four runtime table types. The table type decides data scope, write path, storage layout, live-query routing, and which role checks run before the server exposes rows. - `USER` - `SHARED` - `STREAM` - `SYSTEM` (internal) ## Table-Type Routing Model ```mermaid flowchart LR A["CREATE TABLE / runtime lookup"] --> B{"Table type"} B -- "TYPE = 'USER'" --> C["USER scope"] B -- "TYPE = 'SHARED'" --> D["Shared table scope"] B -- "TYPE = 'STREAM'" --> E["Per-user stream scope"] B -- "Engine-created" --> F["SYSTEM scope"] C --> C1["Effective user_id selects tenant partition"] C1 --> C2["Hot RocksDB + optional Parquet flush"] D --> D1["ACCESS_LEVEL policy controls read/write roles"] D1 --> D2["One shared table scope + optional Parquet flush"] E --> E1["Effective user_id selects stream partition"] E1 --> E2["TTL eviction, no Parquet flush path"] F --> F1["Metadata and operational views"] F1 --> F2["DBA/System-only access"] ``` ## Quick Selection | Choose | When the data is | Typical examples | |---|---|---| | `USER` | Private to the authenticated or effective user | chat messages, agent memory, preferences, tenant records | | `SHARED` | Global within the namespace and governed by table policy | feature flags, catalogs, plans, public reference data | | `STREAM` | Short-lived and user-scoped | typing indicators, presence, cursor positions, transient events | | `SYSTEM` | Engine metadata or runtime state | users, schemas, jobs, cluster views, transactions | Application SQL can create `USER`, `SHARED`, and `STREAM` tables. `SYSTEM` tables are created and evolved by the server. For SQL syntax, see [/docs/server/sql-reference/tables](https://kalamdb.org/docs/server/sql-reference/tables). ## SQL Creation Patterns Recommended unified syntax: ```sql CREATE TABLE app.messages ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), content TEXT NOT NULL, created_at TIMESTAMP DEFAULT NOW() ) WITH (TYPE = 'USER'); ``` Also supported for compatibility: - `CREATE USER TABLE ...` - `CREATE SHARED TABLE ...` - `CREATE STREAM TABLE ...` If neither a typed prefix nor `WITH (TYPE = ...)` is supplied, the parser currently defaults to a `SHARED` table. Prefer setting `TYPE` explicitly in new application SQL so the data boundary is obvious in reviews. ## USER Tables Per-user logical isolation with user-aware routing and permissions. ```sql CREATE TABLE chat.messages ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), conversation_id BIGINT NOT NULL, content TEXT NOT NULL, created_at TIMESTAMP DEFAULT NOW() ) WITH ( TYPE = 'USER', FLUSH_POLICY = 'rows:1000,interval:60', STORAGE_ID = 'local' ); ``` Use for: conversations, preferences, private records, tenant data. ### USER Table Behavior - Rows are scoped by the authenticated or effective `user_id` from the session. - Reads do not implicitly cross user boundaries, even for `system`, `dba`, or `service` roles. - Cross-user work must use an authorized impersonation flow such as `EXECUTE AS USER`. - `USER` tables require a primary key. - `STORAGE_ID`, `USE_USER_STORAGE`, and `FLUSH_POLICY` apply to this table type. - Hot rows can flush into cold Parquet segments. The flush scope is `(table, user)`. - `FILE` columns and file APIs are supported. - Live queries are evaluated in the user-scoped partition. ### USER Role Access | Operation | Anonymous | User | Service | DBA | System | |---|---:|---:|---:|---:|---:| | Read own/effective `USER` scope | No | Yes | Yes | Yes | Yes | | Insert/update/delete own/effective `USER` scope | No | Yes | Yes | Yes | Yes | | Create `USER` table | No | No | Yes | Yes | Yes | | Alter `USER` table | No | No | Yes | Yes | Yes | | Drop `USER` table | No | No | No | Yes | Yes | ## SHARED Tables Global shared scope for all users. ```sql CREATE TABLE app.config ( key TEXT PRIMARY KEY, value TEXT NOT NULL, updated_at TIMESTAMP DEFAULT NOW() ) WITH ( TYPE = 'SHARED', ACCESS_LEVEL = 'PRIVATE' ); ``` Use for: reference data, global settings, shared catalogs. ### SHARED Table Behavior - Rows are not partitioned by `user_id`. - Access is controlled by the table's `ACCESS_LEVEL` option. - If `ACCESS_LEVEL` is omitted, the server stores `PRIVATE`. - `SHARED` tables require a primary key. - `STORAGE_ID`, `FLUSH_POLICY`, and `ACCESS_LEVEL` apply to this table type. - `USE_USER_STORAGE` is rejected because shared data has no user-specific storage scope. - Hot rows can flush into cold Parquet segments. The flush scope is the shared table scope. - `FILE` columns and file APIs are supported. - Live queries use a shared-table subscription path optimized for large fan-out. ### ACCESS_LEVEL Semantics The SQL parser accepts `PUBLIC`, `PRIVATE`, `RESTRICTED`, and `DBA` for `ACCESS_LEVEL` on shared tables. Values are case-insensitive. `PRIVATE` and `RESTRICTED` currently enforce the same runtime role matrix; keep `RESTRICTED` for data you want to label as elevated service/admin-only access. ```mermaid flowchart TD A["Request touches SHARED table"] --> B["Load table definition"] B --> C["Read ACCESS_LEVEL from table options"] C --> D{"Operation"} D -- "SELECT / live initial read" --> E["can_access_shared_table"] D -- "INSERT / UPDATE / DELETE" --> F["can_write_shared_table"] E --> G{"Role allowed?"} F --> G G -- "yes" --> H["Continue execution"] G -- "no" --> I["Access denied"] ``` | `ACCESS_LEVEL` | Read: Anonymous | Read: User | Read: Service | Read: DBA | Read: System | Write: User | Write: Service | Write: DBA | Write: System | |---|---:|---:|---:|---:|---:|---:|---:|---:|---:| | `PUBLIC` | Yes | Yes | Yes | Yes | Yes | No | Yes | Yes | Yes | | `PRIVATE` | No | No | Yes | Yes | Yes | No | Yes | Yes | Yes | | `RESTRICTED` | No | No | Yes | Yes | Yes | No | Yes | Yes | Yes | | `DBA` | No | No | No | Yes | Yes | No | No | Yes | Yes | Notes: - `PUBLIC` means readable by every session role, including anonymous session context, but regular `user` role sessions still cannot write. - `PRIVATE` is the default and is service/admin read-write. - `DBA` is the strictest shared-table option and excludes `service` roles. - DDL still has its own gate: `service`, `dba`, and `system` can create or alter application tables; regular `user` sessions cannot create, alter, or drop tables. ### Changing Shared Access Two ALTER forms are supported: ```sql ALTER TABLE app.config SET TBLPROPERTIES (ACCESS_LEVEL = 'PUBLIC'); ALTER TABLE app.config SET ACCESS LEVEL public; ``` The `SET TBLPROPERTIES` form accepts `PUBLIC`, `PRIVATE`, `RESTRICTED`, and `DBA`. The shorthand `SET ACCESS LEVEL ...` form currently accepts `PUBLIC`, `PRIVATE`, and `RESTRICTED`. ## STREAM Tables Ephemeral rows with TTL-based eviction. Like `USER` tables, `STREAM` tables are scoped per user tenant and are **not** shared across all users. ```sql CREATE TABLE chat.typing_events ( event_id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), user_id TEXT NOT NULL, event_type TEXT NOT NULL, created_at TIMESTAMP DEFAULT NOW() ) WITH ( TYPE = 'STREAM', TTL_SECONDS = 30 ); ``` Use for: typing indicators, presence, cursor events, transient signals. ### STREAM Table Behavior - Rows are scoped by the authenticated or effective `user_id`. - `TTL_SECONDS` is required in SQL creation. - Stream rows are evicted by the stream lifecycle rather than flushed into Parquet cold storage. - `STREAM` tables use the same read/write permission gate as `USER` tables. - `FILE` columns and file APIs are not supported. - Live queries are supported and are commonly used for transient UI state. ## Realtime Subscriptions Today, KalamDB supports realtime subscriptions to all three primary application table types: `USER`, `SHARED`, and `STREAM`. This allows applications to listen for live changes (inserts, updates, deletes) as they happen, enabling reactive UIs and realtime workflows. Subscriptions can be full-row (`SELECT *`) or projected (`SELECT username, status FROM chat.changes ...`) depending on the payload shape your client needs. For the delivery path, shared-table fan-out, and reconnect semantics, see [/docs/server/architecture/live-query](https://kalamdb.org/docs/server/architecture/live-query). ## SYSTEM Tables Internal control-plane and metadata tables (`system.*`). - not intended for normal app DDL lifecycle - role-restricted access for sensitive operations - file upload/download flows reject `SYSTEM` tables - direct DML against `system.*` tables is blocked for normal application workflows - computed `system.*` views expose runtime metadata such as sessions, transactions, cluster state, and jobs `SYSTEM` table access is restricted to `dba` and `system` roles. `service`, `user`, and anonymous roles cannot read system tables through the normal table-access gate. For inspectable system views, see [/docs/server/sql-reference/system-views](https://kalamdb.org/docs/server/sql-reference/system-views). ## Table Options By Type | Option | `USER` | `SHARED` | `STREAM` | `SYSTEM` | Server behavior | |---|---:|---:|---:|---:|---| | `TYPE` | Yes | Yes | Yes | No app DDL | Selects table type when not using `CREATE USER/SHARED/STREAM TABLE`. | | `STORAGE_ID` | Yes | Yes | Parsed but not used | Internal | Defaults to `local`; must reference an existing storage backend. | | `USE_USER_STORAGE` | Yes | No | No | No | Valid only on `USER`; rejected on `SHARED`/`STREAM`. | | `FLUSH_POLICY` | Yes | Yes | Parsed but no flush path | Internal | Triggers hot-to-cold movement for `USER` and `SHARED`. | | `DELETED_RETENTION_HOURS` | Parsed | Parsed | Parsed | Internal | Parsed into the DDL statement; runtime deletion retention is handled by server lifecycle code. | | `TTL_SECONDS` | No | No | Required | No | Valid only on `STREAM`; omitted TTL is rejected. | | `ACCESS_LEVEL` | No | Yes | No | No | Valid only on `SHARED`; omitted value defaults to `PRIVATE`. | For storage templates and `STORAGE_ID` examples, see [/docs/server/sql-reference/storage-id-usage](https://kalamdb.org/docs/server/sql-reference/storage-id-usage). ## DDL Role Matrix | DDL operation | Anonymous | User | Service | DBA | System | |---|---:|---:|---:|---:|---:| | Create `USER` / `SHARED` / `STREAM` table | No | No | Yes | Yes | Yes | | Create `SYSTEM` table | No | No | No | No | No via app SQL | | Alter application table | No | No | Yes | Yes | Yes | | Drop application table | No | No | No | Yes | Yes | | Create view | No | No | No | Yes | Yes | ## Capability Matrix | Capability | USER | SHARED | STREAM | SYSTEM | |---|---|---|---|---| | App DDL creation | Yes | Yes | Yes | Internal only | | Per-user isolation | Yes | No | Yes | Internal | | `ACCESS_LEVEL` option | No | Yes | No | No | | `TTL_SECONDS` option | No | No | Required | No | | `USE_USER_STORAGE` option | Yes | No | No | No | | Cold-tier Parquet lifecycle | Yes | Yes | No | Internal-specific | | File APIs supported | Yes | Yes | No | No | | Live Query | Yes | Yes | Yes | No | | Cluster multi-group Raft support | Yes | Yes | Yes | Internal | ### Capability Definitions - **App DDL creation**: Whether application SQL can create/manage this table type directly. - **Per-user isolation**: Whether data is automatically isolated by authenticated user/tenant. - **`ACCESS_LEVEL` option**: Whether table-level access policy can be configured through `ACCESS_LEVEL`. - **`TTL_SECONDS` option**: Whether the table supports TTL-based automatic row eviction. - **`USE_USER_STORAGE` option**: Whether table storage can be explicitly pinned to user-scoped storage paths. - **Cold-tier Parquet lifecycle**: Whether hot data can flush/compact into cold-tier Parquet segments. - **File APIs supported**: Whether `/v1/files/...` upload/download flows are supported for rows in this table type. - **Live Query**: Whether realtime subscriptions can stream change events from this table type. - **Cluster multi-group Raft support**: Whether metadata/data operations for this table type participate in clustered Raft replication groups. ## Related Docs - [/docs/server/sql-reference/tables](https://kalamdb.org/docs/server/sql-reference/tables) - [/docs/server/sql-reference/dml](https://kalamdb.org/docs/server/sql-reference/dml) - [/docs/server/sql-reference/impersonation](https://kalamdb.org/docs/server/sql-reference/impersonation) - [/docs/server/architecture/storage-tiers](https://kalamdb.org/docs/server/architecture/storage-tiers) - [/docs/server/architecture/live-query](https://kalamdb.org/docs/server/architecture/live-query) - [/docs/server/architecture/file-upload-datatype](https://kalamdb.org/docs/server/architecture/file-upload-datatype) - [/docs/server/sql-reference/storage-id-usage](https://kalamdb.org/docs/server/sql-reference/storage-id-usage) ================================================================================ # Vector Search URL: https://kalamdb.org/docs/server/architecture/vector-search Source: content/server/architecture/vector-search.mdx > Learn how to store embeddings, build cosine indexes, and run vector search in KalamDB with EMBEDDING columns and COSINE_DISTANCE queries. # Vector Search KalamDB supports vector search with native `EMBEDDING(n)` columns, cosine indexes, and SQL ranking via `COSINE_DISTANCE(...)`. Use this when you need semantic retrieval for agent memory, RAG document recall, or similarity search over user-scoped data. Vector search is table-type aware because embeddings live in normal KalamDB tables. Use [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types) to choose `USER` for private semantic memory or `SHARED` for global catalogs. ## What Vector Search Looks Like The current, tested flow in KalamDB is: 1. Create one or more `EMBEDDING(n)` columns. 2. Build a vector index on each embedding column with `ALTER TABLE ... CREATE INDEX ... USING COSINE`. 3. Query nearest rows with `ORDER BY COSINE_DISTANCE(...) LIMIT k`. ## Create a Vector Table ```sql CREATE NAMESPACE IF NOT EXISTS rag; CREATE TABLE rag.documents_vectors ( id BIGINT PRIMARY KEY, doc_embedding EMBEDDING(3), attachment_a_embedding EMBEDDING(3), attachment_b_embedding EMBEDDING(3) ) WITH (TYPE = 'USER'); ``` Why this shape works well: - Keep document metadata and files in your main table. - Keep embeddings in a parallel table keyed by the same `id`. - Use `TYPE = 'USER'` when each signed-in user should search only their own vectors. ## Build Cosine Indexes ```sql ALTER TABLE rag.documents_vectors CREATE INDEX doc_embedding USING COSINE; ALTER TABLE rag.documents_vectors CREATE INDEX attachment_a_embedding USING COSINE; ALTER TABLE rag.documents_vectors CREATE INDEX attachment_b_embedding USING COSINE; ``` KalamDB currently documents and tests cosine similarity indexes. If you create multiple embedding columns, index each column you plan to search independently. ## Insert Embeddings Embeddings are inserted as JSON-like numeric arrays in SQL strings: ```sql INSERT INTO rag.documents_vectors ( id, doc_embedding, attachment_a_embedding, attachment_b_embedding ) VALUES ( 1, '[1.0,0.0,0.0]', '[0.95,0.05,0.0]', '[0.90,0.10,0.0]' ); ``` Make sure the vector length matches the `EMBEDDING(n)` dimension declared in the schema. ## Run a Similarity Query ```sql SELECT id FROM rag.documents_vectors ORDER BY COSINE_DISTANCE(doc_embedding, '[1.0,0.0,0.0]') LIMIT 3; ``` Smaller `COSINE_DISTANCE(...)` values are more similar, so the nearest matches appear first. Pass embeddings as a JSON array literal in the second argument (for example `'[1.0,0.0,0.0]'`). That is the supported KalamDB pattern for `EMBEDDING(n)` columns. When both arguments are array/list values, KalamDB can also use DataFusion 54's native `cosine_distance` implementation. You can search any indexed embedding column: ```sql SELECT id FROM rag.documents_vectors ORDER BY COSINE_DISTANCE(attachment_b_embedding, '[0.0,1.0,0.0]') LIMIT 2; ``` ## Join Search Results Back to Documents In most apps, vector search is only the retrieval step. After ranking the embedding rows, join back to your main document table: ```sql SELECT d.id, d.title, d.body FROM rag.documents AS d JOIN rag.documents_vectors AS v ON v.id = d.id ORDER BY COSINE_DISTANCE(v.doc_embedding, '[1.0,0.0,0.0]') LIMIT 5; ``` That pattern lets you keep rich rows, FILE attachments, and embeddings separate while still querying them together. ## RAG Pattern With Files One tested KalamDB scenario stores: - a USER-scoped documents table with two `FILE` columns - a USER-scoped vectors table keyed by the same `id` - vector indexes persisted across flushes - similarity queries that continue working across hot and cold storage This is a good fit for: - private agent memory per user - semantic search over uploaded files - document recall before LLM summarization or answer generation ## Practical Notes - Use `TYPE = 'USER'` for tenant-safe semantic search. - Keep embedding generation in your application or worker pipeline; KalamDB stores and indexes the vectors. - If you update or delete rows, the vector search results follow the table state. - Vector search continues to work when some rows are still hot and others have already flushed to cold storage. ## Related Docs - [/docs/server/architecture/datatypes](https://kalamdb.org/docs/server/architecture/datatypes) - [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types) - [/docs/server/sql-reference/tables](https://kalamdb.org/docs/server/sql-reference/tables) - [/docs/server/sql-reference/functions](https://kalamdb.org/docs/server/sql-reference/functions) - [/docs/ts-sdk/examples](https://kalamdb.org/docs/ts-sdk/examples) ================================================================================ # Auto-Provisioning Users URL: https://kalamdb.org/docs/server/auth/auto-provisioning Source: content/server/auth/auto-provisioning.mdx > Understand when regular OIDC users are rowless, when explicit persisted users are required, and how OIDC subjects become KalamDB user IDs. # Auto-Provisioning Users KalamDB's current OIDC model has two distinct paths: - regular external users can authenticate directly from validated token claims - elevated or explicit external users are stored locally with `issuer` and `subject` That means auto-provisioning is not just "create a row on first login" anymore. For the common `user` role path, KalamDB can stay stateless after checking whether a local row already exists for the OIDC subject. ## Enable Auto-Provisioning ```toml [auth] jwt_trusted_issuers = "kalamdb,https://idp.example.com/realms/kalamdb" [auth.oidc] enabled = true issuer = "https://idp.example.com/realms/kalamdb" client_id = "kalamdb" scopes = ["openid", "email", "profile"] auto_provision = true default_role = "user" ``` Environment variable form: ```bash ``` If `auth.oidc.auto_provision = false`, authentication succeeds only when a matching local OIDC user already exists. ## What Happens For Regular Users When these conditions are both true: - `auth.oidc.auto_provision = true` - `auth.oidc.default_role = "user"` KalamDB uses the OIDC `sub` claim directly as the KalamDB `user_id`. Regular users resolve from validated token claims when no persisted `system.users` row exists for that subject. KalamDB checks the row first so explicit persisted OIDC users, deleted users, and same-ID local password users keep their local policy. ## What Still Gets Persisted Persisted local state is still important for: - elevated users such as `service`, `dba`, or `system` - soft-deleted external users that must remain blocked - deployments with `auto_provision = false` - deployments that intentionally use a default role other than `user` Persisted OIDC users store only this identity binding: ```json { "issuer": "https://idp.example.com/realms/kalamdb", "subject": "provider-subject" } ``` For persisted OIDC users, `subject` must match `system.users.user_id`. ## Pre-Create Or Elevate An OIDC User ```sql CREATE USER 'provider-subject' WITH OIDC '{"issuer":"https://idp.example.com/realms/kalamdb","subject":"provider-subject"}' ROLE dba EMAIL 'alice@example.com'; ``` Use explicit provisioning when you need: - pre-approved access only - an elevated role before first login - no end-user auto-provisioning in production ## What Happens When The User Does Not Exist If `auth.oidc.auto_provision = false`, the first request for an unknown external identity fails even when the provider token itself is valid. That is expected. Token validation and user mapping are separate steps. ## Identity Stability Rules KalamDB uses the token subject as the user ID, not the email address and not a provider code. That means: - changing email at the provider does not change the KalamDB user ID - a different issuer or provider may emit a different subject for the same person - deleting and recreating a provider-side account can create a new subject and therefore a new KalamDB user ID ## Recommended Rollout Pattern 1. Enable trusted issuers. 2. Turn on `auth.oidc.auto_provision = true` for normal end users. 3. Keep elevated roles explicit. 4. Check the created identity with `GET /v1/api/auth/me` or `SELECT CURRENT_USER();`. 5. Promote only the accounts that need broader access. ## Related - [Authentication Overview](https://kalamdb.org/docs/server/auth) - [Token Auth](https://kalamdb.org/docs/server/auth/token-auth) - [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc) - [Single OIDC Provider Model](https://kalamdb.org/docs/server/auth/providers) - [User Management](https://kalamdb.org/docs/server/sql-reference/users) ================================================================================ # Authentication Providers URL: https://kalamdb.org/docs/server/auth Source: content/server/auth/index.mdx > Authentication model overview for KalamDB. Learn how local auth and the single external OIDC provider fit together, how tokens are routed, and how OIDC subjects become user IDs. # Authentication Overview KalamDB has one authentication surface with two modes: - local username/password login through `[auth.local]` - one external OpenID Connect provider through `[auth.oidc]` The same API and SDK surfaces work for both: - `POST /v1/api/auth/login` - `POST /v1/api/auth/refresh` - `GET /v1/api/auth/me` - `GET /v1/api/auth/login-options` - bearer auth for `/v1/api/sql`, files, topics, and WebSocket traffic ## Authentication Methods | Method | Header | Description | |---|---|---| | Basic Auth | `Authorization: Basic ` | Username + password login, usually exchanged for JWT | | Bearer (Internal JWT) | `Authorization: Bearer ` | KalamDB-issued `HS256` token | | Bearer (External OIDC) | `Authorization: Bearer ` | Token from the configured trusted OIDC issuer | All bearer tokens go through the same validation pipeline. KalamDB routes internal and external tokens automatically from the JWT algorithm and issuer. ## Bearer Token Routing ``` Client Identity Provider KalamDB │ │ │ │── sign in ────────────────►│ │ │◄── ID token (RS256) ────────│ │ │ │ │ │── Authorization: Bearer ─────────────────►│ │ │ 1. Check iss is trusted │ │ │ 2. OIDC discovery │ │ │ 3. Fetch + cache JWKS │ │ │ 4. Verify RS256 sig │ │ │ 5. Resolve/provision user│ │◄── SQL response ────────────────────────────────────── │ ``` KalamDB inspects the token before verification: 1. **Algorithm** — `HS256` → internal shared-secret validation; `RS256/ES256/…` → external OIDC flow. 2. **Issuer (`iss`)** — must appear in `jwt_trusted_issuers`; untrusted issuers are rejected before any network I/O. 3. **JWKS** — keys are fetched on first use and cached per-issuer; key rotation is handled automatically. 4. **Audience (`aud`)** — validated against `auth.oidc.audience` or `auth.oidc.client_id` when configured. ## Single External OIDC Provider KalamDB no longer uses a built-in provider enum or a provider matrix. The active external identity model is issuer-based: ```toml [auth] jwt_trusted_issuers = "kalamdb,https://idp.example.com/realms/kalamdb" [auth.oidc] enabled = true display_name = "Company SSO" issuer = "https://idp.example.com/realms/kalamdb" client_id = "kalamdb" scopes = ["openid", "email", "profile"] auto_provision = true default_role = "user" ``` Examples of issuers that fit this model: - Dex - Keycloak - Okta - Auth0 - Entra ID - Google - Firebase The important point is that one KalamDB server process supports one configured external OIDC issuer. ## Identity Mapping External users use the OIDC `sub` claim directly as the KalamDB `user_id`. There is no provider code, username prefix, or generated hash ID in the active model. The same subject value appears in SQL identity, `system.users.user_id`, and PG extension `EXECUTE AS USER ''` workflows. The subject must be a valid KalamDB user ID: ASCII letters, digits, `_`, or `-`, up to 128 characters. When KalamDB persists a local override or explicit OIDC user, it stores only this provider identity data: ```json { "issuer": "https://idp.example.com/realms/kalamdb", "subject": "provider-subject" } ``` ## Auto-Provision vs Explicit Users When `auth.oidc.auto_provision = true` and `auth.oidc.default_role = "user"`, regular external users authenticate as role `user` without a per-user row on the hot path. KalamDB checks for an existing `system.users` row with `user_id = sub` first. That keeps explicit persisted OIDC users, deleted users, and same-ID local password users from being bypassed. Elevated users still need explicit persisted overrides. Use this when a user must be `service`, `dba`, or `system`: ```sql CREATE USER 'provider-subject' WITH OIDC '{"issuer":"https://idp.example.com/realms/kalamdb","subject":"provider-subject"}' ROLE dba EMAIL 'alice@example.com'; ``` ## Browser And Device Login Surfaces The Admin UI and CLI both discover auth capabilities from `GET /v1/api/auth/login-options`. Current user-facing OIDC flows: - Admin UI browser login through `/v1/api/auth/oidc/exchange-code` - CLI browser login with `kalam login --oidc` - CLI direct device login with `kalam login --oidc --no-browser` - CLI brokered device login with `kalam login --oidc --no-browser --brokered` ## Role Management Roles remain the same regardless of auth source: - `user`: normal app or tenant user - `service`: workers, automation, agents - `dba`: database administration - `system`: highest-privilege internal role ## Supported Algorithms | Algorithm | Supported | |---|---| | HS256 | ✅ Internal tokens only | | RS256 | ✅ | | RS384 | ✅ | | RS512 | ✅ | | PS256 | ✅ | | PS384 | ✅ | | PS512 | ✅ | | ES256 | ✅ | | ES384 | ✅ | | ES512 | ❌ Not supported | ## Related - [Single OIDC Provider Model](https://kalamdb.org/docs/server/auth/providers) - [Token Auth](https://kalamdb.org/docs/server/auth/token-auth) - [Auto-Provisioning Users](https://kalamdb.org/docs/server/auth/auto-provisioning) - [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc) - [Dex](https://kalamdb.org/docs/server/integrations/dex) ================================================================================ # Single OIDC Provider Model URL: https://kalamdb.org/docs/server/auth/providers Source: content/server/auth/providers.mdx > Understand KalamDB's current OIDC identity model: one configured issuer, direct subject user IDs, and no provider-family enum. # Single OIDC Provider Model KalamDB's active external auth model is issuer-based, not provider-family-based. That means: - one KalamDB server process supports one configured external OIDC provider - the provider is identified by `auth.oidc.issuer` and `auth.oidc.client_id` - there is no built-in provider enum, provider prefix table, or provider-specific username format in the active model Dex, Keycloak, Okta, Auth0, Entra ID, Google, Firebase, and similar services all fit this same shape as long as they expose standards-compliant OIDC discovery. ## The Current `server.toml` Shape ```toml [auth] jwt_trusted_issuers = "kalamdb,https://idp.example.com/realms/kalamdb" [auth.oidc] enabled = true display_name = "Company SSO" issuer = "https://idp.example.com/realms/kalamdb" client_id = "kalamdb" scopes = ["openid", "email", "profile"] auto_provision = true default_role = "user" ``` ## No Provider Matrix The current docs and backend surface should not talk about: - `[oauth]` - `[oauth.providers.*]` - provider name lists such as `keycloak`, `firebase`, `auth0`, and so on - provider-code username formats such as `oidc:kcl:` Those were part of an older design. The active model is a single configured OIDC issuer. ## How External Users Are Identified External users use the provider `sub` claim directly as the KalamDB `user_id`. The same value is used by SQL identity, `system.users.user_id`, and PG extension `EXECUTE AS USER ''`. Subjects must be valid KalamDB user IDs: ASCII letters, digits, `_`, or `-`, up to 128 characters. If the provider emits unsafe subject characters, normalize that at the IdP layer before using it with KalamDB. Persisted OIDC users store the issuer and subject, and the `subject` must match the row's `user_id`: ```json { "issuer": "https://idp.example.com/realms/kalamdb", "subject": "provider-subject" } ``` ## Auto-Provision And Explicit Users Regular users can authenticate without a per-user row when: - `auth.oidc.auto_provision = true` - `auth.oidc.default_role = "user"` Elevated roles should be explicit. Example: ```sql CREATE USER 'provider-subject' WITH OIDC '{"issuer":"https://idp.example.com/realms/kalamdb","subject":"provider-subject"}' ROLE dba EMAIL 'alice@example.com'; ``` ## Related - [Authentication Overview](https://kalamdb.org/docs/server/auth) - [Token Auth](https://kalamdb.org/docs/server/auth/token-auth) - [Auto-Provisioning Users](https://kalamdb.org/docs/server/auth/auto-provisioning) - [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc) - [Dex](https://kalamdb.org/docs/server/integrations/dex) ================================================================================ # Token Auth URL: https://kalamdb.org/docs/server/auth/token-auth Source: content/server/auth/token-auth.mdx > Set up token authentication in KalamDB. Covers local login, OIDC exchange flows, direct external bearer tokens, refresh, and SDK token-provider patterns. # Token Auth Use token auth when your app should send `Authorization: Bearer ` on every request instead of raw user/password credentials. KalamDB supports two token sources: - KalamDB-issued access and refresh tokens returned by local login or OIDC exchange endpoints - direct external OIDC ID tokens from the configured trusted issuer ## Internal Token Flow The built-in login flow is: 1. Send user and password to `POST /v1/api/auth/login`. 2. Receive `access_token` and `refresh_token`. 3. Send the access token on API and WebSocket requests. 4. Refresh it through `POST /v1/api/auth/refresh` when needed. ### Login ```bash curl -X POST http://127.0.0.1:2900/v1/api/auth/login \ -H 'Content-Type: application/json' \ -d '{"user":"admin","password":"AdminPass123!"}' ``` Typical response fields: - `access_token` - `refresh_token` - `expires_at` or token-expiry metadata - authenticated user profile fields ### Use the access token ```bash TOKEN="" curl -X POST http://127.0.0.1:2900/v1/api/sql \ -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d '{"sql":"SELECT CURRENT_USER();"}' ``` ## Refresh Tokens ```bash REFRESH="" curl -X POST http://127.0.0.1:2900/v1/api/auth/refresh \ -H "Authorization: Bearer $REFRESH" ``` Use the returned access token for subsequent requests. ## Checking Auth State There are two practical checks: ### Check the current authenticated user ```bash curl http://127.0.0.1:2900/v1/api/auth/me \ -H "Authorization: Bearer $TOKEN" ``` This confirms whether the token is accepted and shows the current user record. ### Check from SQL ```bash curl -X POST http://127.0.0.1:2900/v1/api/sql \ -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d '{"sql":"SELECT CURRENT_USER();"}' ``` This is the fastest way to confirm the runtime identity that SQL sees. ## OIDC Browser And Device Exchange Flows The Admin UI and CLI normally exchange provider login results for KalamDB-issued tokens. Endpoints: - `POST /v1/api/auth/oidc/exchange-code` for browser Authorization Code + PKCE - `POST /v1/api/auth/oidc/exchange-token` for direct device login when the CLI obtains a provider ID token - `POST /v1/api/auth/oidc/device/start` and `POST /v1/api/auth/oidc/device/poll` for brokered device flow CLI entry points: ```bash # Browser PKCE login kalam login --instance local --url http://127.0.0.1:2900 --oidc # Direct device login kalam login --instance local --url http://127.0.0.1:2900 --oidc --no-browser # Brokered device login kalam login --instance local --url http://127.0.0.1:2900 --oidc --no-browser --brokered ``` All three flows return or save KalamDB access and refresh tokens. They do not persist provider credentials. ## Direct External Bearer Tokens If you already have a valid token from the configured trusted OIDC issuer, you can also skip the exchange endpoints and send that provider token directly as a bearer token. KalamDB will: 1. read `alg` and `iss` 2. reject untrusted issuers 3. perform OIDC discovery for the issuer 4. fetch and cache the issuer JWKS 5. validate the token signature and claims 6. resolve or create the matching local user OIDC setup details live in: - [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc) - [Single OIDC Provider Model](https://kalamdb.org/docs/server/auth/providers) - [Dex](https://kalamdb.org/docs/server/integrations/dex) ## SDK Pattern For long-lived apps, return a fresh token from the SDK auth provider callback instead of hard-coding a token once. ### TypeScript ```ts const authProvider: AuthProvider = async () => { const token = await myApp.getOrRefreshJwt(); return Auth.jwt(token); }; const client = createClient({ url: 'https://db.example.com', authProvider, }); ``` ### Dart ```dart final client = await KalamClient.connect( url: 'https://db.example.com', authProvider: () async { final token = await myApp.getOrRefreshJwt(); return Auth.jwt(token); }, ); ``` ## Configuration Notes Internal token auth still depends on a strong shared secret: ```toml [auth] jwt_secret = "replace-with-strong-random-secret-32-plus-chars" cookie_secure = true ``` OIDC also depends on the configured single provider block: ```toml [auth.oidc] enabled = true issuer = "https://idp.example.com/realms/kalamdb" client_id = "kalamdb" scopes = ["openid", "email", "profile"] ``` Use `KALAMDB_JWT_SECRET` and the `KALAMDB_AUTH_OIDC_*` env vars in production instead of committing secrets to `server.toml`. ## Related - [Authentication Overview](https://kalamdb.org/docs/server/auth) - [Auto-Provisioning Users](https://kalamdb.org/docs/server/auth/auto-provisioning) - [HTTP API Reference](https://kalamdb.org/docs/server/api/http-reference) - [Authentication & Bootstrap](https://kalamdb.org/docs/server/getting-started/authentication) ================================================================================ # Benchmarking URL: https://kalamdb.org/docs/server/benchmarking Source: content/server/benchmarking/index.mdx > Static KalamDB benchmark report links synced from benchv2/results, with one HTML benchmark report listed for each tracked version. # Benchmarking This page publishes the newest benchmark HTML report for each KalamDB version currently synced from the `benchv2/results` folder in the main repository. The June 17 [`v0.5.3-rc.1` pre-release](https://kalamdb.org/docs/server/release-v0-5-3-rc-1) is the newest synced run: **38/38 benchmarks passed** with **213,667.71 ms measured benchmark time**, an **8.0% improvement** over `0.5.2-rc.1`. Open the [exact HTML benchmark artifact](https://kalamdb.org/benchmark/results/bench-2026-06-17-131609-0-5-3-rc-1.html) or read the [release blog post](https://kalamdb.org/blog/kalamdb-v0-5-3-rc-1-pre-release). ## What The Table Shows - `Version` is read from the synced benchmark metadata when available. - `Run at` is the benchmark timestamp associated with the synced report. - `Passed/Total` comes from the synced benchmark summary. - `Duration` is the displayed benchmark runtime for that report, preferring measured time when newer reports provide it. - `Delta` compares each row's displayed duration against the next older listed run, with an arrow and color showing whether it got faster or slower. - `HTML report` opens the static benchmark report directly. ================================================================================ # Advanced Configuration URL: https://kalamdb.org/docs/server/configurations/advanced Source: content/server/configurations/advanced.mdx > User-friendly guide to every KalamDB configuration option, with plain-English explanations, practical tuning advice, and production-ready examples. # Advanced Configuration This page is the canonical public reference for every supported `server.toml` key and every documented `KALAMDB_*` runtime override. Use it when you need exact key names, actual defaults, or a clear answer to whether a setting is configured through TOML, an environment variable, or both. ## Before You Start - If you are new to setup, start with [/docs/server/getting-started/configuration](https://kalamdb.org/docs/server/getting-started/configuration). - If you want the fully commented sample file, download [server.example.toml (full commented sample)](https://raw.githubusercontent.com/kalamdb/KalamDB/refs/heads/main/backend/server.example.toml). - The tables below show the real defaults from the config crate. The checked-in sample config may intentionally use different values for local development, CI, or demo environments. Focused guides: - [/docs/server/configurations/logging](https://kalamdb.org/docs/server/configurations/logging) - [/docs/server/configurations/otel](https://kalamdb.org/docs/server/configurations/otel) - [/docs/server/configurations/oidc](https://kalamdb.org/docs/server/configurations/oidc) - [/docs/server/integrations/minio](https://kalamdb.org/docs/server/integrations/minio) - [/docs/server/integrations/keycloak](https://kalamdb.org/docs/server/integrations/keycloak) - [/docs/server/integrations/jaeger](https://kalamdb.org/docs/server/integrations/jaeger) ## Loading Order 1. Load `server.toml`. 2. Apply config-loader environment overrides from `KALAMDB_*` variables. 3. Apply runtime-only environment behavior that lives outside the config crate. 4. Normalize paths and validate the final configuration. Runtime-only env vars are currently: - `KALAMDB_ROOT_PASSWORD` for root bootstrap password selection during startup. - `KALAMDB_TOKIO_WORKER_THREADS` for overriding Tokio runtime worker threads. ## Environment Override Reference Only the variables below are supported today. If a setting is not listed here, configure it in `server.toml`. Boolean parsing notes: - Most positive boolean env vars treat `true`, `1`, and `yes` as enabled. - `KALAMDB_COOKIE_SECURE` is inverted for convenience: `false`, `0`, and `no` disable it; any other non-empty value enables it. List parsing notes: - `KALAMDB_SECURITY_CORS_ALLOWED_ORIGINS` and `KALAMDB_SECURITY_TRUSTED_PROXY_RANGES` accept comma-separated values. - `KALAMDB_CLUSTER_PEERS` accepts `node_id@rpc_addr@api_addr[@rpc_server_name]` entries separated by semicolons. ### Server, Logging, and Storage | Environment variable | Maps to | Notes | |---|---|---| | `KALAMDB_SERVER_HOST` | `server.host` | Bind host/interface. | | `KALAMDB_SERVER_PORT` | `server.port` | HTTP listen port. | | `KALAMDB_SERVER_PUBLIC_ORIGIN` | `server.public_origin` | Empty string preserves browser-origin fallback behavior. | | `KALAMDB_SERVER_WORKERS` | `server.workers` | Actix worker threads. | | `KALAMDB_LOG_LEVEL` | `logging.level` | `error`, `warn`, `info`, `debug`, `trace`. | | `KALAMDB_LOG_FORMAT` | `logging.format` | `compact`, `pretty`, or `json`. | | `KALAMDB_LOGS_DIR` | `logging.logs_path` | Directory for `server.log`, `server.jsonl`, and `slow.log`. | | `KALAMDB_LOG_TO_CONSOLE` | `logging.log_to_console` | Also emit logs to stdout/stderr. | | `KALAMDB_SLOW_QUERY_THRESHOLD_MS` | `logging.slow_query_threshold_ms` | Slow query threshold in milliseconds. | | `KALAMDB_OTLP_ENABLED` | `logging.otlp.enabled` | Enable OTLP trace export. | | `KALAMDB_OTLP_ENDPOINT` | `logging.otlp.endpoint` | Collector endpoint. | | `KALAMDB_OTLP_PROTOCOL` | `logging.otlp.protocol` | `grpc` or `http`. | | `KALAMDB_OTLP_SERVICE_NAME` | `logging.otlp.service_name` | Service name shown by the collector/backend. | | `KALAMDB_OTLP_TIMEOUT_MS` | `logging.otlp.timeout_ms` | OTLP export timeout. | | `KALAMDB_DATA_DIR` | `storage.data_path` | Base data directory. | ### Topics, Auth, Security, and WebSockets | Environment variable | Maps to | Notes | |---|---|---| | `KALAMDB_TOPIC_VISIBILITY_TIMEOUT_SECS` | `topics.visibility_timeout_secs` | Canonical topic visibility timeout override. | | `KALAMDB_VISIBILITY_TIMEOUT_SECS` | `topics.visibility_timeout_secs` | Legacy compatibility alias. | | `KALAMDB_TOPIC_DEFAULT_RETENTION_SECONDS` | `topics.default_retention_seconds` | Default topic time retention. | | `KALAMDB_TOPIC_DEFAULT_RETENTION_MAX_BYTES` | `topics.default_retention_max_bytes` | Default topic byte retention. | | `KALAMDB_TOPIC_RETENTION_CHECK_INTERVAL_SECONDS` | `topics.retention_check_interval_seconds` | Scheduler interval. | | `KALAMDB_TOPIC_RETENTION_BATCH_SIZE` | `topics.retention_batch_size` | Delete batch size. | | `KALAMDB_JWT_SECRET` | `auth.jwt_secret` | Internal JWT signing/verification secret. | | `KALAMDB_JWT_TRUSTED_ISSUERS` | `auth.jwt_trusted_issuers` | Comma-separated issuer list. | | `KALAMDB_JWT_EXPIRY_HOURS` | `auth.jwt_expiry_hours` | Internal token TTL. | | `KALAMDB_COOKIE_SECURE` | `auth.cookie_secure` | HTTPS-only auth cookies. | | `KALAMDB_ALLOW_REMOTE_SETUP` | `auth.allow_remote_setup` | Enables first-time setup from non-localhost clients. | | `KALAMDB_PG_AUTH_TOKEN` | `auth.pg_auth_token` | Pre-shared token for `pg_kalam` gRPC auth. | | `KALAMDB_AUTH_OIDC_AUTO_PROVISION` | `auth.oidc.auto_provision` | OIDC auto-provisioning override. | | `KALAMDB_SECURITY_CORS_ALLOWED_ORIGINS` | `security.cors.allowed_origins` | Comma-separated list or `*`. | | `KALAMDB_SECURITY_TRUSTED_PROXY_RANGES` | `security.trusted_proxy_ranges` | Canonical proxy trust override. | | `KALAMDB_TRUSTED_PROXY_RANGES` | `security.trusted_proxy_ranges` | Legacy env alias kept for compatibility. | | `KALAMDB_RATE_LIMIT_AUTH_REQUESTS_PER_IP_PER_SEC` | `rate_limit.max_auth_requests_per_ip_per_sec` | Brute-force protection rate limit. | | `KALAMDB_WEBSOCKET_CLIENT_TIMEOUT_SECS` | `websocket.client_timeout_secs` | Client heartbeat timeout. | | `KALAMDB_WEBSOCKET_AUTH_TIMEOUT_SECS` | `websocket.auth_timeout_secs` | Time allowed before auth message arrives. | | `KALAMDB_WEBSOCKET_HEARTBEAT_INTERVAL_SECS` | `websocket.heartbeat_interval_secs` | Heartbeat scan interval. | ### Cluster and RPC TLS | Environment variable | Maps to | Notes | |---|---|---| | `KALAMDB_CLUSTER_ID` | `cluster.cluster_id` | Creates the cluster section if needed. | | `KALAMDB_NODE_ID` | `cluster.node_id` | Canonical node ID override. | | `KALAMDB_CLUSTER_NODE_ID` | `cluster.node_id` | Legacy alias for `KALAMDB_NODE_ID`. | | `KALAMDB_CLUSTER_RPC_ADDR` | `cluster.rpc_addr` | Advertised Raft RPC address. | | `KALAMDB_CLUSTER_API_ADDR` | `cluster.api_addr` | Advertised API address. | | `KALAMDB_CLUSTER_PEERS` | `cluster.peers` | `node_id@rpc_addr@api_addr[@rpc_server_name];...` | | `KALAMDB_RPC_TLS_ENABLED` | `rpc_tls.enabled` | Enable TLS/mTLS on the gRPC listener. | | `KALAMDB_RPC_TLS_CA_CERT` | `rpc_tls.ca_cert` | File path or inline PEM. | | `KALAMDB_RPC_TLS_SERVER_CERT` | `rpc_tls.server_cert` | File path or inline PEM. | | `KALAMDB_RPC_TLS_SERVER_KEY` | `rpc_tls.server_key` | File path or inline PEM. | | `KALAMDB_RPC_TLS_REQUIRE_CLIENT_CERT` | `rpc_tls.require_client_cert` | Require client certs signed by `ca_cert`. | ### Runtime-Only Environment Variables | Environment variable | Effect | Notes | |---|---|---| | `KALAMDB_ROOT_PASSWORD` | Sets the initial root password during startup bootstrap. | This does not come from `apply_env_overrides`; it is read in server lifecycle startup. It takes precedence over `auth.root_password`. | | `KALAMDB_TOKIO_WORKER_THREADS` | Overrides Tokio runtime worker thread count. | This is read in the server binary and takes precedence over `performance.tokio_worker_threads`. | ## Full `server.toml` Reference ### Top-Level Keys | Key | Default | Description | |---|---|---| | `transaction_timeout_secs` | `300` | Maximum lifetime of an open transaction before the server aborts it. | | `max_transaction_buffer_bytes` | `104857600` (100 MB) | Maximum in-memory transaction overlay size before new writes are rejected. | ### `[server]` | Key | Default | Description | |---|---|---| | `host` | `"127.0.0.1"` | Network interface to bind. Use `0.0.0.0` only with explicit CORS/origin policy. | | `port` | `2900` | HTTP port for REST, SQL, auth, and WebSocket entrypoints. | | `public_origin` | unset | Public browser-facing origin used by the Admin UI for `/v1/api` and `/v1/ws`. | | `workers` | `0` | Actix worker threads. `0` means automatic sizing. | | `api_version` | `"v1"` | Version prefix for API routes. | | `enable_http2` | `true` | Enables HTTP/1.1 + cleartext HTTP/2 negotiation. | | `ui_path` | unset | Path to a built Admin UI bundle to serve from the server. | ### `[storage]` | Key | Default | Description | |---|---|---| | `data_path` | `"./data"` | Base path for RocksDB, Parquet storage, snapshots, streams, exports, and temp files. | | `shared_tables_template` | `"{namespace}/{tableName}"` | Folder template for shared tables. | | `user_tables_template` | `"{namespace}/{tableName}/{userId}"` | Folder template for user-scoped tables. | #### `[storage.remote_timeouts]` | Key | Default | Description | |---|---|---| | `request_timeout_secs` | `60` | Request timeout for remote storage backends. | | `connect_timeout_secs` | `10` | Connection timeout for remote storage backends. | #### `[storage.rocksdb]` | Key | Default | Description | |---|---|---| | `block_cache_size` | `2097152` (2 MB) | Shared RocksDB read block cache across all column families. | | `max_background_jobs` | `4` | Background compaction and flush worker count. | | `max_open_files` | `512` | Maximum open files kept by RocksDB. `-1` means unlimited. | | `sync_writes` | `false` | Fsync WAL on each write for stronger durability and lower throughput. | | `disable_wal` | `false` | Disables WAL for speed at the cost of crash recovery guarantees. | | `compact_on_startup` | `false` | Compact column families during startup to reduce SST spread. Disabled by default to keep startup fast. | #### `[storage.rocksdb.cf_profiles.system_meta]` | Key | Default | Description | |---|---|---| | `write_buffer_size` | `32768` | Write buffer size for system metadata CFs. | | `max_write_buffers` | `2` | Max RocksDB memtables for system metadata CFs. | #### `[storage.rocksdb.cf_profiles.system_index]` | Key | Default | Description | |---|---|---| | `write_buffer_size` | `32768` | Write buffer size for system index CFs. | | `max_write_buffers` | `2` | Max RocksDB memtables for system index CFs. | #### `[storage.rocksdb.cf_profiles.hot_data]` | Key | Default | Description | |---|---|---| | `write_buffer_size` | `131072` | Write buffer size for hot data CFs. | | `max_write_buffers` | `2` | Max RocksDB memtables for hot data CFs. | #### `[storage.rocksdb.cf_profiles.hot_index]` | Key | Default | Description | |---|---|---| | `write_buffer_size` | `65536` | Write buffer size for hot index CFs. | | `max_write_buffers` | `2` | Max RocksDB memtables for hot index CFs. | #### `[storage.rocksdb.cf_profiles.raft]` | Key | Default | Description | |---|---|---| | `write_buffer_size` | `262144` | Write buffer size for the Raft CF. | | `max_write_buffers` | `2` | Max RocksDB memtables for the Raft CF. | ### `[datafusion]` | Key | Default | Description | |---|---|---| | `memory_limit` | `33554432` (32 MB) | Query execution memory budget. | | `query_parallelism` | `2` | Query execution worker concurrency. | | `max_partitions` | `4` | Maximum partitions used in planning and scans. | | `batch_size` | `1024` | Record batch size for execution operators. | ### `[flush]` | Key | Default | Description | |---|---|---| | `default_row_limit` | `10000` | Row threshold that triggers flush when no per-table override exists. | | `default_time_interval` | `300` | Time threshold in seconds for automatic flush. | | `flush_batch_size` | `10000` | Rows loaded into memory per flush batch. | | `check_interval_seconds` | `60` | Scheduler scan interval for pending writes. `0` disables the scheduler. | #### `[flush.compaction]` Optional post-flush Parquet compaction for trailing small segments. This is configured in `server.toml`; there are no dedicated `KALAMDB_*` overrides for these keys today. For the architecture behind these settings, see [/docs/server/architecture/storage-tiers](https://kalamdb.org/docs/server/architecture/storage-tiers) and [/docs/server/architecture/manifests](https://kalamdb.org/docs/server/architecture/manifests). | Key | Default | Description | |---|---|---| | `enabled` | `false` | Enable leader-only post-flush compaction for eligible `USER` and `SHARED` table scopes. | | `min_eligible_segments` | `5` | Minimum number of newest trailing small segments required before a compaction job is considered. | | `max_segments_per_run` | `8` | Maximum number of newest trailing segments rewritten by one compaction job. | | `user_max_segment_rows` | `10000` | User-table segments below this row count are considered small. | | `shared_max_segment_rows` | `25000` | Shared-table segments below this row count are considered small. | Compaction rewrites only the manifest tail: newest committed segments on the same schema version and below the configured row target. If a newer flush changes that tail while compaction is writing, the swap is skipped and the compacted output is discarded. ### `[retention]` | Key | Default | Description | |---|---|---| | `enable_dba_stats` | `true` | Enables background `dba.stats` collection. | | `dba_stats_retention_days` | `7` | Number of days to retain `dba.stats` samples. `0` disables automatic cleanup. | ### `[stream]` | Key | Default | Description | |---|---|---| | `default_ttl_seconds` | `10` | Default lifetime of stream rows/events. | | `default_max_buffer` | `10000` | Default in-memory stream buffer limit. | | `eviction_interval_seconds` | `60` | Background eviction interval for expired stream data. | ### `[manifest_cache]` Manifest cache behavior is explained in [/docs/server/architecture/manifests](https://kalamdb.org/docs/server/architecture/manifests). | Key | Default | Description | |---|---|---| | `eviction_interval_seconds` | `300` | Interval between manifest cache eviction passes. | | `max_entries` | `500` | Maximum hot manifest entries kept in memory. | | `eviction_ttl_days` | `7` | Age after which unused manifest entries are evicted. | ### `[limits]` | Key | Default | Description | |---|---|---| | `max_message_size` | `1048576` (1 MB) | Maximum accepted request payload/message size. | | `max_query_limit` | `1000` | Hard maximum `LIMIT` the server will accept. | | `default_query_limit` | `50` | Implicit `LIMIT` when the query omits one. | ### `[logging]` For end-to-end examples and deployment guidance, use [/docs/server/configurations/logging](https://kalamdb.org/docs/server/configurations/logging). | Key | Default | Description | |---|---|---| | `level` | `"info"` | Global log verbosity. | | `logs_path` | `"./logs"` | Directory for server log files. | | `log_to_console` | `true` | Also emit logs to stdout/stderr. | | `format` | `"compact"` | `compact`, `pretty`, or `json`. | | `slow_query_threshold_ms` | `1200` | Slow query threshold in milliseconds. | `[logging.targets]` is an optional dynamic table of per-target log levels, for example: ```toml [logging.targets] datafusion = "warn" arrow = "warn" parquet = "warn" ``` #### `[logging.otlp]` | Key | Default | Description | |---|---|---| | `enabled` | `false` | Enables OpenTelemetry trace export. | | `endpoint` | `"http://127.0.0.1:4317"` | Collector endpoint. | | `protocol` | `"grpc"` | OTLP transport: `grpc` or `http`. | | `service_name` | `"kalamdb-server"` | Service name attached to exported spans. | | `timeout_ms` | `3000` | OTLP export timeout in milliseconds. | ### `[performance]` | Key | Default | Description | |---|---|---| | `request_timeout` | `30` | Maximum full request execution time in seconds. | | `keepalive_timeout` | `75` | Idle HTTP keep-alive timeout in seconds. | | `max_connections` | `25000` | Maximum simultaneous client connections. | | `backlog` | `4096` | Kernel listen backlog for pending TCP connections. | | `tokio_worker_threads` | `0` | Tokio runtime worker threads. `0` means auto-size, capped in the binary. | | `worker_max_blocking_threads` | `32` | Per-worker cap for blocking tasks such as RocksDB I/O. | | `client_request_timeout` | `5` | Time allowed for a client to finish sending request headers. | | `client_disconnect_timeout` | `2` | Graceful client disconnect timeout. | | `max_header_size` | `16384` (16 KB) | Maximum HTTP header size. | ### `[rate_limit]` | Key | Default | Description | |---|---|---| | `max_queries_per_sec` | `100` | Per-user SQL query rate limit. | | `max_messages_per_sec` | `50` | Per-WebSocket incoming message rate limit. | | `max_subscriptions_per_user` | `10` | Maximum live subscriptions per user. | | `max_auth_requests_per_ip_per_sec` | `20` | Rate limit for auth/setup endpoints per IP. | | `max_connections_per_ip` | `100` | Maximum concurrent connections per IP. | | `max_requests_per_ip_per_sec` | `200` | Pre-auth request flood protection per IP. | | `request_body_limit_bytes` | `10485760` (10 MB) | Request body cap used by connection protection. | | `ban_duration_seconds` | `300` | Temporary IP ban duration after repeated abuse. | | `enable_connection_protection` | `true` | Master switch for connection abuse protection. | | `cache_max_entries` | `1000` | Maximum cached rate-limit entries. | | `cache_ttl_seconds` | `600` | Idle TTL for cached rate-limit entries. | ### `[security]` | Key | Default | Description | |---|---|---| | `trusted_proxy_ranges` | `[]` | Proxy IPs or CIDR ranges allowed to supply forwarded client IP headers. | | `max_ws_message_size` | `1048576` (1 MB) | Maximum accepted WebSocket message size. | | `strict_ws_origin_check` | `false` | Reject WebSocket connections that lack a valid `Origin` header. | | `max_request_body_size` | `10485760` (10 MB) | Global HTTP request body cap. | #### `[security.cors]` | Key | Default | Description | |---|---|---| | `allowed_origins` | `[]` | Allowed browser origins. Empty behaves like wildcard at runtime, but non-localhost startup validation requires explicit origins. | | `allowed_methods` | `["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"]` | Allowed CORS methods. | | `allowed_headers` | `["Authorization", "Content-Type", "Accept", "Origin", "X-Requested-With"]` | Allowed browser request headers. | | `expose_headers` | `[]` | Response headers exposed to browser JavaScript. | | `allow_credentials` | `false` | Allow cookies or authorization headers on CORS requests. | | `max_age` | `3600` | Preflight cache duration in seconds. | | `allow_private_network` | `false` | Enables private network preflight support where browsers implement it. | ### `[websocket]` | Key | Default | Description | |---|---|---| | `client_timeout_secs` | `10` | Maximum time without client heartbeat activity before disconnect. | | `auth_timeout_secs` | `3` | Time allowed after connect before auth must arrive. | | `heartbeat_interval_secs` | `5` | Interval between heartbeat scans/pings. | ### `[auth]` or `[authentication]` Both section names are accepted. The config crate stores them as `auth` internally. | Key | Default | Description | |---|---|---| | `root_password` | unset | Config-file root bootstrap password. Runtime env `KALAMDB_ROOT_PASSWORD` takes precedence. | | `jwt_secret` | `"CHANGE_ME_IN_PRODUCTION"` | Secret for signing and validating internal HS256 tokens. | | `jwt_trusted_issuers` | `""` | Comma-separated trusted external issuer list. | | `jwt_expiry_hours` | `24` | Lifetime of internal JWTs in hours. | | `cookie_secure` | `true` | Restricts auth cookies to HTTPS. | | `allow_remote_setup` | `false` | Allow first-time setup from non-localhost clients. | | `pg_auth_token` | unset | Pre-shared token for `pg_kalam` gRPC authentication. | Compatibility note: older configs may still contain a pre-rename password-policy key; the current supported key is `auth.local.enforce_password_complexity`. #### `[auth.local]` | Key | Default | Description | |---|---|---| | `enabled` | `true` | Enables local username/password login and password setup. | | `bcrypt_cost` | `12` | Password hash work factor. Valid range is determined by bcrypt. | | `min_password_length` | `8` | Minimum accepted password length. | | `max_password_length` | `1024` | Maximum locally accepted password length. | | `enforce_password_complexity` | `false` | Require uppercase, lowercase, digit, and special characters in passwords. | #### `[auth.oidc]` KalamDB supports one external OpenID Connect provider per server process. | Key | Default | Description | |---|---|---| | `enabled` | `false` | Enables the configured OIDC provider. | | `display_name` | unset | Human-facing provider label for UI and CLI login. | | `issuer` | unset | OIDC issuer URL. Must be included in `auth.jwt_trusted_issuers`. | | `client_id` | unset | Client ID used for browser/device flows and default audience validation. | | `client_secret` | unset | Client secret for confidential clients or brokered provider exchanges. | | `scopes` | `["openid", "email", "profile"]` | OIDC scopes. Must include `openid`. | | `device_authorization_endpoint` | unset | Optional device-flow endpoint override when discovery omits it. | | `broker_device_flow_enabled` | `false` | Enables KalamDB-brokered OIDC device flow. | | `auto_provision` | `false` | Allows absent regular OIDC subjects when default role is `user`; creates rows for elevated default roles. | | `default_role` | `"user"` | Role used for auto-provisioned OIDC users. | | `audience` | unset | Explicit audience override; defaults to `client_id`. | ### `[user_management]` | Key | Default | Description | |---|---|---| | `deletion_grace_period_days` | `30` | Grace period before soft-deleted users are purged. | | `cleanup_job_schedule` | `"0 2 * * *"` | Cron schedule for user cleanup. | ### `[files]` | Key | Default | Description | |---|---|---| | `max_size_bytes` | `26214400` (25 MB) | Maximum size of a single uploaded file. | | `max_files_per_request` | `20` | Maximum uploaded files accepted in one request. | | `max_files_per_folder` | `5000` | Folder rotation threshold for durable file storage. | | `staging_path` | `"./data/tmp"` | Temporary upload staging directory. | | `allowed_mime_types` | `[]` | Allowed MIME types. Empty means allow all. | ### `[shutdown.flush]` | Key | Default | Description | |---|---|---| | `timeout` | `300` | Maximum wait time in seconds for flush jobs during graceful shutdown. | ### `[jobs]` | Key | Default | Description | |---|---|---| | `max_concurrent` | `10` | Maximum concurrently running background jobs. | | `max_retries` | `3` | Retry attempts before a job is marked failed. | | `retry_backoff_ms` | `100` | Initial retry backoff in milliseconds. | | `wal_cleanup_interval_seconds` | `300` | Interval for memtable flushes that reclaim stale WAL files. `0` disables it. | ### `[execution]` | Key | Default | Description | |---|---|---| | `handler_timeout_seconds` | `30` | Maximum execution handler duration in seconds. | | `max_parameters` | `50` | Maximum parameter count per statement. | | `max_parameter_size_bytes` | `524288` (512 KB) | Maximum size of a single parameter value. | | `sql_plan_cache_max_entries` | `200` | Maximum cached SQL logical plans. | | `sql_plan_cache_ttl_seconds` | `900` | Idle TTL for cached SQL logical plans. | ### `[topics]` | Key | Default | Description | |---|---|---| | `visibility_timeout_secs` | `60` | Hide unacknowledged consumer claims for this many seconds before redelivery. | | `default_retention_seconds` | `604800` (7 days) | Default topic time retention. | | `default_retention_max_bytes` | `1073741824` (1 GiB) | Default topic byte retention per partition. | | `retention_check_interval_seconds` | `3600` | Topic retention scheduler interval in seconds. `0` disables scheduling. | | `retention_batch_size` | `10000` | Maximum deletes per partition in a single retention pass. | ### `[rpc_tls]` This secures the shared gRPC listener used by Raft replication, cluster RPC, and the PostgreSQL extension. | Key | Default | Description | |---|---|---| | `enabled` | `false` | Enable TLS or mTLS on the gRPC listener. | | `ca_cert` | unset | CA certificate used to validate incoming client certificates. File path or inline PEM. | | `server_cert` | unset | Server certificate. File path or inline PEM. | | `server_key` | unset | Private key for `server_cert`. File path or inline PEM. | | `require_client_cert` | `true` | Require clients to present a CA-signed certificate. | ### `[cluster]` Omit this section entirely for standalone mode. For the Multi-Raft model behind these options, see [/docs/server/architecture/clustering](https://kalamdb.org/docs/server/architecture/clustering). | Key | Default | Description | |---|---|---| | `cluster_id` | `"cluster"` | Logical cluster identifier shared by all nodes. | | `node_id` | required | Unique node ID for this server. Must be greater than `0`. | | `rpc_addr` | `"127.0.0.1:2910"` | Advertised Raft RPC address. Use a reachable address, not a wildcard, in real clusters. | | `api_addr` | `"0.0.0.0:2900"` | Advertised API address for this node. | | `user_shards` | `8` | Number of user-data shards. | | `shared_shards` | `1` | Number of shared-data shards. | | `heartbeat_interval_ms` | `250` | Raft leader heartbeat interval. | | `election_timeout_ms` | `(500, 1000)` | Min/max election timeout range. Min must exceed heartbeat. | | `snapshot_policy` | `"LogsSinceLast(1000)"` | Snapshot policy. Use `LogsSinceLast(N)` or `Never`. | | `max_snapshots_to_keep` | `3` | Snapshot retention count. `0` keeps all snapshots. | | `replication_timeout_ms` | `5000` | Timeout for learner catch-up and replication progress. | | `reconnect_interval_ms` | `3000` | Delay between reconnect attempts to unreachable peers. | | `peer_wait_max_retries` | unset | Optional cap on peer-startup readiness retries. | | `peer_wait_initial_delay_ms` | unset | Optional initial delay between peer readiness checks. | | `peer_wait_max_delay_ms` | unset | Optional backoff cap for peer readiness checks. | #### `[[cluster.peers]]` | Key | Default | Description | |---|---|---| | `node_id` | required | Peer node ID. | | `rpc_addr` | required | Peer RPC address. | | `api_addr` | required | Peer API address. | | `rpc_server_name` | unset | Optional TLS server-name override for SNI/hostname verification. | **Total Raft groups** = 3 fixed groups (system metadata, user metadata, jobs) + `user_shards` + `shared_shards`. Validation highlights: - `node_id` must be greater than `0` - `election_timeout_ms.0` must be greater than `heartbeat_interval_ms` - `election_timeout_ms.1` must be greater than `election_timeout_ms.0` - `user_shards` and `shared_shards` must be greater than `0` - non-localhost multi-node clusters require `rpc_tls.enabled = true` ================================================================================ # Configurations URL: https://kalamdb.org/docs/server/configurations Source: content/server/configurations/index.mdx > Complete configuration guide for KalamDB deployments. Learn how to tune your server, configure logging, configure OIDC, and set up OpenTelemetry. # Configurations This section centralizes runtime configuration for KalamDB. If you are just getting started, begin with [Getting Started: Configuration](https://kalamdb.org/docs/server/getting-started/configuration). For full production-grade tuning and every configuration section exposed by the config crate, continue to [Advanced Configuration](https://kalamdb.org/docs/server/configurations/advanced). For focused guides, continue to: - [Logging & Traceability](https://kalamdb.org/docs/server/configurations/logging) - [OpenTelemetry (OTEL)](https://kalamdb.org/docs/server/configurations/otel) - [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc) Integration-specific guides are in [Integrations](https://kalamdb.org/docs/server/integrations): - [Dex](https://kalamdb.org/docs/server/integrations/dex) - [MinIO (S3-Compatible)](https://kalamdb.org/docs/server/integrations/minio) - [Jaeger](https://kalamdb.org/docs/server/integrations/jaeger) ## Download Full Sample Config If you want a complete config file with inline comments for every section, download: - [server.example.toml (full commented sample)](https://raw.githubusercontent.com/kalamdb/KalamDB/refs/heads/main/backend/server.example.toml) Quick download command: ```bash curl -L https://raw.githubusercontent.com/kalamdb/KalamDB/refs/heads/main/backend/server.example.toml -o server.sample.toml ``` ================================================================================ # Logging & Traceability URL: https://kalamdb.org/docs/server/configurations/logging Source: content/server/configurations/logging.mdx > Configure KalamDB logging, JSON logs, OTLP tracing, Docker log output, and Dokploy-compatible runtime logging from server.toml or environment variables. # Logging & Traceability This page is the canonical guide for KalamDB runtime logging, JSON log output, Docker log collection, and distributed tracing. Use it when you want to: - switch the backend to JSON logs - configure logging from `server.toml` - run with logging overrides from environment variables - ship logs through Docker or Dokploy - enable OTLP tracing for Jaeger or another collector ## What KalamDB Supports Today KalamDB currently supports two practical runtime log output modes: - `compact`: human-readable text logs written to `server.log` - `json`: JSON Lines logs written to `server.jsonl` When `log_to_console = true`, the same events are also emitted to container stdout or your local terminal. For traceability beyond logs, KalamDB also supports OTLP trace export under `[logging.otlp]`. ## JSON Logging Syntax KalamDB JSON logs are emitted as JSON Lines. - Each log event is one JSON object - Each object is written on its own line - The log file is `server.jsonl` - This format is the one read by `system.server_logs` Example real log line: ```json {"timestamp":"2026-05-13T04:39:33.428337Z","level":"INFO","message":"KalamDB Server v0.5.0-beta.1 | Build: 2026-05-13 03:42:16 UTC","log.target":"kalamdb_server","log.module_path":"kalamdb_server","log.file":"backend/src/main.rs","log.line":340,"target":"kalamdb_server","threadName":"main"} ``` Common fields you should expect: | Field | Meaning | |---|---| | `timestamp` | UTC event timestamp in RFC 3339 format | | `level` | Log severity such as `INFO`, `WARN`, `ERROR`, `DEBUG`, `TRACE` | | `message` | Event message text | | `target` | Rust tracing target for the event | | `threadName` | Thread name that emitted the event | | `log.file` | Source file when available | | `log.line` | Source line when available | | `log.module_path` | Rust module path when available | Depending on the event and span context, extra tracing metadata can also appear. ## Important Limitation: No Custom JSON Schema Yet KalamDB does **not** currently support custom JSON log templates. That means: - you can choose `compact` or `json` - you can choose the log level and destination - you can export OTLP traces - you **cannot** rename JSON keys or define a custom JSON logging pattern yet If you need a different schema for downstream systems, transform the JSON lines in your log pipeline after KalamDB emits them. ## Configure Logging In `server.toml` Use this when you want structured logs on disk and on stdout: ```toml [logging] level = "info" format = "json" logs_path = "./logs" log_to_console = true slow_query_threshold_ms = 1200 [logging.targets] datafusion = "warn" arrow = "warn" parquet = "warn" ``` With that config: - file logs go to `./logs/server.jsonl` - console logs also use the same event stream - `system.server_logs` can read the JSON log file If you use `format = "compact"`, the server writes `server.log` instead. ## Configure Logging With Environment Variables Environment variables override `server.toml` at startup. ### JSON Logging From Env ```bash KALAMDB_LOG_LEVEL=info \ KALAMDB_LOG_FORMAT=json \ KALAMDB_LOGS_DIR=./logs \ KALAMDB_LOG_TO_CONSOLE=true \ cargo run --manifest-path backend/Cargo.toml --bin kalamdb-server ``` ### Logging Environment Variables | Environment variable | Maps to | Notes | |---|---|---| | `KALAMDB_LOG_LEVEL` | `logging.level` | `error`, `warn`, `info`, `debug`, `trace` | | `KALAMDB_LOG_FORMAT` | `logging.format` | Use `json` for JSON Lines, `compact` for text | | `KALAMDB_LOGS_DIR` | `logging.logs_path` | Directory for `server.log` or `server.jsonl` | | `KALAMDB_LOG_TO_CONSOLE` | `logging.log_to_console` | `true`, `1`, `yes` enable console emission | | `KALAMDB_SLOW_QUERY_THRESHOLD_MS` | `logging.slow_query_threshold_ms` | Slow query threshold in milliseconds | | `KALAMDB_OTLP_ENABLED` | `logging.otlp.enabled` | Enables trace export | | `KALAMDB_OTLP_ENDPOINT` | `logging.otlp.endpoint` | OTLP collector endpoint | | `KALAMDB_OTLP_PROTOCOL` | `logging.otlp.protocol` | `grpc` or `http` | | `KALAMDB_OTLP_SERVICE_NAME` | `logging.otlp.service_name` | Service name shown in tracing backends | | `KALAMDB_OTLP_TIMEOUT_MS` | `logging.otlp.timeout_ms` | OTLP export timeout | Current limitation: - per-target overrides under `[logging.targets]` are currently configured in `server.toml`, not through dedicated environment variables ## Docker Logging For Docker deployments, the simplest pattern is: 1. emit JSON logs to stdout 2. keep `log_to_console = true` 3. optionally persist the same logs under `/data/logs` The single-node Docker config shipped with KalamDB already does this: ```toml [logging] level = "info" logs_path = "/data/logs" log_to_console = true format = "json" ``` ### `docker run` Example ```bash docker run --rm \ -p 2900:2900 \ -v "$PWD/data:/data" \ -e KALAMDB_LOG_LEVEL=info \ -e KALAMDB_LOG_FORMAT=json \ -e KALAMDB_LOG_TO_CONSOLE=true \ -e KALAMDB_LOGS_DIR=/data/logs \ jamals86/kalamdb:latest ``` ### Docker Compose Example ```yaml services: kalamdb: image: jamals86/kalamdb:latest ports: - "2900:2900" volumes: - ./data:/data environment: KALAMDB_LOG_LEVEL: info KALAMDB_LOG_FORMAT: json KALAMDB_LOG_TO_CONSOLE: "true" KALAMDB_LOGS_DIR: /data/logs ``` ## Dokploy Compatibility Dokploy runtime logs are compatible with KalamDB JSON logs **as raw log lines**. What this means in practice: - if KalamDB writes JSON logs to stdout, Dokploy can display them - each KalamDB log event appears as one JSON line - this works best when `KALAMDB_LOG_FORMAT=json` and `KALAMDB_LOG_TO_CONSOLE=true` What you should **not** assume today: - Dokploy does not appear to have a documented KalamDB-specific schema for these fields - I did not find evidence that Dokploy automatically maps fields like `threadName`, `target`, or `log.line` into structured columns for KalamDB logs The safe assumption is: - Dokploy will show the JSON lines correctly - any deeper structured parsing depends on Dokploy-side features or downstream tooling, not on a KalamDB-specific format contract Dokploy’s log UI is built around Docker log streaming, so stdout compatibility matters more than matching a custom syntax. ## OTLP Tracing For distributed tracing, configure `[logging.otlp]` in addition to normal logs. ```toml [logging.otlp] enabled = true endpoint = "http://127.0.0.1:4317" protocol = "grpc" service_name = "kalamdb-server" timeout_ms = 3000 ``` Environment equivalent: ```bash ``` Use OTLP when you want: - trace spans in Jaeger, Tempo, or an OpenTelemetry Collector - request correlation across services - timing and span visibility beyond plain log lines Use JSON logging when you want: - structured application logs - Docker or Dokploy log shipping - queryable `system.server_logs` entries Most production setups use both. ## Query Logs From SQL `system.server_logs` reads the JSON log file generated by the backend. Example: ```sql SELECT timestamp, level, target, message FROM system.server_logs ORDER BY timestamp DESC LIMIT 50; ``` This view only works reliably when the backend log format is `json`. ## Recommended Production Setup For most production deployments: 1. set `format = "json"` 2. keep `log_to_console = true` 3. write logs under a persistent directory such as `/data/logs` 4. enable OTLP if you need request tracing in Jaeger or Tempo 5. use Dokploy or Docker for raw log streaming, and a collector for deeper analytics if needed ## Related - [OpenTelemetry (OTEL)](https://kalamdb.org/docs/server/configurations/otel) - [Advanced Configuration](https://kalamdb.org/docs/server/configurations/advanced#logging) - [Jaeger](https://kalamdb.org/docs/server/integrations/jaeger) ================================================================================ # OIDC & Issuer Trust URL: https://kalamdb.org/docs/server/configurations/oidc Source: content/server/configurations/oidc.mdx > Configure KalamDB's single external OpenID Connect provider, issuer trust, device flow options, and the exact `server.toml` / env vars used by the Admin UI and CLI. # OIDC & Issuer Trust KalamDB supports one external OpenID Connect provider per server process, configured under `[auth.oidc]`. Use this page when you want the Admin UI, the CLI, or direct bearer-token clients to authenticate through a standards-compliant OIDC issuer such as Dex, Keycloak, Okta, Auth0, Entra ID, Google, or Firebase. There is no active `[oauth]` or `[oauth.providers.*]` matrix anymore. The current model is: - `[auth.local]` for local username/password login - `[auth.oidc]` for one external OIDC provider - `auth.jwt_trusted_issuers` for the issuer allow-list used by bearer-token validation ## Token Routing Model KalamDB inspects every bearer token **before** signature verification to decide how to validate it: | Token `alg` | Token `iss` | Validation Path | |---|---|---| | HS256 | `kalamdb` | Internal: verified using `auth.jwt_secret` | | RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384 | Any trusted issuer | External: verified via OIDC discovery + provider JWKS | > **ES512 is not supported.** This means external tokens from supported OIDC providers are validated natively through the bearer-token pipeline. ## `server.toml` Settings ```toml [auth] jwt_secret = "replace-with-strong-random-secret-32-plus-chars" jwt_trusted_issuers = "kalamdb,https://issuer.example.com" jwt_expiry_hours = 24 allow_remote_setup = false cookie_secure = true [auth.local] enabled = true [auth.oidc] enabled = true display_name = "Company SSO" issuer = "https://issuer.example.com" client_id = "kalamdb" client_secret = "optional-confidential-client-secret" scopes = ["openid", "email", "profile"] auto_provision = true default_role = "user" broker_device_flow_enabled = true # Optional override when discovery metadata omits device flow. device_authorization_endpoint = "https://issuer.example.com/oauth2/device_authorization" # Optional explicit audience override. Defaults to client_id when unset. # audience = "kalamdb-api" ``` Notes: - `[auth]` is the canonical section name. `[authentication]` still deserializes as a compatibility alias for local files, but new docs and examples use `[auth]`. - `auth.oidc.issuer` and `auth.oidc.client_id` are required when `auth.oidc.enabled = true`. - `auth.oidc.scopes` must include `openid`. - `auth.oidc.audience` is optional and defaults to `client_id` when omitted. - `jwt_trusted_issuers` is a comma-separated string, not an array. - Keep `kalamdb` in `jwt_trusted_issuers` so local KalamDB-issued tokens continue to verify. - Legacy `[oauth]` and `[oauth.providers.*]` sections are no longer supported. ## Environment Variable Overrides ```bash ``` Boolean env vars accept `"true"`, `"1"`, or `"yes"` (case-insensitive). Environment variables override `server.toml` values. ## OIDC Configuration Matrix | `server.toml` key | Environment variable | Required when OIDC enabled | Notes | |---|---|---|---| | `auth.jwt_secret` | `KALAMDB_JWT_SECRET` | Yes | Used for KalamDB-issued HS256 access and refresh tokens | | `auth.jwt_trusted_issuers` | `KALAMDB_JWT_TRUSTED_ISSUERS` | Yes | Include `kalamdb` plus the configured OIDC issuer | | `auth.local.enabled` | `KALAMDB_AUTH_LOCAL_ENABLED` | No | Disable local username/password auth after OIDC migration if desired | | `auth.local.bcrypt_cost` | — | No | Password hash work factor for local users | | `auth.local.min_password_length` | — | No | Minimum accepted local password length | | `auth.local.max_password_length` | — | No | Maximum accepted local password length | | `auth.local.enforce_password_complexity` | — | No | Require uppercase, lowercase, digit, and special characters | | `auth.oidc.enabled` | `KALAMDB_AUTH_OIDC_ENABLED` | Yes | Enables the single external OIDC provider | | `auth.oidc.display_name` | `KALAMDB_AUTH_OIDC_DISPLAY_NAME` | No | Human-facing label in UI/CLI, for example `Dex` or `Company SSO` | | `auth.oidc.issuer` | `KALAMDB_AUTH_OIDC_ISSUER` | Yes | Must start with `http://` or `https://` | | `auth.oidc.client_id` | `KALAMDB_AUTH_OIDC_CLIENT_ID` | Yes | Used for browser and device flows and default audience validation | | `auth.oidc.client_secret` | `KALAMDB_AUTH_OIDC_CLIENT_SECRET` | No | Needed for confidential clients or some brokered device flows | | `auth.oidc.scopes` | `KALAMDB_AUTH_OIDC_SCOPES` | No | CSV env value; must include `openid` | | `auth.oidc.device_authorization_endpoint` | `KALAMDB_AUTH_OIDC_DEVICE_AUTHORIZATION_ENDPOINT` | No | Optional override when discovery omits device flow | | `auth.oidc.broker_device_flow_enabled` | `KALAMDB_AUTH_OIDC_BROKER_DEVICE_FLOW_ENABLED` | No | Enables KalamDB-brokered device flow endpoints | | `auth.oidc.auto_provision` | `KALAMDB_AUTH_OIDC_AUTO_PROVISION` | No | Allows absent regular OIDC subjects when default role is `user`; creates rows for elevated default roles | | `auth.oidc.default_role` | `KALAMDB_AUTH_OIDC_DEFAULT_ROLE` | No | Default role for auto-provisioned users | | `auth.oidc.audience` | `KALAMDB_AUTH_OIDC_AUDIENCE` | No | Explicit audience override; defaults to `client_id` | ## Browser And Device Endpoints Clients discover the current auth surface from: ```text GET /v1/api/auth/login-options ``` KalamDB exposes these OIDC-facing endpoints: | Endpoint | Used by | Purpose | |---|---|---| | `POST /v1/api/auth/oidc/exchange-code` | Admin UI and CLI browser login | Exchange authorization code and PKCE verifier for KalamDB tokens | | `POST /v1/api/auth/oidc/exchange-token` | CLI direct device login | Exchange provider ID token for KalamDB tokens | | `POST /v1/api/auth/oidc/device/start` | CLI brokered device login | Start KalamDB-brokered device flow | | `POST /v1/api/auth/oidc/device/poll` | CLI brokered device login | Poll for brokered device completion | Redirect URIs to register at the provider: - Admin UI: `https://YOUR_KALAMDB_ORIGIN/ui/oauth/callback` - CLI browser login: `http://127.0.0.1:8787/callback` ## OIDC Discovery Flow When KalamDB encounters an external token (non-HS256): 1. Extracts `alg` and `kid` from the JWT header (unverified). 2. Extracts `iss` from the JWT payload (unverified). 3. Confirms `iss` is in `jwt_trusted_issuers`. 4. Fetches `{issuer_url}/.well-known/openid-configuration`. 5. Extracts `jwks_uri` from the discovery response. 6. Fetches the JWKS endpoint and caches keys by `kid`. 7. Matches the token `kid` to a cached signing key. 8. Validates signature, issuer, expiry, and audience (if configured). ## JWKS Caching Behavior - Keys are cached per-issuer in memory, indexed by `kid`. - **Cache-miss refresh**: if a token's `kid` is not found, KalamDB automatically re-fetches the JWKS endpoint. - **No TTL-based eviction**: cache refreshes only on miss — keys remain cached until an unknown `kid` triggers a refresh. - Keys without a `kid` field are silently discarded from the cache. - Key rotation at the provider is handled automatically (new `kid` → cache miss → refresh). ## JWT Claims Structure KalamDB reads these claims from external tokens: | Claim | Type | Required | Notes | |---|---|---|---| | `sub` | string | Yes | KalamDB `user_id`; must be a valid `UserId` | | `iss` | string | Yes | Must match a trusted issuer | | `exp` | number | Yes | Expiry (Unix timestamp, seconds) | | `iat` | number | Yes | Issued-at (Unix timestamp, seconds) | | `username` / `preferred_username` | string | No | Informational only; not used as the KalamDB `user_id` | | `email` | string | No | User email | | `role` | string | No | KalamDB role: `user`, `service`, `dba`, `system` | | `token_type` | string | No | `"access"` or `"refresh"` | ## Deployment Checklist 1. Confirm the exact issuer URL. Trailing slashes matter. 2. Register the Admin UI and CLI redirect URIs at the provider. 3. Set `auth.oidc.issuer`, `auth.oidc.client_id`, and `auth.jwt_trusted_issuers`. 4. Keep `auth.local.enabled = true` during bootstrap and migration. 5. Test `GET /v1/api/auth/login-options`. 6. Test Admin UI browser login and at least one CLI OIDC mode. 7. Disable local auth later only after OIDC access is verified. ## Troubleshooting | Symptom | Likely Cause | Fix | |---|---|---| | `401 Unauthorized` on external token | Issuer not in `jwt_trusted_issuers` | Add exact issuer URL | | `auth.oidc.issuer is required` on startup | OIDC enabled without issuer | Set `auth.oidc.issuer` | | `auth.oidc.client_id is required` on startup | OIDC enabled without client ID | Set `auth.oidc.client_id` | | `auth.oidc.scopes must include the 'openid' scope` | Missing required scope | Add `openid` to `auth.oidc.scopes` | | `MissingKid` error | Token missing `kid` header | Provider must include `kid` in JWT header | | `KeyNotFound` after JWKS refresh | Key rotated and `kid` no longer present | Check provider JWKS, ensure key is published | | `DiscoveryFailed` | Cannot reach `/.well-known/openid-configuration` | Check network, DNS, and issuer URL format | | `JwtValidationFailed: ExpiredSignature` | Token expired | Get fresh token from provider | | User not found after auth | `auth.oidc.auto_provision` is `false` or elevated roles are explicit | Enable auto-provision or pre-create the user with `CREATE USER '' WITH OIDC ...` | ## Related - [Authentication & Bootstrap](https://kalamdb.org/docs/server/getting-started/authentication) - [Single OIDC Provider Model](https://kalamdb.org/docs/server/auth/providers) - [Dex](https://kalamdb.org/docs/server/integrations/dex) - [OpenTelemetry (OTEL)](https://kalamdb.org/docs/server/configurations/otel) ================================================================================ # OpenTelemetry (OTEL) URL: https://kalamdb.org/docs/server/configurations/otel Source: content/server/configurations/otel.mdx > Configure KalamDB distributed tracing with OpenTelemetry. Set up OTLP exporters, Jaeger integration, environment overrides, and server.toml settings. # OpenTelemetry (OTEL) This page shows how to enable distributed tracing for KalamDB with OTLP-compatible backends such as Jaeger. ## Quick Start (Jaeger) Download a ready-to-run compose file: - [Download docker-compose.yml (Jaeger)](https://kalamdb.org/downloads/otel/docker-compose.yml) Run it from the folder where you downloaded it: ```bash cd /path/to/downloaded/file docker compose up -d ``` Jaeger UI: `http://127.0.0.1:16686` This example mirrors the Jaeger section in `KalamDB/docker/utils/docker-compose.yml`. ## Configure `server.toml` KalamDB OTEL settings live under `[logging.otlp]`: ```toml [logging.otlp] enabled = true endpoint = "http://127.0.0.1:4317" protocol = "grpc" service_name = "kalamdb-server" timeout_ms = 3000 ``` ## Environment Variable Overrides Environment variables override `server.toml` values at startup. ```bash ``` `KALAMDB_OTLP_ENABLED` accepts `"true"`, `"1"`, or `"yes"` (case-insensitive). ## Full OTEL Configuration Matrix | `server.toml` key | Environment variable | Default | Notes | |---|---|---|---| | `logging.otlp.enabled` | `KALAMDB_OTLP_ENABLED` | `false` | Enables OTLP trace exporting. | | `logging.otlp.endpoint` | `KALAMDB_OTLP_ENDPOINT` | `http://127.0.0.1:4317` | Collector endpoint. | | `logging.otlp.protocol` | `KALAMDB_OTLP_PROTOCOL` | `grpc` | Supported values: `grpc`, `http`. | | `logging.otlp.service_name` | `KALAMDB_OTLP_SERVICE_NAME` | `kalamdb-server` | Service name shown in tracing backend. | | `logging.otlp.timeout_ms` | `KALAMDB_OTLP_TIMEOUT_MS` | `3000` | Export timeout in milliseconds. | Protocol behavior: - `grpc` expects endpoint like `http://127.0.0.1:4317` (Jaeger port 4317) - `http` expects collector HTTP endpoint like `http://127.0.0.1:4318`; the exporter appends `/v1/traces` automatically ## Validation Checklist 1. Start Jaeger/collector 2. Start KalamDB with OTEL enabled 3. Execute API requests or SQL calls 4. Open Jaeger UI and confirm service name appears ## Configuration Precedence KalamDB applies settings in this order: 1. `server.toml` 2. `KALAMDB_*` environment overrides If both are set, environment variables win. ## Supported Backends Any OTLP-compatible collector works with KalamDB, including: - [Jaeger](https://kalamdb.org/docs/server/integrations/jaeger) (built-in compose example) - Grafana Tempo - Honeycomb - Datadog (OTLP ingest) - OpenTelemetry Collector (forwarding to any backend) ## Related - [Logging & Traceability](https://kalamdb.org/docs/server/configurations/logging) - [Jaeger Integration](https://kalamdb.org/docs/server/integrations/jaeger) - [Advanced Configuration](https://kalamdb.org/docs/server/configurations/advanced#loggingotlp) ================================================================================ # Admin UI Guide URL: https://kalamdb.org/docs/server/getting-started/admin-ui Source: content/server/getting-started/admin-ui.mdx > Operate KalamDB from the built-in Admin UI. Learn how to access setup, login, use SQL Studio, monitor operations, and manage your database visually. # KalamDB Admin UI Guide The Admin UI is served by the KalamDB server at `/ui` and is the fastest way to run SQL, inspect metadata, and monitor operations. ## Access The UI Open: ```text http://localhost:2900/ui/sql ``` If server setup is not complete, finish bootstrap first from [Authentication & Bootstrap](https://kalamdb.org/docs/server/getting-started/authentication). ## Sign In Use a real DBA/system account you created during setup. There is no universal hardcoded production credential. After login, the UI stores auth state using secure cookie/token flow from `/v1/api/auth/*` endpoints. ## SQL Studio SQL Studio is the primary workspace: - namespace/table explorer - multi-tab SQL editor - results and execution details - schema context and recent query history Starter query: ```sql SELECT * FROM system.namespaces LIMIT 100; ``` ## Main Navigation Areas - [Dashboard](https://kalamdb.org/docs/server/admin-ui/dashboard): health and high-level runtime indicators - [SQL Studio](https://kalamdb.org/docs/server/admin-ui/sql-studio): query execution and schema exploration - [Create a Table](https://kalamdb.org/docs/server/admin-ui/create-table): first schema workflow from SQL Studio - [Streaming](https://kalamdb.org/docs/server/admin-ui/streaming): topic monitoring and stream operations - [Users](https://kalamdb.org/docs/server/admin-ui/users): user/role administration - [Jobs](https://kalamdb.org/docs/server/admin-ui/jobs): background job tracking (flush/retention/cleanup) - [Live Queries](https://kalamdb.org/docs/server/admin-ui/live-queries): active subscription inspection - [Logging](https://kalamdb.org/docs/server/admin-ui/logging): server/job/audit log views - [Settings](https://kalamdb.org/docs/server/admin-ui/settings): runtime configuration values exposed by the backend For page-by-page screenshots and operational workflows, continue to [Admin UI](https://kalamdb.org/docs/server/admin-ui). ## Practical Operator Workflow 1. Check node health and queue pressure in `Dashboard`. 2. Validate schema and data paths in `SQL Studio`. 3. Inspect `Jobs` for flush and maintenance behavior. 4. Review `Live Queries` and `Streaming` for realtime workload behavior. 5. Verify production guardrails in `Settings` before rollout. ================================================================================ # AI Agent Coding Guidelines URL: https://kalamdb.org/docs/server/getting-started/ai-agent-guidelines Source: content/server/getting-started/ai-agent-guidelines.mdx > Best practices for AI-generated KalamDB code: use subscriptions for realtime updates, avoid interval polling, and follow consumer ACK patterns. # AI Agent Coding Guidelines When AI agents generate code for KalamDB, use these rules as defaults. Install the official KalamDB skill so supported coding agents can load KalamDB commands, SQL syntax, SDK examples, and operations guidance: ```bash npx skills add kalamdb/kalamdb-skills ``` ## Prefer subscriptions for realtime state For live UI or state sync, use WebSocket subscriptions: - `client.live(sql, callback, options)` for materialized row state - `client.liveTable(table, callback, options)` when table-name sugar is enough - `client.liveEvents(sql, callback, options)` only when you need raw protocol events Use subscriptions as the primary realtime pattern instead of interval polling. Keep live SQL in the strict supported shape: `SELECT ... FROM ... WHERE ...`. Do not put `ORDER BY` or `LIMIT` inside live subscription SQL; sort or cap rows after they arrive. ## Do not use interval polling for realtime updates Avoid timer-based loops such as: - `setInterval(() => fetch(...), 1000)` - periodic `SELECT` queries to detect row changes - fixed sleep loops to simulate streaming These patterns add unnecessary load and increase staleness compared to server-push subscriptions. ## Use topics and consumers for background workers For worker pipelines and asynchronous processing, use topic consumers with ACK semantics: - `createConsumerClient({...}).consumer({...}).run(handler)` - `runConsumer({...})` This is preferred over polling tables on a schedule. ## Reliability defaults for AI-generated code - Keep writes idempotent. - Track and restore progress with sequence IDs / offsets when reconnecting. - Handle subscription `error` events explicitly. - Use bounded retries and `onFailed` persistence in agent/consumer flows. ## Recommended references - [Realtime Subscriptions](https://kalamdb.org/docs/ts-sdk/subscriptions) - [Topic Consumers & ACK](https://kalamdb.org/docs/ts-sdk/consumers) - [Consumer Runtime](https://kalamdb.org/docs/ts-sdk/agents) - [SQL Subscriptions](https://kalamdb.org/docs/server/sql-reference/subscriptions) - [SQL Topics & Consumers](https://kalamdb.org/docs/server/topic-pubsub/sql-topics) ================================================================================ # Authentication & Bootstrap URL: https://kalamdb.org/docs/server/getting-started/authentication Source: content/server/getting-started/authentication.mdx > Bootstrap KalamDB, keep or disable local password login, and configure the single OpenID Connect provider used by the Admin UI and CLI. # Authentication & Bootstrap KalamDB now has one authentication surface with two modes: - local username/password login controlled by `[auth.local]` - one external OpenID Connect provider controlled by `[auth.oidc]` There is no active provider matrix anymore. Dex, Keycloak, Okta, Auth0, Entra ID, Google, Firebase, and similar services all fit the same single-provider OIDC slot as long as they expose standards-compliant discovery metadata. Use this page for the practical rollout: 1. bootstrap the server locally 2. create the initial DBA and root credentials 3. configure `[auth.oidc]` 4. test Admin UI and CLI login 5. optionally disable local password login after migration For the full config matrix, see [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc). For a local test IdP, use [Dex](https://kalamdb.org/docs/server/integrations/dex). ## 1. Bootstrap The Server Check whether the server still needs first-time setup: ```bash curl http://127.0.0.1:2900/v1/api/auth/status ``` When the response contains `"needs_setup": true`, create the initial DBA and root password: ```bash curl -X POST http://127.0.0.1:2900/v1/api/auth/setup \ -H 'Content-Type: application/json' \ -d @- <<'JSON' { "user": "admin", "password": "AdminPass123!", "root_password": "RootPass123!", "email": "admin@example.com" } JSON ``` Important behavior: - `POST /v1/api/auth/setup` is localhost-only unless `auth.allow_remote_setup = true` - setup creates the initial local admin credentials; it does not return tokens - if you plan to disable local auth later, do the bootstrap first while `[auth.local].enabled = true` ## 2. Configure Local + OIDC Auth In `server.toml` Start with a combined rollout so you keep a local admin escape hatch while validating OIDC: ```toml [auth] jwt_secret = "replace-with-a-strong-random-secret-at-least-32-chars" jwt_trusted_issuers = "kalamdb,https://idp.example.com/realms/kalamdb" jwt_expiry_hours = 24 allow_remote_setup = false cookie_secure = true [auth.local] enabled = true [auth.oidc] enabled = true display_name = "Company SSO" issuer = "https://idp.example.com/realms/kalamdb" client_id = "kalamdb" client_secret = "optional-confidential-client-secret" scopes = ["openid", "email", "profile"] auto_provision = true default_role = "user" broker_device_flow_enabled = true # Optional when discovery does not advertise device flow. device_authorization_endpoint = "https://idp.example.com/realms/kalamdb/protocol/openid-connect/auth/device" ``` Rules that matter: - KalamDB supports one external OIDC provider per server process - `auth.oidc.issuer` and `auth.oidc.client_id` are required when `auth.oidc.enabled = true` - `auth.oidc.scopes` must include `openid` - `auth.jwt_trusted_issuers` should include both `kalamdb` and the external issuer Equivalent environment overrides: ```bash ``` Boolean env vars accept `true`, `1`, or `yes` (case-insensitive). ## 3. Discover Available Login Methods The Admin UI and CLI discover auth capabilities from the same endpoint: ```bash curl http://127.0.0.1:2900/v1/api/auth/login-options ``` Typical response: ```json { "local": { "enabled": true }, "oidc": { "enabled": true, "display_name": "Company SSO", "issuer": "https://idp.example.com/realms/kalamdb", "client_id": "kalamdb", "authorization_endpoint": "https://idp.example.com/.../auth", "token_endpoint": "https://idp.example.com/.../token", "device_authorization_endpoint": "https://idp.example.com/.../device", "scopes": ["openid", "email", "profile"], "device_flow": { "direct_supported": true, "broker_supported": true, "broker_start_endpoint": "/v1/api/auth/oidc/device/start", "broker_poll_endpoint": "/v1/api/auth/oidc/device/poll" } } } ``` ## 4. Admin UI Browser Login For browser login, configure the IdP redirect URI to: ```text https://YOUR_KALAMDB_ORIGIN/ui/oauth/callback ``` The Admin UI uses Authorization Code + PKCE: 1. browser fetches `/v1/api/auth/login-options` 2. browser redirects to the configured OIDC authorization endpoint 3. the callback page posts the code and PKCE verifier to `/v1/api/auth/oidc/exchange-code` 4. KalamDB verifies the provider response and returns KalamDB access and refresh tokens ## 5. CLI Login ### Local username/password login ```bash kalam login --instance local --url http://127.0.0.1:2900 --user admin --password ``` ### Browser OIDC login ```bash kalam login --instance local --url http://127.0.0.1:2900 --oidc ``` For browser OIDC, register this redirect URI at the provider: ```text http://127.0.0.1:8787/callback ``` ### Device login without opening a browser callback ```bash kalam login --instance local --url http://127.0.0.1:2900 --oidc --no-browser ``` This uses direct provider device flow when the provider exposes a device authorization endpoint. ### Brokered device login ```bash kalam login --instance local --url http://127.0.0.1:2900 --oidc --no-browser --brokered ``` Use brokered mode when the CLI host cannot reach the provider directly but can reach KalamDB. Operational notes: - successful interactive `kalam login` continues directly into the SQL shell - non-interactive `kalam login` saves credentials and exits - `--no-save` skips writing credentials to disk ## 6. Use KalamDB Tokens For API Calls After local login or OIDC exchange, use the returned KalamDB access token on API calls: ```bash curl -X POST http://127.0.0.1:2900/v1/api/sql \ -H "Authorization: Bearer " \ -H 'Content-Type: application/json' \ -d '{"sql":"SELECT CURRENT_USER();"}' ``` Use `/v1/api/auth/me` to confirm the resolved identity: ```bash curl http://127.0.0.1:2900/v1/api/auth/me \ -H "Authorization: Bearer " ``` ## 7. Auto-Provisioning And Explicit OIDC Users With `auth.oidc.auto_provision = true` and `auth.oidc.default_role = "user"`, regular OIDC users authenticate as their OIDC `sub` without a persisted per-user row when no local row exists. When you need an elevated role before first login, or when `auto_provision = false`, pre-create the OIDC user explicitly with issuer and subject: ```sql CREATE USER 'provider-subject' WITH OIDC '{"issuer":"https://idp.example.com/realms/kalamdb","subject":"provider-subject"}' ROLE service EMAIL 'alice@example.com'; ``` For persisted OIDC users, the `CREATE USER` id must match the OIDC `subject`. ## 8. Disable Local Password Login After Migration Once OIDC is proven and you still have a safe DBA path, you can disable local password login: ```toml [auth.local] enabled = false ``` This disables local username/password login server-side. The Admin UI and CLI also learn that state through `/v1/api/auth/login-options` and should hide local login controls. ## Related - [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc) - [Kalam CLI](https://kalamdb.org/docs/cli) - [Dex](https://kalamdb.org/docs/server/integrations/dex) - [Single OIDC Provider Model](https://kalamdb.org/docs/server/auth/providers) The bridge issuer (e.g. `kalamdb-keycloak-bridge`) must appear in `jwt_trusted_issuers`. ## 8. Production Hardening Before exposing KalamDB publicly: 1. Set strong `auth.jwt_secret` (or `KALAMDB_JWT_SECRET`). 2. Keep `auth.allow_remote_setup = false`. 3. Set `auth.cookie_secure = true` behind HTTPS. 4. Configure `auth.jwt_trusted_issuers` when using external IdPs. 5. Tune `rate_limit.max_auth_requests_per_ip_per_sec`. For full endpoint auth behavior, see [HTTP API Reference](https://kalamdb.org/docs/server/api/http-reference). ================================================================================ # GitHub Binaries URL: https://kalamdb.org/docs/server/getting-started/binaries Source: content/server/getting-started/binaries.mdx > Download prebuilt kalamdb-server and kalam CLI binaries from GitHub Releases. Supports Linux, macOS Apple Silicon, and Windows platforms. # GitHub Binaries If you do not want Docker or a local Rust toolchain, use release artifacts from GitHub: - [KalamDB Releases](https://github.com/kalamdb/KalamDB/releases) Each release publishes `kalamdb-server` and `kalamcli` archives, a `SHA256SUMS` file, and `versions.json`. Always verify checksums before running downloaded binaries. ## What to download From the selected release, download **both** the server and CLI archive for your platform, plus `SHA256SUMS`: | Platform | Server archive | CLI archive | |----------|----------------|-------------| | Linux x86_64 | `kalamdb-server--linux-x86_64.tar.gz` | `kalamcli--linux-x86_64.tar.gz` | | Linux ARM64 | `kalamdb-server--linux-aarch64.tar.gz` | `kalamcli--linux-aarch64.tar.gz` | | macOS Apple Silicon | `kalamdb-server--macos-aarch64.tar.gz` | `kalamcli--macos-aarch64.tar.gz` | | Windows x86_64 | `kalamdb-server--windows-x86_64.zip` | `kalamcli--windows-x86_64.zip` | The file names below automatically use the latest GitHub release tag. macOS Intel (`x86_64`) builds are not published — use Docker, build from source, or run under Rosetta with a Linux binary. ## Important: binary names and config Release archives extract to **versioned filenames** such as `kalamdb-server-0.5.3-rc.1-linux-x86_64`, not plain `kalamdb-server`. The server also requires a `server.toml` config path on startup — it will not run with zero arguments. ## Start the server server.toml <<'EOF' [server] host = "127.0.0.1" port = 2900 [storage] data_path = "./data" [limits] [logging] logs_path = "./logs" log_to_console = true [performance] [auth] jwt_secret = "local-binary-test-secret-32chars-min" root_password = "kalamdb123" EOF ./kalamdb-server-{{RELEASE_VERSION}}-linux-x86_64 server.toml`} /> Confirm it is up: ```bash curl http://127.0.0.1:2900/v1/api/auth/status ``` Point the CLI at the same URL: ```bash ./kalamcli--linux-x86_64 --url http://127.0.0.1:2900 --user root --password ``` Optional: set `KALAMDB_SERVER_BIN` when using `kalam dev` in a project: ```bash kalam dev ``` ## Linux example (x86_64) server.toml <<'EOF' [server] host = "127.0.0.1" port = 2900 [storage] data_path = "./data" [limits] [logging] logs_path = "./logs" log_to_console = true [performance] [auth] jwt_secret = "local-binary-test-secret-32chars-min" root_password = "kalamdb123" EOF ./kalamdb-server-{{RELEASE_VERSION}}-linux-x86_64 server.toml`} /> In another terminal: ```bash curl http://127.0.0.1:2900/v1/api/auth/status ./kalam --help ``` Install onto your PATH (optional): ```bash install -m 755 kalamdb-server kalam ~/.local/bin/ ``` ## macOS example (Apple Silicon) server.toml <<'EOF' [server] host = "127.0.0.1" port = 2900 [storage] data_path = "./data" [limits] [logging] logs_path = "./logs" log_to_console = true [performance] [auth] jwt_secret = "local-binary-test-secret-32chars-min" root_password = "kalamdb123" EOF ./kalamdb-server-{{RELEASE_VERSION}}-macos-aarch64 server.toml ./kalam --help`} /> ## Windows example (x86_64) Verify and use the CLI: ```powershell curl http://127.0.0.1:2900/v1/api/auth/status .\kalamcli--windows-x86_64.exe --help ``` ## Next steps - [Quick Start](https://kalamdb.org/docs/server/getting-started) - [Kalam CLI](https://kalamdb.org/docs/cli) - [Configuration](https://kalamdb.org/docs/server/getting-started/configuration) - [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc) - [Firebase](https://kalamdb.org/docs/server/integrations/firebase) - [Keycloak](https://kalamdb.org/docs/server/integrations/keycloak) - [MinIO (S3-Compatible)](https://kalamdb.org/docs/server/integrations/minio) - [Jaeger](https://kalamdb.org/docs/server/integrations/jaeger) - [OpenTelemetry (OTEL)](https://kalamdb.org/docs/server/configurations/otel) ================================================================================ # Configuration URL: https://kalamdb.org/docs/server/getting-started/configuration Source: content/server/getting-started/configuration.mdx > Minimal configuration for running KalamDB safely in development and production. Covers server.toml settings and environment variable overrides. # Configuration KalamDB loads runtime settings from `server.toml` and then applies `KALAMDB_*` environment overrides. For complete section-by-section tuning, use [Advanced Configuration](https://kalamdb.org/docs/server/configurations/advanced). Focused guides: - [OpenTelemetry (OTEL)](https://kalamdb.org/docs/server/configurations/otel) - [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc) - [Dex](https://kalamdb.org/docs/server/integrations/dex) - [MinIO (S3-Compatible)](https://kalamdb.org/docs/server/integrations/minio) - [Jaeger](https://kalamdb.org/docs/server/integrations/jaeger) ## Download Full Sample Config - [server.example.toml (full commented sample)](https://raw.githubusercontent.com/kalamdb/KalamDB/refs/heads/main/backend/server.example.toml) ```bash curl -L https://raw.githubusercontent.com/kalamdb/KalamDB/refs/heads/main/backend/server.example.toml -o server.example.toml ``` ## Run With Config File ```bash kalamdb-server --config /path/to/server.toml ``` ## Minimal Production-Oriented Example ```toml [server] host = "0.0.0.0" port = 2900 api_version = "v1" enable_http2 = true [storage] data_path = "./data" [auth] jwt_secret = "replace-with-strong-random-secret-32-plus-chars" cookie_secure = true allow_remote_setup = false [auth.local] enabled = true [rate_limit] enable_connection_protection = true max_requests_per_ip_per_sec = 200 max_auth_requests_per_ip_per_sec = 20 [topics] visibility_timeout_secs = 60 [security] max_request_body_size = 10485760 max_ws_message_size = 1048576 [security.cors] allowed_origins = ["https://app.example.com"] allow_credentials = true ``` Notes: - `data_path` is the canonical storage root key (not `data_dir`). - `auth` can also be written as `[authentication]` (alias supported by config loader). - table flush behavior is set per table via SQL `WITH (FLUSH_POLICY = '...')`, not a top-level `storage.flush_policy` key. - `topics.visibility_timeout_secs` controls when unacknowledged topic consumer claims become available for redelivery. ## Add OIDC To `server.toml` KalamDB supports one external OIDC provider at a time. ```toml [auth] jwt_trusted_issuers = "kalamdb,https://idp.example.com/realms/kalamdb" [auth.local] enabled = true [auth.oidc] enabled = true display_name = "Company SSO" issuer = "https://idp.example.com/realms/kalamdb" client_id = "kalamdb" scopes = ["openid", "email", "profile"] auto_provision = true default_role = "user" broker_device_flow_enabled = true ``` Keep local auth enabled during bootstrap and rollout, then disable it later with `[auth.local].enabled = false` if you want an OIDC-only deployment. ## High-Value Environment Overrides These are commonly used in Docker and CI: - `KALAMDB_SERVER_HOST`, `KALAMDB_SERVER_PORT`, `KALAMDB_SERVER_PUBLIC_ORIGIN`, `KALAMDB_SERVER_WORKERS` - `KALAMDB_DATA_DIR` - `KALAMDB_LOG_LEVEL`, `KALAMDB_LOG_FORMAT`, `KALAMDB_LOGS_DIR`, `KALAMDB_LOG_TO_CONSOLE` - `KALAMDB_JWT_SECRET`, `KALAMDB_JWT_TRUSTED_ISSUERS`, `KALAMDB_JWT_EXPIRY_HOURS`, `KALAMDB_COOKIE_SECURE`, `KALAMDB_ALLOW_REMOTE_SETUP` - `KALAMDB_AUTH_LOCAL_ENABLED` - `KALAMDB_AUTH_OIDC_ENABLED`, `KALAMDB_AUTH_OIDC_DISPLAY_NAME`, `KALAMDB_AUTH_OIDC_ISSUER`, `KALAMDB_AUTH_OIDC_CLIENT_ID`, `KALAMDB_AUTH_OIDC_CLIENT_SECRET` - `KALAMDB_AUTH_OIDC_SCOPES`, `KALAMDB_AUTH_OIDC_DEVICE_AUTHORIZATION_ENDPOINT`, `KALAMDB_AUTH_OIDC_BROKER_DEVICE_FLOW_ENABLED` - `KALAMDB_AUTH_OIDC_AUTO_PROVISION`, `KALAMDB_AUTH_OIDC_DEFAULT_ROLE`, `KALAMDB_AUTH_OIDC_AUDIENCE` - `KALAMDB_SECURITY_CORS_ALLOWED_ORIGINS`, `KALAMDB_SECURITY_TRUSTED_PROXY_RANGES` - `KALAMDB_RATE_LIMIT_AUTH_REQUESTS_PER_IP_PER_SEC` - `KALAMDB_TOPIC_VISIBILITY_TIMEOUT_SECS`, `KALAMDB_TOPIC_DEFAULT_RETENTION_SECONDS`, `KALAMDB_TOPIC_DEFAULT_RETENTION_MAX_BYTES` - `KALAMDB_CLUSTER_ID`, `KALAMDB_NODE_ID`, `KALAMDB_CLUSTER_RPC_ADDR`, `KALAMDB_CLUSTER_API_ADDR`, `KALAMDB_CLUSTER_PEERS` - `KALAMDB_RPC_TLS_ENABLED`, `KALAMDB_RPC_TLS_CA_CERT`, `KALAMDB_RPC_TLS_SERVER_CERT`, `KALAMDB_RPC_TLS_SERVER_KEY` - `KALAMDB_ROOT_PASSWORD` for root bootstrap and `KALAMDB_TOKIO_WORKER_THREADS` for Tokio runtime sizing Compatibility aliases still accepted today: - `KALAMDB_CLUSTER_NODE_ID` for `KALAMDB_NODE_ID` - `KALAMDB_VISIBILITY_TIMEOUT_SECS` for `KALAMDB_TOPIC_VISIBILITY_TIMEOUT_SECS` - `KALAMDB_TRUSTED_PROXY_RANGES` for `KALAMDB_SECURITY_TRUSTED_PROXY_RANGES` For OTEL-specific override behavior, see [OpenTelemetry (OTEL)](https://kalamdb.org/docs/server/configurations/otel). For IdP issuer setup, see [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc). For the full, complete override table, use [Advanced Configuration](https://kalamdb.org/docs/server/configurations/advanced). ## Cluster Override Format `KALAMDB_CLUSTER_PEERS` uses this format: ```text node_id@rpc_addr@api_addr[@rpc_server_name];node_id@rpc_addr@api_addr[@rpc_server_name] ``` Example: ```text 2@kalamdb-node2:2910@http://kalamdb-node2:2900;3@kalamdb-node3:2910@http://kalamdb-node3:2900 ``` ================================================================================ # Docker Deployment URL: https://kalamdb.org/docs/server/getting-started/docker Source: content/server/getting-started/docker.mdx > Run KalamDB with Docker or Docker Compose in single-node or 3-node cluster mode. Includes copy-paste commands, health checks, and volume configuration. # Docker Deployment KalamDB publishes [`jamals86/kalamdb:latest`](https://hub.docker.com/r/jamals86/kalamdb) on Docker Hub. The image includes `kalamdb-server`, `kalam-cli`, and a `kalam` symlink. The server listens on port **2900** inside the container. > **Note:** Some older Docker Hub overview text references port `8080` or the `kalamstack` GitHub org. The commands below match the current image. Canonical Hub readme source: [`docker/REPO-README.md`](https://github.com/kalamdb/KalamDB/blob/main/docker/REPO-README.md) in the KalamDB repo. ## Quick start — copy-paste Run a single-node server on host port **2900**, bootstrap auth, and log in: ```bash docker run -d --name kalamdb -p 2900:2900 \ -e KALAMDB_SERVER_HOST=0.0.0.0 \ -e KALAMDB_JWT_SECRET="$(openssl rand -base64 32)" \ -e KALAMDB_ALLOW_REMOTE_SETUP=true \ -v kalamdb_data:/data \ jamals86/kalamdb:latest sleep 5 curl -sS http://127.0.0.1:2900/v1/api/auth/status curl -sS -X POST http://127.0.0.1:2900/v1/api/auth/setup \ -H 'Content-Type: application/json' \ -d '{"user":"admin","password":"AdminPass123!","root_password":"RootPass123!"}' curl -sS -X POST http://127.0.0.1:2900/v1/api/auth/login \ -H 'Content-Type: application/json' \ -d '{"user":"admin","password":"AdminPass123!"}' ``` Save the `access_token` from the login response. Verify from the host with `auth/status` (recommended). The `/v1/api/healthcheck` endpoint is localhost-only when reached through Docker port publishing — use this from inside the container instead: ```bash docker exec kalamdb wget -qO- http://127.0.0.1:2900/v1/api/healthcheck ``` Admin UI: `http://127.0.0.1:2900/ui` — log in as `admin` / `AdminPass123!`. Useful environment variables for local Docker runs: | Variable | Purpose | |----------|---------| | `KALAMDB_SERVER_HOST` | Bind address inside the container — use `0.0.0.0` when publishing ports | | `KALAMDB_JWT_SECRET` | JWT signing secret — use at least 32 characters in shared environments | | `KALAMDB_ALLOW_REMOTE_SETUP` | Allow first-time auth setup from the host when running in Docker | | `KALAMDB_SECURITY_CORS_ALLOWED_ORIGINS` | CORS origins for the Admin UI (compose files default to `*` for local demos) | Avoid `KALAMDB_ROOT_PASSWORD` for first-time Docker setup on the host: it can mark the server as configured while host login still fails. Use `POST /v1/api/auth/setup` instead (as in the quick start above). ## Single node with Docker Compose Download the compose file, create a compatible `server.toml` (the checked-in `docker/run/single/server.toml` in the KalamDB repo is missing required sections for current server builds), then start: ```bash mkdir kalamdb-docker && cd kalamdb-docker curl -fsSL https://raw.githubusercontent.com/kalamdb/KalamDB/main/docker/run/single/docker-compose.yml -o docker-compose.yml cat > server.toml <<'EOF' [server] host = "0.0.0.0" port = 2900 [storage] data_path = "/data" [limits] [logging] level = "info" logs_path = "/data/logs" log_to_console = true format = "json" [performance] [auth] jwt_secret = "docker-compose-placeholder-secret-32c" [security.cors] allowed_origins = ["*"] [rate_limit] max_queries_per_sec = 1000000 max_messages_per_sec = 10000000 max_subscriptions_per_user = 100000 max_auth_requests_per_ip_per_sec = 20000000 max_connections_per_ip = 100000 max_requests_per_ip_per_sec = 20000000 request_body_limit_bytes = 104857600 ban_duration_seconds = 300 enable_connection_protection = true EOF KALAMDB_JWT_SECRET="$(openssl rand -base64 32)" KALAMDB_PORT=2900 docker compose up -d ``` By default the compose file maps `${KALAMDB_PORT:-8088}:2900`. Setting `KALAMDB_PORT=2900` publishes the API at `http://127.0.0.1:2900`. Bootstrap and verify: ```bash curl -sS http://127.0.0.1:2900/v1/api/auth/status curl -sS -X POST http://127.0.0.1:2900/v1/api/auth/setup \ -H 'Content-Type: application/json' \ -d '{"user":"admin","password":"AdminPass123!","root_password":"RootPass123!"}' ``` Friendly alias for the compose file only: [kalamdb.org/docker-compose.yml](https://kalamdb.org/docker-compose.yml) redirects to the GitHub source. You still need a compatible `server.toml` beside it. ## 3-node cluster ```bash mkdir kalamdb-cluster && cd kalamdb-cluster curl -fsSL https://raw.githubusercontent.com/kalamdb/KalamDB/main/docker/run/cluster/docker-compose.yml -o docker-compose.yml KALAMDB_JWT_SECRET="$(openssl rand -base64 32)" docker compose up -d ``` Default host ports: | Node | HTTP API | Raft gRPC | |------|----------|-----------| | node 1 | `8081` | `9091` | | node 2 | `8082` | `9092` | | node 3 | `8083` | `9093` | Verify node 1 from the host: ```bash curl http://127.0.0.1:8081/v1/api/auth/status ``` ## Windows PowerShell `docker run` with bootstrap: ```powershell docker run -d --name kalamdb -p 2900:2900 ` -e KALAMDB_SERVER_HOST=0.0.0.0 ` -e KALAMDB_JWT_SECRET="$([Convert]::ToBase64String((1..32 | ForEach-Object { Get-Random -Maximum 256 })))" ` -e KALAMDB_ALLOW_REMOTE_SETUP=true ` -v kalamdb_data:/data ` jamals86/kalamdb:latest Start-Sleep -Seconds 5 Invoke-RestMethod http://127.0.0.1:2900/v1/api/auth/status Invoke-RestMethod -Method Post http://127.0.0.1:2900/v1/api/auth/setup ` -ContentType "application/json" ` -Body '{"user":"admin","password":"AdminPass123!","root_password":"RootPass123!"}' ``` Compose (after creating `docker-compose.yml` and the `server.toml` from the single-node section above): ```powershell $env:KALAMDB_JWT_SECRET = [Convert]::ToBase64String((1..32 | ForEach-Object { Get-Random -Maximum 256 })) $env:KALAMDB_PORT = "2900" Invoke-WebRequest https://raw.githubusercontent.com/kalamdb/KalamDB/main/docker/run/single/docker-compose.yml -UseBasicParsing -OutFile docker-compose.yml docker compose up -d ``` ## CLI inside the container The image ships with `kalam` and `kalam-cli`: ```bash docker exec kalamdb kalam --version docker exec -it kalamdb kalam --url http://127.0.0.1:2900 --user admin --password ``` CLI state defaults to `/data/.kalam` inside the container so credentials persist with the `/data` volume. ## Persistence and cleanup Data is stored under `/data` in the container. The quick-start `docker run` command uses the named volume `kalamdb_data`. ```bash docker logs -f kalamdb # view logs docker stop kalamdb # stop without deleting data docker rm kalamdb # remove container; volume keeps data docker rm -f kalamdb && docker volume rm kalamdb_data # full reset ``` ## Notes - Keep `KALAMDB_JWT_SECRET` non-default in shared environments. - For first login/setup in Docker, review [Authentication & Bootstrap](https://kalamdb.org/docs/server/getting-started/authentication). - For OIDC issuer trust, review [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc). - Official image: [jamals86/kalamdb on Docker Hub](https://hub.docker.com/r/jamals86/kalamdb) ================================================================================ # Quick Start Guide URL: https://kalamdb.org/docs/server/getting-started Source: content/server/getting-started/index.mdx > Run KalamDB, bootstrap authentication, create your first table, and continue into the TypeScript SDK, HTTP API, or PostgreSQL extension. # Quick Start This guide takes you from zero to a working KalamDB flow: 1. Run the server 2. Bootstrap authentication 3. Create a namespace and table 4. Execute SQL 5. Connect from the TypeScript SDK or CLI Use this path if you want the fastest route into a working local KalamDB setup before moving into the TypeScript SDK, HTTP API, PostgreSQL extension, or production guides. ## Prerequisites Pick the path that matches how you want to run the server: | Option | You need | |--------|----------| | **1. Docker** | Docker Engine or Docker Desktop | | **2. Kalam CLI** | [Kalam CLI](https://kalamdb.org/docs/cli/setup) installed (`install.sh` or npm) and, for TypeScript starters, Node.js plus a package manager (npm, pnpm, yarn, or bun) | | **3. GitHub binaries** | `curl` or a browser — no Rust toolchain required | | **4. Build from source** | Git and Rust `1.92+` | ## Option 1: Docker The fastest way to run KalamDB locally is the published image [`jamals86/kalamdb:latest`](https://hub.docker.com/r/jamals86/kalamdb). Copy-paste the commands below — they start the server, bootstrap auth, and log you in as the initial DBA user. ```bash docker run -d --name kalamdb -p 2900:2900 \ -e KALAMDB_SERVER_HOST=0.0.0.0 \ -e KALAMDB_JWT_SECRET="$(openssl rand -base64 32)" \ -e KALAMDB_ALLOW_REMOTE_SETUP=true \ -v kalamdb_data:/data \ jamals86/kalamdb:latest sleep 5 curl -sS http://127.0.0.1:2900/v1/api/auth/status curl -sS -X POST http://127.0.0.1:2900/v1/api/auth/setup \ -H 'Content-Type: application/json' \ -d '{"user":"admin","password":"AdminPass123!","root_password":"RootPass123!"}' curl -sS -X POST http://127.0.0.1:2900/v1/api/auth/login \ -H 'Content-Type: application/json' \ -d '{"user":"admin","password":"AdminPass123!"}' ``` What this does: - Starts `kalamdb-server` with data persisted in the named volume `kalamdb_data` - Generates a JWT signing secret (required when binding to `0.0.0.0`) - Allows first-time setup from your host machine (`KALAMDB_ALLOW_REMOTE_SETUP=true`) - Creates DBA user `admin` and returns a JWT when login succeeds Save the `access_token` from the login response for the SQL steps below. > **Docker note:** `/v1/api/healthcheck` is localhost-only inside the container. From your host, use `/v1/api/auth/status` (as above) or run `docker exec kalamdb wget -qO- http://127.0.0.1:2900/v1/api/healthcheck`. Admin UI: `http://127.0.0.1:2900/ui` — log in as `admin` with `AdminPass123!`. For Docker Compose (single node or 3-node cluster), volume overrides, and Windows PowerShell examples, see [Docker Deployment](https://kalamdb.org/docs/server/getting-started/docker). ## Option 2: Kalam CLI (`kalam init` + `kalam dev`) Use this path when you want a project scaffold (`kalam.toml`, `schema.sql`, migrations) and a single command that starts the local server, applies schema, and supervises your app. ### Step 1 — Install the CLI ```bash curl -fsSL https://kalamdb.org/install.sh | sh ``` Or with npm: ```bash npm install -g @kalamdb/cli ``` Verify: ```bash kalam --version kalam doctor ``` ### Step 2 — Scaffold a project with `kalam init` Create a directory and run the interactive wizard: ```bash mkdir my-app && cd my-app kalam init ``` The wizard asks for: 1. **Project name** — written to `[project].name` in `kalam.toml` 2. **Schema mode** — choose **SQL file** (`schema.sql`) 3. **Language targets** — TypeScript and/or Dart for generated SDK types 4. **Template** — built-in TypeScript starter (for example `simple-live`) 5. **Package manager** — npm, pnpm, yarn, or bun (TypeScript only) 6. **Server mode** — choose **local** so `kalam dev` can start `kalamdb-server` for you 7. **Server URL** — defaults to `http://localhost:2900` for local mode Non-interactive equivalent (CI or scripted setup): ```bash kalam init --yes \ --name my-app \ --languages typescript \ --template simple-live \ --package-manager pnpm \ --server-mode local ``` `kalam init` creates at minimum: | File / directory | Purpose | |------------------|---------| | `kalam.toml` | Project config: environments, schema paths, dev orchestration | | `schema.sql` | Your schema source | | `kalam/migrations/` | Migration history | | `kalam/server/server.toml` | Local server config (includes `auth.root_password`) | | `src/generated/kalam.ts` | Generated TypeScript types (after first dev run) | ### Step 3 — Start everything with `kalam dev` From the project root: ```bash kalam dev ``` On startup, `kalam dev`: 1. Resolves the dev environment from `kalam.toml` (`http://localhost:2900` by default) 2. **Starts or reuses** a local `kalamdb-server` — if no binary is installed, the CLI can download the matching release into `~/.kalam/bin` (interactive terminals only) 3. **Bootstraps auth** — logs in as local root using `kalam/server/server.toml` and saves credentials to `~/.kalam/` 4. **Applies schema** — runs the migration pipeline against `schema.sql` 5. **Regenerates types** — refreshes SDK artifacts when configured 6. **Supervises app processes** — starts commands from `[dev.processes]` (for example `pnpm dev` for TypeScript starters) To wipe local database files and start over, stop the dev session and run `kalam db reset` (use `--yes` when reusing a non-project server or in non-interactive shells), then `kalam dev` again. Press `Ctrl+C` to shut down managed processes cleanly. Useful flags: ```bash kalam dev --env dev --namespace app --project-dir . kalam dev --force # skip draft confirmation prompts on startup ``` Check project state in another terminal: ```bash kalam status curl http://127.0.0.1:2900/v1/api/auth/status ``` While `kalam dev` is running, sign in to the local server with **`root` / `kalamdb123`**: - **Admin UI:** [http://localhost:2900/ui](http://localhost:2900/ui) - **CLI:** `kalam --url http://127.0.0.1:2900 --user root --password kalamdb123` If port **2900** is already in use, `kalam dev` reuses that server instead of starting a new one. Use a free port when scaffolding: ```bash kalam init --yes --name my-app --languages typescript --server-mode local --server-url http://localhost:2933 ``` Full references: [Project Init](https://kalamdb.org/docs/cli/init) and [Local Development](https://kalamdb.org/docs/cli/dev). ## Option 3: GitHub release binaries Download prebuilt `kalamdb-server` and `kalam` archives from [KalamDB Releases](https://github.com/kalamdb/KalamDB/releases). Each release publishes platform archives plus a `SHA256SUMS` file — verify checksums before extracting. ### Choose your platform The latest release ships these server and CLI archives (version shown dynamically): | Platform | Server archive | CLI archive | |----------|----------------|-------------| | Linux x86_64 | `kalamdb-server--linux-x86_64.tar.gz` | `kalamcli--linux-x86_64.tar.gz` | | Linux ARM64 | `kalamdb-server--linux-aarch64.tar.gz` | `kalamcli--linux-aarch64.tar.gz` | | macOS Apple Silicon | `kalamdb-server--macos-aarch64.tar.gz` | `kalamcli--macos-aarch64.tar.gz` | | Windows x86_64 | `kalamdb-server--windows-x86_64.zip` | `kalamcli--windows-x86_64.zip` | Replace `` with the bare release tag from GitHub (for example `0.5.3-rc.1`). macOS Intel builds are not published — use Docker, Rosetta with a Linux binary, or build from source on Intel Macs. Release archives contain **versioned binary names** (not `kalamdb-server` / `kalam`). You also need a `server.toml` config file — the binary does not start without one. ### Linux (x86_64) example server.toml <<'EOF' [server] host = "127.0.0.1" port = 2900 [storage] data_path = "./data" [limits] [logging] logs_path = "./logs" log_to_console = true [performance] [auth] jwt_secret = "local-binary-test-secret-32chars-min" root_password = "kalamdb123" EOF # 5. Start the server ./kalamdb-server-{{RELEASE_VERSION}}-linux-x86_64 server.toml`} /> In another terminal, confirm the server is up and run the CLI: ```bash curl http://127.0.0.1:2900/v1/api/auth/status ./kalamcli--linux-x86_64 --help # or ./kalam --help if you created the symlink ``` ### macOS (Apple Silicon) example server.toml <<'EOF' [server] host = "127.0.0.1" port = 2900 [storage] data_path = "./data" [limits] [logging] logs_path = "./logs" log_to_console = true [performance] [auth] jwt_secret = "local-binary-test-secret-32chars-min" root_password = "kalamdb123" EOF ./kalamdb-server-{{RELEASE_VERSION}}-macos-aarch64 server.toml`} /> ### Windows (x86_64) example Then verify from PowerShell or Command Prompt: ```powershell curl http://127.0.0.1:2900/v1/api/auth/status .\kalamcli--windows-x86_64.exe --help ``` More platform notes: [GitHub Binaries](https://kalamdb.org/docs/server/getting-started/binaries). ## Option 4: Build from source Clone the repository and run the server with Cargo (requires Rust `1.92+`): ```bash git clone https://github.com/kalamdb/KalamDB.git cd KalamDB/backend cargo run --release --bin kalamdb-server -- server.example.toml ``` The example config binds to `http://127.0.0.1:2900` by default. Copy and edit `server.example.toml` if you need different paths or secrets. To build the CLI as well: ```bash cd ../cli cargo build --release ./target/release/kalam --help ``` ## Verify Health After starting the server with any option above: ```bash curl http://127.0.0.1:2900/v1/api/auth/status ``` For a native or CLI-managed local server you can also use: ```bash curl http://127.0.0.1:2900/v1/api/healthcheck ``` Docker users: the health endpoint is localhost-only from inside the container. Use `auth/status` from the host, or `docker exec kalamdb wget -qO- http://127.0.0.1:2900/v1/api/healthcheck`. ## Bootstrap Authentication Check whether the server still needs first-time setup: ```bash curl http://127.0.0.1:2900/v1/api/auth/status ``` If it returns `"needs_setup": true`, initialize root + DBA user: ```bash curl -X POST http://127.0.0.1:2900/v1/api/auth/setup \ -H 'Content-Type: application/json' \ -d '{"user":"admin","password":"AdminPass123!","root_password":"RootPass123!"}' ``` If the server is already configured, this endpoint returns conflict; in that case, go directly to login. Then log in: ```bash curl -X POST http://127.0.0.1:2900/v1/api/auth/login \ -H 'Content-Type: application/json' \ -d '{"user":"admin","password":"AdminPass123!"}' ``` Keep the returned `access_token` for API calls. ## Create Namespace And Table ```bash TOKEN="" curl -X POST http://127.0.0.1:2900/v1/api/sql \ -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d '{"sql":"CREATE NAMESPACE IF NOT EXISTS app;"}' curl -X POST http://127.0.0.1:2900/v1/api/sql \ -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d @- <<'JSON' {"sql":"CREATE TABLE app.messages (id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), sender TEXT NOT NULL, content TEXT NOT NULL, created_at TIMESTAMP DEFAULT NOW()) WITH (TYPE='USER', FLUSH_POLICY='rows:1000,interval:60');"} JSON ``` ## Insert And Query ```bash curl -X POST http://127.0.0.1:2900/v1/api/sql \ -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d @- <<'JSON' {"sql":"INSERT INTO app.messages (sender, content) VALUES ('alice', 'Hello KalamDB');"} JSON curl -X POST http://127.0.0.1:2900/v1/api/sql \ -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d '{"sql":"SELECT * FROM app.messages ORDER BY created_at DESC LIMIT 10;"}' ``` ## Connect With TypeScript SDK (`@kalamdb/client`) Install the package first: ```bash npm install @kalamdb/client ``` Package reference: ```ts const client = createClient({ url: 'http://127.0.0.1:2900', authProvider: async () => Auth.jwt(''), }); const res = await client.query('SELECT * FROM app.messages LIMIT 5'); console.log(res.results[0]); ``` ## Connect With Kalam CLI Install the CLI with npm or the curl installer: ```bash npm install -g @kalamdb/cli # or curl -fsSL https://kalamdb.org/install.sh | sh ``` Log in and run a query: ```bash kalam login --instance local --url http://127.0.0.1:2900 --user admin --password kalam -c "SELECT * FROM app.messages ORDER BY created_at DESC LIMIT 5;" kalam doctor ``` If you want to test the OIDC flow instead of local password login: ```bash kalam login --instance local --url http://127.0.0.1:2900 --oidc kalam login --instance local --url http://127.0.0.1:2900 --oidc --no-browser ``` Full CLI reference: [Kalam CLI](https://kalamdb.org/docs/cli) — start with [Project Init](https://kalamdb.org/docs/cli/init) and [Local Development](https://kalamdb.org/docs/cli/dev) ## Use KalamDB Skills With Coding Agents Install the official KalamDB skill for Codex, Claude Code, OpenCode, and Agent Skills-compatible tools: ```bash npx skills add kalamdb/kalamdb-skills ``` ## Choose Your Next Track ### Building an app or AI-agent backend in TypeScript Continue with [TypeScript Setup](https://kalamdb.org/docs/ts-sdk/setup), [Authentication](https://kalamdb.org/docs/ts-sdk/auth), [Querying & DML](https://kalamdb.org/docs/ts-sdk/querying), and [Realtime Subscriptions](https://kalamdb.org/docs/ts-sdk/subscriptions). ### Building background workers and agent automation Continue with [Topic Consumers & ACK](https://kalamdb.org/docs/ts-sdk/consumers), [Consumer Runtime](https://kalamdb.org/docs/ts-sdk/agents), and [AI Agent Coding Guidelines](https://kalamdb.org/docs/server/getting-started/ai-agent-guidelines). ### Integrating through PostgreSQL Continue with [PostgreSQL Extension Getting Started](https://kalamdb.org/docs/pg-kalam/getting-started), [SQL Syntax](https://kalamdb.org/docs/pg-kalam/sql-syntax), and [Data Type Conversions](https://kalamdb.org/docs/pg-kalam/type-conversions). ### Hardening for production Continue with [Authentication & Bootstrap](https://kalamdb.org/docs/server/getting-started/authentication), [Configuration](https://kalamdb.org/docs/server/getting-started/configuration), [Security](https://kalamdb.org/docs/server/security), and [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc). ### Advanced features and integrations Continue with [SQL Reference](https://kalamdb.org/docs/server/sql-reference), [Vector Search](https://kalamdb.org/docs/server/architecture/vector-search), [Dex](https://kalamdb.org/docs/server/integrations/dex), [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc), [MinIO (S3-Compatible)](https://kalamdb.org/docs/server/integrations/minio), [Jaeger](https://kalamdb.org/docs/server/integrations/jaeger), and [OpenTelemetry (OTEL)](https://kalamdb.org/docs/server/configurations/otel). ================================================================================ # Server Docs URL: https://kalamdb.org/docs/server Source: content/server/index.mdx > Run KalamDB, configure authentication, use SQL, operate storage, and deploy production-ready realtime database services. # KalamDB Server Docs Use these server docs to run KalamDB, build a SQL-first backend for AI agents and realtime apps, configure authentication, operate storage, and deploy production services. If you are comparing backend-as-a-service options, KalamDB gives you a self-hosted realtime data layer with SQL, authentication, tenant isolation, live subscriptions, topics, and vector search in one runtime. ## Start Here 1. [/docs/server/getting-started](https://kalamdb.org/docs/server/getting-started) 2. [/docs/server/sql-reference](https://kalamdb.org/docs/server/sql-reference) 3. [/docs/server/getting-started/authentication](https://kalamdb.org/docs/server/getting-started/authentication) 4. [/docs/server/configurations](https://kalamdb.org/docs/server/configurations) 5. [/docs/server/security](https://kalamdb.org/docs/server/security) ## Choose Your Path ### I am building AI agents or a chat application Start with [/docs/server/getting-started](https://kalamdb.org/docs/server/getting-started), then continue to [/docs/ts-sdk/setup](https://kalamdb.org/docs/ts-sdk/setup), [/docs/ts-sdk/subscriptions](https://kalamdb.org/docs/ts-sdk/subscriptions), and [/docs/ts-sdk/consumers](https://kalamdb.org/docs/ts-sdk/consumers). ### I want a backend-as-a-service alternative I can control Start with [/docs/server/getting-started](https://kalamdb.org/docs/server/getting-started), then read [/docs/server/getting-started/authentication](https://kalamdb.org/docs/server/getting-started/authentication), [/docs/server/sql-reference](https://kalamdb.org/docs/server/sql-reference), [/docs/server/security](https://kalamdb.org/docs/server/security), and [/docs/server/configurations/oidc](https://kalamdb.org/docs/server/configurations/oidc). ### I need PostgreSQL compatibility Start with [/docs/pg-kalam/getting-started](https://kalamdb.org/docs/pg-kalam/getting-started), then read [/docs/pg-kalam/architecture](https://kalamdb.org/docs/pg-kalam/architecture), [/docs/pg-kalam/sql-syntax](https://kalamdb.org/docs/pg-kalam/sql-syntax), and [/docs/pg-kalam/type-conversions](https://kalamdb.org/docs/pg-kalam/type-conversions). ### I need advanced data and operations guidance Start with [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types), [/docs/server/architecture/vector-search](https://kalamdb.org/docs/server/architecture/vector-search), [/docs/server/topic-pubsub](https://kalamdb.org/docs/server/topic-pubsub), [/docs/server/architecture/storage-tiers](https://kalamdb.org/docs/server/architecture/storage-tiers), [/docs/server/architecture/clustering](https://kalamdb.org/docs/server/architecture/clustering), and [/docs/server/configurations/otel](https://kalamdb.org/docs/server/configurations/otel). ## Questions New Users Ask ### What should I read first? If you are new, start at [/docs/server/getting-started](https://kalamdb.org/docs/server/getting-started). It takes you from running KalamDB locally to creating your first table and sending your first SQL query. ### Can frontend clients execute SQL directly against KalamDB? Yes. Use [/docs/ts-sdk](https://kalamdb.org/docs/ts-sdk) for browser and app-facing clients. USER tables keep the same SQL scoped to the signed-in user, and live subscriptions push updates over WebSockets. ### Where do I start for workers, automation, and advanced agent flows? Go from [/docs/ts-sdk/setup](https://kalamdb.org/docs/ts-sdk/setup) to [/docs/ts-sdk/consumers](https://kalamdb.org/docs/ts-sdk/consumers), [/docs/ts-sdk/agents](https://kalamdb.org/docs/ts-sdk/agents), and [/docs/server/getting-started/ai-agent-guidelines](https://kalamdb.org/docs/server/getting-started/ai-agent-guidelines). ### Where do I learn production deployment and observability? Read [/docs/server/security](https://kalamdb.org/docs/server/security), [/docs/server/configurations/logging](https://kalamdb.org/docs/server/configurations/logging), [/docs/server/configurations/advanced](https://kalamdb.org/docs/server/configurations/advanced), [/docs/server/configurations/oidc](https://kalamdb.org/docs/server/configurations/oidc), [/docs/server/integrations/dex](https://kalamdb.org/docs/server/integrations/dex), [/docs/server/integrations/jaeger](https://kalamdb.org/docs/server/integrations/jaeger), and [/docs/server/configurations/otel](https://kalamdb.org/docs/server/configurations/otel). ## Core Capabilities KalamDB combines: - SQL query and DDL workflows - user-isolated and shared table models - live query subscriptions over WebSocket - topic consume/ack worker patterns - vector search with `EMBEDDING(n)` columns and cosine ranking - hot + cold storage lifecycle in one runtime | Capability | What it gives you | |------------|-------------------| | `USER` tables | per-user/tenant isolation | | `SHARED` tables | global app data scope | | `STREAM` tables | ephemeral realtime state with TTL | | live subscriptions | push-based UI/state updates | | topics + consumers | queue-like worker processing | | vector search | semantic retrieval with cosine distance | | storage commands | explicit flush/compact/manifest tooling | ## Release Information For the latest published versions and artifacts, use: - [KalamDB Releases](https://github.com/kalamdb/KalamDB/releases) ================================================================================ # Dex URL: https://kalamdb.org/docs/server/integrations/dex Source: content/server/integrations/dex.mdx > Use Dex as a local or test OpenID Connect provider for KalamDB, including server.toml config, Admin UI login, and CLI browser/device flows. # Dex Dex is the simplest supported way to test KalamDB's current OIDC flow locally. Use Dex when you want: - a small standards-compliant OIDC issuer for local development - repeatable browser and device-flow testing for the Admin UI and CLI - a test IdP that matches KalamDB's single-provider OIDC model ## Local Dex Setup The KalamDB repo already includes a Dex container and config: ```bash cd KalamDB/docker/utils docker compose up -d dex ``` Default local Dex values from the shared development config: - issuer: `http://127.0.0.1:5556` - public client ID: `client` - Admin UI callback: `http://127.0.0.1:2900/ui/oauth/callback` - CLI browser callback: `http://127.0.0.1:8787/callback` - test user: `alice@example.org` - test password: `kalamdb123` ## KalamDB `server.toml` Example ```toml [auth] jwt_secret = "replace-with-a-strong-random-secret-at-least-32-chars" jwt_trusted_issuers = "kalamdb,http://127.0.0.1:5556" allow_remote_setup = false cookie_secure = false [auth.local] enabled = true [auth.oidc] enabled = true display_name = "Dex" issuer = "http://127.0.0.1:5556" client_id = "client" scopes = ["openid", "email", "profile"] auto_provision = true default_role = "user" broker_device_flow_enabled = false device_authorization_endpoint = "http://127.0.0.1:5556/device/code" ``` For the public Dex client used in local development, leave `client_secret` unset. ## Admin UI Login Make sure the Dex client allows this redirect URI: ```text http://127.0.0.1:2900/ui/oauth/callback ``` Then open the Admin UI, choose the OIDC login button, and sign in as `alice@example.org` with password `kalamdb123`. ## CLI Login ### Browser login ```bash kalam login --instance local --url http://127.0.0.1:2900 --oidc ``` This uses the local browser callback at `http://127.0.0.1:8787/callback`. ### Direct device login ```bash kalam login --instance local --url http://127.0.0.1:2900 --oidc --no-browser ``` Use this when you want to stay headless and let the CLI complete provider device flow directly. ### Brokered device login ```bash kalam login --instance local --url http://127.0.0.1:2900 --oidc --no-browser --brokered ``` Enable brokered mode only when the CLI host cannot reach Dex directly but can reach KalamDB. ## Notes On Production Use Dex can be used beyond local development, but production use is an operational choice, not a KalamDB requirement. Dex is a good fit when: - you control the deployment environment - you manage TLS, connector configuration, storage, and backups yourself - you want a lightweight self-hosted OIDC layer Many teams still prefer an existing enterprise IdP for production. From KalamDB's perspective, that is fine because the runtime only cares about one configured OIDC issuer. ## Related - [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc) - [Authentication & Bootstrap](https://kalamdb.org/docs/server/getting-started/authentication) - [Kalam CLI](https://kalamdb.org/docs/cli) - [OIDC Model](https://kalamdb.org/docs/server/auth/providers) ================================================================================ # Firebase URL: https://kalamdb.org/docs/server/integrations/firebase Source: content/server/integrations/firebase.mdx > Use Firebase as the single configured OIDC issuer for KalamDB. This page shows the current generic OIDC model, not the removed provider-specific config surface. # Firebase KalamDB no longer has Firebase-specific auth configuration. Firebase now fits the same single-provider OIDC model as Dex, Keycloak, Okta, Auth0, Entra ID, Google, and other standards-compliant issuers. Use the generic OIDC docs and set the issuer to your Firebase project issuer. ## Example `server.toml` ```toml [auth] jwt_trusted_issuers = "kalamdb,https://securetoken.google.com/YOUR_PROJECT_ID" [auth.oidc] enabled = true display_name = "Firebase" issuer = "https://securetoken.google.com/YOUR_PROJECT_ID" client_id = "YOUR_PROJECT_ID" scopes = ["openid", "email", "profile"] auto_provision = true default_role = "user" ``` If you need explicit audience validation, keep `client_id` set to the Firebase project ID. ## What Changed Do not use or document these removed shapes anymore: - `[oauth]` - `[oauth.providers.firebase]` - provider-coded identities such as `oidc:fbs:` Current external identities use the Firebase/OIDC `sub` claim directly as the KalamDB `user_id`. If you need explicit persisted users or elevated roles, create them with `issuer` and `subject`: ```sql CREATE USER 'FIREBASE_UID' WITH OIDC '{"issuer":"https://securetoken.google.com/YOUR_PROJECT_ID","subject":"FIREBASE_UID"}' ROLE dba EMAIL 'user@example.com'; ``` ## Related - [Authentication Overview](https://kalamdb.org/docs/server/auth) - [Token Auth](https://kalamdb.org/docs/server/auth/token-auth) - [Auto-Provisioning Users](https://kalamdb.org/docs/server/auth/auto-provisioning) - [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc) - [OIDC Model](https://kalamdb.org/docs/server/auth/providers) ================================================================================ # Integrations URL: https://kalamdb.org/docs/server/integrations Source: content/server/integrations/index.mdx > Integration guides for external services used with KalamDB. Learn how to use Dex for local OIDC testing and how to connect MinIO and Jaeger. # Integrations This section covers external tools and identity/storage integrations. - [Dex](https://kalamdb.org/docs/server/integrations/dex) - [MinIO (S3-Compatible)](https://kalamdb.org/docs/server/integrations/minio) - [Jaeger](https://kalamdb.org/docs/server/integrations/jaeger) KalamDB's current auth surface supports one external OIDC provider at a time. Dex is the recommended local and CI-friendly test issuer in the current docs set. *** Add File: /Users/jamal/git/KalamSite/content/server/integrations/dex.mdx --- title: "Dex" description: "Use Dex as a local or test OpenID Connect provider for KalamDB, including server.toml config, Admin UI login, and CLI browser/device flows." --- # Dex Dex is the simplest supported way to test KalamDB's current OIDC flow locally. Use Dex when you want: - a small standards-compliant OIDC issuer for local development - repeatable browser and device-flow testing for the Admin UI and CLI - a test IdP that matches KalamDB's single-provider OIDC model ## Local Dex Setup The KalamDB repo already includes a Dex container and config: ```bash cd KalamDB/docker/utils docker compose up -d dex ``` Default local Dex values from the shared development config: - issuer: `http://127.0.0.1:5556` - public client ID: `client` - Admin UI callback: `http://127.0.0.1:2900/ui/oauth/callback` - CLI browser callback: `http://127.0.0.1:8787/callback` - test user: `alice@example.org` - test password: `kalamdb123` ## KalamDB `server.toml` Example ```toml [auth] jwt_secret = "replace-with-a-strong-random-secret-at-least-32-chars" jwt_trusted_issuers = "kalamdb,http://127.0.0.1:5556" allow_remote_setup = false cookie_secure = false [auth.local] enabled = true [auth.oidc] enabled = true display_name = "Dex" issuer = "http://127.0.0.1:5556" client_id = "client" scopes = ["openid", "email", "profile"] auto_provision = true default_role = "user" broker_device_flow_enabled = false device_authorization_endpoint = "http://127.0.0.1:5556/device/code" ``` For the public Dex client used in local development, leave `client_secret` unset. ## Admin UI Login Make sure the Dex client allows this redirect URI: ```text http://127.0.0.1:2900/ui/oauth/callback ``` Then open the Admin UI, choose the OIDC login button, and sign in as `alice@example.org` with password `kalamdb123`. ## CLI Login ### Browser login ```bash kalam login --instance local --url http://127.0.0.1:2900 --oidc ``` This uses the local browser callback at `http://127.0.0.1:8787/callback`. ### Direct device login ```bash kalam login --instance local --url http://127.0.0.1:2900 --oidc --no-browser ``` Use this when you want to stay headless and let the CLI complete provider device flow directly. ### Brokered device login ```bash kalam login --instance local --url http://127.0.0.1:2900 --oidc --no-browser --brokered ``` Enable brokered mode only when the CLI host cannot reach Dex directly but can reach KalamDB. ## Notes On Production Use Dex can be used beyond local development, but production use is an operational choice, not a KalamDB requirement. Dex is a good fit when: - you control the deployment environment - you manage TLS, connector configuration, storage, and backups yourself - you want a lightweight self-hosted OIDC layer Many teams still prefer an existing enterprise IdP for production. From KalamDB's perspective, that is fine because the runtime only cares about one configured OIDC issuer. ## Related - [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc) - [Authentication & Bootstrap](https://kalamdb.org/docs/server/getting-started/authentication) - [Kalam CLI](https://kalamdb.org/docs/cli) - [OIDC Model](https://kalamdb.org/docs/server/auth/providers) ================================================================================ # Jaeger URL: https://kalamdb.org/docs/server/integrations/jaeger Source: content/server/integrations/jaeger.mdx > Set up Jaeger for distributed tracing with KalamDB. Configure OTEL exports via gRPC or HTTP, visualize traces, and debug production workloads. # Jaeger Use Jaeger as an OTLP-compatible backend for KalamDB distributed tracing. ## Download Compose Example - [Download docker-compose.yml (Jaeger)](https://kalamdb.org/downloads/otel/docker-compose.yml) ## Docker Compose Details The compose file runs: ```yaml jaeger: image: jaegertracing/jaeger:latest environment: COLLECTOR_OTLP_ENABLED: "true" ports: - "16686:16686" # Jaeger UI - "4317:4317" # OTLP gRPC receiver - "4318:4318" # OTLP HTTP receiver ``` Start it: ```bash docker compose up -d ``` Jaeger UI: `http://127.0.0.1:16686` ## Connect KalamDB → Jaeger ### Via gRPC (default, port 4317) ```toml [logging.otlp] enabled = true endpoint = "http://127.0.0.1:4317" protocol = "grpc" service_name = "kalamdb-server" timeout_ms = 3000 ``` ### Via HTTP (port 4318) ```toml [logging.otlp] enabled = true endpoint = "http://127.0.0.1:4318" protocol = "http" service_name = "kalamdb-server" timeout_ms = 3000 ``` The OTLP HTTP exporter appends `/v1/traces` automatically. ### Via Environment Variables ```bash ``` ## What to Expect in Jaeger UI After starting KalamDB with OTEL enabled: 1. Open `http://127.0.0.1:16686`. 2. Select the service name from the dropdown (default: `kalamdb-server`). 3. Click **Find Traces**. 4. You should see traces for API requests, SQL execution, and storage operations. Each trace shows the full request lifecycle — from API entry through SQL parsing, query execution, and storage I/O. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | No service in Jaeger dropdown | OTEL not enabled or no traffic yet | Set `enabled = true`, execute some SQL, then refresh | | Service appears but no traces | Endpoint mismatch | Verify `protocol` matches the port: `grpc` → 4317, `http` → 4318 | | Connection refused | Jaeger not running or wrong port | Run `docker compose ps` to verify Jaeger is up | | Traces delayed | Batch export timeout | Default timeout is 3000ms; increase `timeout_ms` if network is slow | ## Related For all OTEL configuration options and environment variable overrides, see [OpenTelemetry (OTEL)](https://kalamdb.org/docs/server/configurations/otel). ================================================================================ # Keycloak URL: https://kalamdb.org/docs/server/integrations/keycloak Source: content/server/integrations/keycloak.mdx > Use Keycloak as the single configured OIDC issuer for KalamDB. This page shows the current generic OIDC model, not the removed provider-specific config surface. # Keycloak KalamDB no longer has Keycloak-specific auth configuration. Keycloak now fits the same single-provider OIDC model as Dex, Okta, Auth0, Entra ID, Google, and other standards-compliant issuers. Use the generic OIDC docs and set the issuer to your Keycloak realm URL. ## Example `server.toml` ```toml [auth] jwt_trusted_issuers = "kalamdb,https://keycloak.example.com/realms/myrealm" [auth.oidc] enabled = true display_name = "Keycloak" issuer = "https://keycloak.example.com/realms/myrealm" client_id = "kalamdb" scopes = ["openid", "email", "profile"] auto_provision = true default_role = "user" ``` The issuer URL must exactly match the `iss` claim in Keycloak tokens. ## What Changed Current external identities use the OIDC `sub` claim directly as the KalamDB `user_id`. If you need explicit persisted users or elevated roles, create them with `issuer` and `subject`: ```sql CREATE USER 'provider-subject' WITH OIDC '{"issuer":"https://keycloak.example.com/realms/myrealm","subject":"provider-subject"}' ROLE dba EMAIL 'alice@example.com'; ``` ## Related - [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc) - [Authentication & Bootstrap](https://kalamdb.org/docs/server/getting-started/authentication) - [OIDC Model](https://kalamdb.org/docs/server/auth/providers) ================================================================================ # MinIO (S3-Compatible) URL: https://kalamdb.org/docs/server/integrations/minio Source: content/server/integrations/minio.mdx > Use MinIO with KalamDB S3 storage. Covers CONFIG fields, storage factory behavior, Docker Compose setup, timeout tuning, and bucket configuration. # MinIO (S3-Compatible) KalamDB uses the S3 storage backend for MinIO. You configure MinIO through `CREATE STORAGE ... TYPE 's3'` and the storage `CONFIG` JSON. ## Quick Start with Docker Compose The KalamDB repository includes a MinIO service in `docker/utils/docker-compose.yml`: ```yaml minio: image: minio/minio:latest environment: MINIO_ROOT_USER: minioadmin MINIO_ROOT_PASSWORD: minioadmin MINIO_CI: "on" ports: - "9120:9000" # S3 API - "9121:9001" # Console UI volumes: - ./minio-data:/data ``` Start it: ```bash docker compose up minio -d ``` MinIO Console: `http://127.0.0.1:9121` (login: `minioadmin` / `minioadmin`) S3 API Endpoint: `http://127.0.0.1:9120` The compose entrypoint auto-creates a `kalamdb-test` bucket and sets public anonymous access for development. ## S3 CONFIG Fields For S3-compatible backends (including MinIO), KalamDB supports these `CONFIG` JSON fields: | Field | Type | Default | Notes | |---|---|---|---| | `type` | string | — | Must be `"s3"` | | `region` | string | `"us-east-1"` | Falls back to `us-east-1` in factory if omitted | | `endpoint` | string | `null` | Custom endpoint for MinIO / S3-compatible | | `allow_http` | boolean | `false` | Must be `true` for non-TLS endpoints | | `access_key_id` | string | `null` | Static credentials | | `secret_access_key` | string | `null` | Static credentials | | `session_token` | string | `null` | Temporary session credentials | ## Example: Register MinIO Storage ```sql CREATE STORAGE s3_minio TYPE 's3' NAME 'MinIO Local Storage' DESCRIPTION 'MinIO bucket for local development' BASE_DIRECTORY 's3://kalamdb-test/' CONFIG '{ "type": "s3", "region": "us-east-1", "endpoint": "http://127.0.0.1:9120", "allow_http": true, "access_key_id": "minioadmin", "secret_access_key": "minioadmin" }' SHARED_TABLES_TEMPLATE '{namespace}/{tableName}' USER_TABLES_TEMPLATE '{namespace}/{tableName}/{userId}'; ``` ## S3 Factory Behavior When a custom `endpoint` is set (as with MinIO), the factory applies special handling: - **Path-style requests**: `virtual_hosted_style_request` is set to `false` automatically. This is required for MinIO and most S3-compatible services. - **Timeout skip**: `ClientOptions` (request/connect timeouts from `[storage.remote_timeouts]`) are **not applied** to S3-compatible endpoints. This avoids timeout conflicts with non-AWS backends. Timeouts only apply to standard AWS S3. - **Region fallback**: If `region` is omitted, it defaults to `"us-east-1"`. ## Notes on CONFIG - Use an `s3://...` `BASE_DIRECTORY` for S3-compatible storage. - `allow_http = true` is required for any non-TLS endpoint (local MinIO, test environments). - The `type` field in the CONFIG JSON must match the `TYPE` in the SQL command. ## Remote Timeout Tuning (`server.toml`) Remote object store timeouts are configured globally in `server.toml`: ```toml [storage.remote_timeouts] request_timeout_secs = 60 connect_timeout_secs = 10 ``` > **Important**: These timeouts apply to **standard AWS S3 only**. They are skipped for S3-compatible endpoints (MinIO, etc.) to avoid conflicts. ## Credential Placement Recommended options: - **Development**: Put credentials in `CONFIG` JSON directly. - **Production (AWS S3)**: Use IAM instance roles or environment-based AWS credential chains — omit `access_key_id`/`secret_access_key` from CONFIG. - **Production (MinIO)**: Use scoped credentials; rotate regularly. - Avoid committing production secrets to source control. ## Verify Storage Health After creating the storage, verify connectivity: ```sql STORAGE CHECK s3_minio; STORAGE CHECK s3_minio EXTENDED; ``` ## Other Cloud Storage Backends KalamDB also supports GCS and Azure Blob Storage. See [/docs/server/sql-reference/storage](https://kalamdb.org/docs/server/sql-reference/storage) for all backend types and CONFIG formats. ## Related Resources - [/docs/server/sql-reference/storage](https://kalamdb.org/docs/server/sql-reference/storage) - [/docs/server/sql-reference/storage-id-usage](https://kalamdb.org/docs/server/sql-reference/storage-id-usage) - [/docs/server/architecture/storage-tiers](https://kalamdb.org/docs/server/architecture/storage-tiers) - [/docs/server/architecture/manifests](https://kalamdb.org/docs/server/architecture/manifests) ================================================================================ # v0.5.3-rc.1 Release URL: https://kalamdb.org/docs/server/release-v0-5-3-rc-1 Source: content/server/release-v0-5-3-rc-1.mdx > KalamDB v0.5.3-rc.1 pre-release notes for the June 17 CLI workflow update, benchmark improvement, and docs destinations. # KalamDB v0.5.3-rc.1 Release KalamDB v0.5.3-rc.1 is the June 17 pre-release for the local CLI project workflow: `kalam init`, `kalam dev`, schema apply, generated SDK artifacts, and supervised app logs. ## v0.5.3-rc.1 Performance Benchmark The synced benchmark artifact for `0.5.3-rc.1` reports **38/38 benchmarks passed** with **213,667.71 ms measured benchmark time**. That is **8.0% faster measured benchmark time** than `0.5.2-rc.1`, which reported 232,155.85 ms. - [Open the v0.5.3-rc.1 HTML benchmark report](https://kalamdb.org/benchmark/results/bench-2026-06-17-131609-0-5-3-rc-1.html) - [View all synced benchmark reports](https://kalamdb.org/docs/server/benchmarking) - [Open the GitHub release](https://github.com/kalamdb/KalamDB/releases/tag/v0.5.3-rc.1) ## CLI Project Workflow The release makes the CLI project loop the recommended path for local development: 1. Run [Project Init](https://kalamdb.org/docs/cli/init) to create `kalam.toml`, `schema.sql`, migrations, optional starter files, and package-manager wiring. 2. Run [Local Development](https://kalamdb.org/docs/cli/dev) to start or reuse a KalamDB server, apply schema changes, regenerate SDK code, and stream supervised process logs. 3. Use [Migrations](https://kalamdb.org/docs/cli/migrations) when schema changes need a committed rollout path. 4. Run `kalam db reset` to clear local server data (`kalam/server/data`, `kalam/server/logs`) when you need a fresh database on the next `kalam dev`. Local projects scaffolded with `--server-mode local` use `root` / `kalamdb123` in `kalam/server/server.toml` and `.env`. ```bash mkdir my-agent-app && cd my-agent-app kalam init --yes --name my-agent-app --languages typescript --package-manager pnpm kalam dev ``` To start over: ```bash kalam db reset kalam dev ``` ## Release Post For the narrative announcement with the CLI workflow image, benchmark summary, and release links, read the [KalamDB v0.5.3-rc.1 pre-release blog post](https://kalamdb.org/blog/kalamdb-v0-5-3-rc-1-pre-release). ================================================================================ # v0.5.4-rc.1 Release URL: https://kalamdb.org/docs/server/release-v0-5-4-rc-1 Source: content/server/release-v0-5-4-rc-1.mdx > KalamDB v0.5.4-rc.1 pre-release notes for INSERT RETURNING, ON CONFLICT upsert, Live OKF Context Sync, and CLI and auth hardening. # KalamDB v0.5.4-rc.1 Release KalamDB v0.5.4-rc.1 is the June 25 pre-release for PostgreSQL-style `INSERT ... RETURNING` and `ON CONFLICT` upsert, the Live OKF Context Sync example, and CLI, auth, and test hardening. ## SQL: INSERT ... RETURNING `INSERT` statements can now return affected rows directly in the SQL response. **Supported syntax:** ```sql INSERT INTO users (id, name, age) VALUES (1, 'Nader', 3) RETURNING *; INSERT INTO users (id, name, age) VALUES (1, 'Nader', 3) RETURNING id, name; ``` **Behavior:** - `RETURNING *` returns all table columns in schema order - `RETURNING col1, col2, ...` returns a selected projection - Works for single-row and multi-row `INSERT` - API responses include the returned rows in the standard `results[].rows` shape - Parameterized inserts support `RETURNING` over HTTP and through the SDKs ## SQL: INSERT ... ON CONFLICT (upsert) PostgreSQL-style upsert is supported on the primary key conflict target. ```sql INSERT INTO users (id, name, age) VALUES (1, 'Nader Updated', 5) ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, age = EXCLUDED.age RETURNING *; INSERT INTO users (id, name, age) VALUES (1, 'Ignored', 99) ON CONFLICT (id) DO NOTHING RETURNING *; ``` | Feature | Behavior | | --- | --- | | `ON CONFLICT (pk) DO UPDATE SET ...` | Updates existing row; unassigned columns are preserved | | `EXCLUDED.column` | References the proposed insert values | | `ON CONFLICT (pk) DO NOTHING` | Skips conflicting rows | | `DO UPDATE ... WHERE` | Applies update only when the predicate is true | | `RETURNING` with `DO NOTHING` | Returns zero rows for skipped conflicts | | Multi-row upsert | One returned row per input row (insert or update) | | User tables | Conflicts are scoped per user (RLS isolation) | | Shared tables | Conflicts are global on the primary key | **Limitations (current release):** - Conflict target must be the table's **primary key** column - `ON CONFLICT ON CONSTRAINT` is not supported - Non-primary-key unique targets are rejected with a clear error ## TypeScript ORM (`@kalamdb/orm`) Drizzle-style upsert and returning now work end-to-end: ```typescript const rows = await db .insert(users) .values({ id: 1, name: 'Nader', age: 3 }) .returning(); const updated = await db .insert(users) .values({ id: 1, name: 'Nader Updated', age: 5 }) .onConflictDoUpdate({ target: users.id, set: { name: sql`excluded.name`, age: sql`excluded.age` }, }) .returning(); ``` FILE uploads through Drizzle use `kalamFile(name, blob)` in `.values()` / `.set()`. `kalamDriver()` detects upload params and routes to multipart `queryWithFiles()` — no separate wrapper per insert: ```typescript await db.insert(attachments).values({ id: 'att_1', file_data: kalamFile('upload', selectedFile), }); ``` See [Live OKF Context Sync](https://kalamdb.org/docs/use-cases/live-okf-context-sync) for a folder-sync example that upserts FILE columns with `onConflictDoUpdate`. ## Live OKF Context Sync example A new showcase app under `examples/live-okf-context-sync/` demonstrates syncing a **Google Open Knowledge Format (OKF)** folder into KalamDB for multi-client AI agent context. - Per-user OKF Markdown folders synced into KalamDB via a Node sync worker - File metadata in a user-scoped SQL table; bytes via KalamDB `FILE` upload/download - Local folder watch (push) + `liveTable()` pull with a SQLite hash cache under `.index/` - Schema-first workflow: `kalam/schema.sql` is the source of truth; TypeScript types are generated via `@kalamdb/orm` - Live subscriptions so every sync worker sees metadata changes in realtime - Delete local `data/` and restart — files restore from the server (KalamDB is the source of truth) Docs: [Live OKF Context Sync](https://kalamdb.org/docs/use-cases/live-okf-context-sync) ## CLI and dev workflow - **`kalam dev` improvements** — stronger prechecks for auth, paths, and server readiness before starting dev processes - **Project init refactor** — cleaner scaffolding for TypeScript projects and connection URL handling - **Self-update** — more reliable version comparison (version first, then build date for same-version rebuilds) - **Update-check tests** — version-bump-proof; no longer require editing hardcoded release numbers on each release - **`run-tests.sh`** — improved running-server credential validation and supplementary suite orchestration ## Security and auth - **OIDC bearer auth fix** — closes a repository bypass where OIDC bearer tokens could skip expected authorization checks - **Auth endpoint hardening** — login, setup, and OIDC exchange paths tightened for consistency ## Upgrade notes 1. **Rebuild or update** server and CLI to `0.5.4-rc.1` 2. **TypeScript projects** — bump `@kalamdb/*` packages together; run `npm install` to refresh lockfiles 3. **Upsert adoption** — replace read-then-write patterns with `ON CONFLICT ... DO UPDATE` where appropriate 4. **RETURNING** — use instead of a follow-up `SELECT` after inserts when you need the written row 5. **OKF example** — requires `kalam dev` (starts server, applies schema, generates ORM types) - [Open the GitHub release](https://github.com/kalamdb/KalamDB/releases/tag/v0.5.4-rc.1) - [Full changelog](https://github.com/kalamdb/KalamDB/compare/v0.5.3-rc.1...v0.5.4-rc.1) ## Release post For the narrative announcement with SQL examples, the OKF context sync summary, and release links, read the [KalamDB v0.5.4-rc.1 pre-release blog post](https://kalamdb.org/blog/kalamdb-v0-5-4-rc-1-pre-release). ================================================================================ # SDK Version Compatibility URL: https://kalamdb.org/docs/server/sdk/compatibility Source: content/server/sdk/compatibility.mdx > Compare KalamDB server, pg_kalam, TypeScript, Dart, Flutter, and Rust SDK versions to choose supported release pairings for apps. # SDK Version Compatibility Use this SDK version compatibility table to choose the KalamDB server, pg_kalam extension, and client SDK versions that belong together for a deployment. ## Compatibility Matrix ## Selection Guidance - For new apps, start with the latest server docs and latest SDK docs unless you are pinned to a specific release candidate. - Keep the server, pg_kalam extension, and SDKs on the same row when you need repeatable staging or production builds. - When upgrading one component across rows, run a SQL request, an authenticated request, and a realtime subscription smoke test before rolling forward. ## Related Docs - [TypeScript SDK setup](https://kalamdb.org/docs/ts-sdk/setup) - [Dart / Flutter SDK setup](https://kalamdb.org/docs/dart-sdk/setup) - [pg_kalam SQL syntax](https://kalamdb.org/docs/pg-kalam/sql-syntax) - [HTTP API reference](https://kalamdb.org/docs/server/api/http-reference) ================================================================================ # SDKs & Client Libraries URL: https://kalamdb.org/docs/server/sdk Source: content/server/sdk/index.mdx > Official KalamDB client libraries. Connect with TypeScript, Dart/Flutter, Rust, Python SDK docs, or the HTTP and WebSocket APIs. # SDKs & Client Libraries > **Status:** TypeScript, Dart/Flutter, and Rust SDKs are documented and available today. The Rust crate [`kalam-client`](https://crates.io/crates/kalam-client) is published on crates.io (beta). Python SDK docs exist for async SQL and live queries, but package publishing may still be in progress. KalamDB client libraries: - [TypeScript SDK](https://kalamdb.org/docs/ts-sdk) for browser/Node.js apps and worker services - [Dart / Flutter SDK](https://kalamdb.org/docs/dart-sdk) for mobile and cross-platform app clients - [Rust SDK](https://kalamdb.org/docs/rust-sdk) — [`kalam-client` on crates.io](https://crates.io/crates/kalam-client) for native async Rust services and topic workers - [Python SDK](https://kalamdb.org/docs/python-sdk) for async SQL and live query examples; see [Python SDK status](https://kalamdb.org/docs/server/sdk/python) before relying on package installation ## Which One To Start With - Building frontend + backend JS/TS apps: start at [TypeScript Setup](https://kalamdb.org/docs/ts-sdk/setup) - Building Dart/Flutter apps: start at [Dart Setup](https://kalamdb.org/docs/dart-sdk/setup) - Building Rust workers/services: start at [Rust SDK](https://kalamdb.org/docs/rust-sdk) - Building Python services today: review [Python SDK](https://kalamdb.org/docs/python-sdk), then fall back to [HTTP API Reference](https://kalamdb.org/docs/server/api/http-reference) if you need a stable package install path ## Shared Concepts Across SDKs - JWT bearer auth for SQL and realtime flows - SQL execution over `/v1/api/sql` - WebSocket realtime subscriptions over `/v1/ws` - Topic consume/ack for worker processing - FILE column uploads and downloads (`FileRef`, `BoundFileRef`, `TableId` in Rust; `BoundFileRef` in TypeScript) - Vector search through regular SQL (`EMBEDDING`, `COSINE_DISTANCE`, and indexed queries) For protocol-level integration details, see [API Reference](https://kalamdb.org/docs/server/api). ================================================================================ # Python SDK Status URL: https://kalamdb.org/docs/server/sdk/python Source: content/server/sdk/python/index.mdx > Check the KalamDB Python SDK status, current docs, and fallback HTTP/WebSocket API paths before relying on package installation. # Python SDK Status KalamDB has Python SDK docs for async SQL helpers and live query streams: - [Python SDK overview](https://kalamdb.org/docs/python-sdk) - [Python Live Queries](https://kalamdb.org/docs/python-sdk/subscriptions) Release metadata lists a `kalamdb` Python SDK package for the current `0.5.3-rc.1` line, but the public package install path is not linked from the site config yet. Treat Python package publishing as in-progress until the docs include a concrete install command. ## Current Recommendation For Python Services For production integrations that need a stable install path, use the protocol docs directly: - [HTTP API Reference](https://kalamdb.org/docs/server/api/http-reference) - [WebSocket Protocol](https://kalamdb.org/docs/server/api/websocket-protocol) - [Authentication & Bootstrap](https://kalamdb.org/docs/server/getting-started/authentication) For Python-facing examples, use the Python SDK docs above as the API shape reference, then verify package availability before shipping. ## Expected Scope - SQL execution over the HTTP API - JWT authentication helpers - realtime subscriptions - topic consume/ack worker APIs - FILE column upload helpers ================================================================================ # Security & Best Practices URL: https://kalamdb.org/docs/server/security Source: content/server/security/index.mdx > Operational security baseline for KalamDB deployments. Learn about auth hardening, TLS, CORS controls, and abuse protection. # Security This chapter is for operators deploying KalamDB in staging/production. ## Security Baseline Checklist 1. Serve API behind HTTPS (TLS at edge proxy/load balancer). 2. Set strong `auth.jwt_secret` and keep it out of source control. 3. Keep `auth.allow_remote_setup = false` after initial bootstrap. 4. Restrict CORS and WebSocket origins to known domains. 5. Keep rate limiting and request-size limits enabled. 6. Restrict setup/health/admin-sensitive routes to trusted networks. 7. For clusters, enable `cluster.rpc_tls` (mTLS between nodes). ## Baseline Config Example ```toml [auth] jwt_secret = "replace-with-strong-random-secret-32-plus-chars" cookie_secure = true allow_remote_setup = false [rate_limit] enable_connection_protection = true max_auth_requests_per_ip_per_sec = 20 max_requests_per_ip_per_sec = 200 max_connections_per_ip = 100 [security] max_request_body_size = 10485760 max_ws_message_size = 1048576 strict_ws_origin_check = true allowed_ws_origins = ["https://app.example.com"] [security.cors] allowed_origins = ["https://app.example.com", "https://admin.example.com"] allow_credentials = true ``` ## High-Risk Misconfigurations To Avoid - Wildcard browser origins in production - Disabled rate-limit middleware in public deployments - Short/static JWT secrets shared across environments - Leaving remote setup enabled permanently - Exposing cluster RPC ports publicly ## Incident Response Priorities 1. Rotate compromised JWT secrets/certificates 2. Disable compromised users/service accounts 3. Tighten ingress and rate limits during active abuse 4. Preserve logs for forensic review ## Related Docs - [Production Checklist](https://kalamdb.org/docs/server/security/production-checklist) - [Authentication Providers](https://kalamdb.org/docs/server/auth) - [Token Auth](https://kalamdb.org/docs/server/auth/token-auth) - [Auto-Provisioning Users](https://kalamdb.org/docs/server/auth/auto-provisioning) - [Firebase](https://kalamdb.org/docs/server/integrations/firebase) - [Rate Limiting](https://kalamdb.org/docs/server/security/rate-limits) - [Advanced Configuration](https://kalamdb.org/docs/server/configurations/advanced) - [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc) - [Keycloak](https://kalamdb.org/docs/server/integrations/keycloak) - [OpenTelemetry (OTEL)](https://kalamdb.org/docs/server/configurations/otel) ================================================================================ # Production Security Checklist URL: https://kalamdb.org/docs/server/security/production-checklist Source: content/server/security/production-checklist.mdx > Step-by-step checklist and hardened configuration reference for running KalamDB safely in production. Covers secrets, tokens, CORS, TLS, RBAC, rate limits, cluster mTLS, and incident response. # Running KalamDB in Production This guide walks you through every setting, secret, and network boundary you need to lock down before exposing a KalamDB server to the public internet or an untrusted network. Work through the checklist top-to-bottom. Each section is independently actionable — you can turn a single item on and restart the server between steps. > **Threat model assumption**: attackers can reach your HTTP and cluster RPC ports. Your defenses must not rely on network isolation alone. --- ## Quick Checklist Copy this into your deploy runbook. Every box must be ticked before you open the port. ### Network & TLS - [ ] `server.host = "127.0.0.1"` (or explicitly a private interface); never `0.0.0.0` unless you need remote access - [ ] Terminate TLS at an edge proxy (nginx, Caddy, ALB, Cloudflare) — KalamDB itself serves plain HTTP - [ ] HTTP server sits behind a firewall/security-group that only allows the edge proxy - [ ] Cluster RPC port (`cluster.rpc_addr`) is **not** reachable from the public internet - [ ] For multi-node clusters: `rpc_tls.enabled = true` with `require_client_cert = true` ### Secrets - [ ] `auth.jwt_secret` is at least 32 bytes, random, unique per environment - [ ] JWT secret is injected via env var or secret manager, **never** committed to source - [ ] Root/admin password is changed from any default and stored in a secret manager - [ ] `KALAMDB_ROOT_PASSWORD` env var is cleared from the shell history after seeding - [ ] OAuth/OIDC client secrets are in env vars, not the config file ### Auth & RBAC - [ ] `auth.allow_remote_setup = false` after first-run bootstrap - [ ] Every human user has a named account, not the built-in `root` - [ ] Service accounts use the `service` role, never `dba`/`system` - [ ] Password policy tuned for local auth (`auth.local.min_password_length`, `auth.local.bcrypt_cost`) - [ ] Access token expiry ≤ 1 hour; refresh token expiry ≤ 7 days ### Origins & Cookies - [ ] `security.cors.allowed_origins` is an explicit allowlist — no `"*"` - [ ] `security.cors.allow_credentials = true` **only** when origins are an explicit allowlist - [ ] `security.strict_ws_origin_check = true` - [ ] Auth cookies served over HTTPS: `auth.cookie_secure = true`, `SameSite=Strict`, `HttpOnly` ### Abuse Controls - [ ] `rate_limit.enable_connection_protection = true` - [ ] `rate_limit.max_auth_requests_per_ip_per_sec` tuned (start at 10–20) - [ ] `rate_limit.max_connections_per_ip` tuned (start at 100) - [ ] `security.max_request_body_size` tuned to your real upload size - [ ] `security.max_ws_message_size` tuned to your real message size ### Observability - [ ] Audit logs shipped to a separate write-only sink (e.g., SIEM, S3 with object lock) - [ ] Metrics endpoint exposed only to the internal monitoring network - [ ] Alerts configured on: failed logins/min, 401/403 rate, new user creation, role changes --- ## 1. Bind Addresses & TLS KalamDB does not terminate TLS itself. Run it behind a reverse proxy that provides HTTPS and forwards to KalamDB on localhost. ### Recommended topology ``` [ client ] ──HTTPS──► [ nginx/Caddy/ALB ] ──HTTP(loopback)──► [ kalamdb :2900 ] [ kalamdb :2910 cluster mTLS ] ``` ### Config ```toml [server] host = "127.0.0.1" # loopback only; proxy handles remote clients port = 2900 [cluster] # Bind cluster RPC to the private cluster network interface only. rpc_addr = "10.0.1.15:2910" [rpc_tls] enabled = true require_client_cert = true ca_cert = "/etc/kalamdb/tls/cluster-ca.pem" server_cert = "/etc/kalamdb/tls/node.pem" server_key = "/etc/kalamdb/tls/node.key" ``` If your cluster spans more than loopback nodes, KalamDB will refuse to start without `rpc_tls.enabled = true`. Do not work around this check. --- ## 2. JWT Secrets & Token Hygiene ### Secret requirements - **Length**: minimum 32 bytes (64+ recommended) - **Source**: cryptographic RNG (`openssl rand -base64 48`), not a passphrase - **Rotation**: rotate on compromise, personnel change, or at least annually - **Scope**: unique per environment (dev, staging, prod never share) Generate and inject via environment: ```bash ``` ```toml [auth] # Leave unset in the file when you set KALAMDB_AUTH_JWT_SECRET in the env. # jwt_secret = "..." access_token_expiry_hours = 1 refresh_token_expiry_hours = 168 # 7 days ``` KalamDB refuses to start on non-loopback binds if the secret is empty, a known-weak placeholder, or shorter than 32 bytes. ### Rotation procedure 1. Generate a new secret. 2. Deploy it to every node simultaneously. 3. Bounce the service. 4. All outstanding tokens will fail verification — clients must re-login. > Do not re-use an old secret. There is no "grace period" key ring; rotation is abrupt by design. ### Where tokens are exposed | Surface | Carries token? | Notes | |---|---|---| | `Authorization: Bearer …` header | Yes | Preferred for server-to-server | | Auth cookies | Yes | Browser flows; always set `Secure`, `HttpOnly`, `SameSite=Strict` | | WebSocket subprotocol | Yes | Sent once during upgrade; never in URL query | | Logs | **No** | Log redaction is enabled by default; keep it enabled | --- ## 3. Admin Bootstrap & Setup On first boot, KalamDB seeds a `root` user. What happens next depends on your configuration. ### Option A — Seeded password (recommended for remote deployments) ```bash # run the server once so the password is hashed and persisted # then unset the env var and store the password in your secret manager unset KALAMDB_ROOT_PASSWORD ``` ### Option B — Remote setup endpoint (one-time) If you cannot set the env var, allow the setup endpoint for the first call only: ```toml [auth] allow_remote_setup = true # flip back to false immediately after setup ``` **Set `allow_remote_setup = false` and restart after you complete setup.** Leaving it enabled lets anyone who can reach the endpoint attempt to seed credentials. ### Option C — Localhost-only root If you only administer via an SSH tunnel, leave the password empty. KalamDB will refuse remote logins for `root` and only accept localhost calls. Create named admin users via `CREATE USER` for remote access. --- ## 4. Password Policy Minimum controls enforced by KalamDB: - Minimum length (configurable via `auth.local.min_password_length`) - Max length (configurable via `auth.local.max_password_length`; bcrypt still uses the first 72 bytes) - Bcrypt hash with configurable cost (`auth.local.bcrypt_cost`, default `12`) - Optional complexity rules via `auth.local.enforce_password_complexity` - Rejected common-password list - Constant-time verification - Generic error messages (no user enumeration) Recommended runtime: ```toml [auth.local] enabled = true min_password_length = 12 bcrypt_cost = 12 enforce_password_complexity = false ``` Push stricter composition requirements (for example character classes) through `auth.local.enforce_password_complexity` or your external identity provider. KalamDB keeps complexity off by default to avoid false rejections of passphrases. --- ## 5. CORS & WebSocket Origins ### CORS for the browser admin UI ```toml [security.cors] allowed_origins = [ "https://admin.example.com", "https://app.example.com", ] allow_credentials = true allowed_methods = ["GET", "POST", "OPTIONS"] allowed_headers = ["Authorization", "Content-Type"] max_age = 600 ``` Hard rules: - Never combine `allowed_origins = ["*"]` with `allow_credentials = true`. Browsers reject it, and KalamDB will refuse to start with that combination. - Use scheme + host + port — origin matching is exact. - Never include localhost origins in production config. ### WebSocket origins ```toml [security] strict_ws_origin_check = true allowed_ws_origins = ["https://app.example.com"] ``` `strict_ws_origin_check = true` rejects connections that omit the `Origin` header (non-browser tooling must set it explicitly). Leave empty `allowed_ws_origins` only on loopback dev installs. --- ## 6. Rate Limiting & Connection Protection Even with auth in place, rate limits protect against credential stuffing, SQL abuse, and connection exhaustion. ```toml [rate_limit] enable_connection_protection = true # Auth endpoints (login, refresh, setup, WS auth) max_auth_requests_per_ip_per_sec = 10 # Per-IP concurrent connection cap max_connections_per_ip = 100 # Pre-auth request flood protection max_requests_per_ip_per_sec = 200 # Per-user query rate (SQL endpoint) max_queries_per_sec = 100 # WebSocket message flood protection max_messages_per_sec = 50 # Temporary ban window for abusive IPs ban_duration_seconds = 300 ``` If you run behind a reverse proxy, configure `security.trusted_proxy_ranges` so that rate limits attribute requests to the real client IP via `X-Forwarded-For`. **Do not** set this to `0.0.0.0/0` — that lets any caller spoof any IP and bypass the limits. ```toml [security] trusted_proxy_ranges = ["10.0.0.0/8", "172.16.0.0/12"] ``` --- ## 7. RBAC & Least Privilege KalamDB has four roles. Use the lowest one that works. | Role | Typical use | Can do | |---|---|---| | `user` | App end-users, SDK clients | Read/write their own namespaces, run DML | | `service` | Backend services, pub/sub consumers | DML + topic consume/ack | | `dba` | Database administrators | DDL, manage users except `system` | | `system` | Reserved for the server itself | Everything (do not create new ones) | Rules of thumb: - Application tokens should be `user` or `service`, never `dba`. - Create one `dba` per human administrator; audit their usage. - `EXECUTE AS ''` follows the role hierarchy: system can target system/DBA/service/user, DBA can target DBA/service/user, service can target service/user, and regular users can only target themselves. - System tables (`system.*`) are read/write restricted to `dba` and `system`. This is enforced inside the query planner, including through subqueries, CTEs, and views. ### On role demotion When you demote or lock a user, rotate their tokens. KalamDB re-validates the DB role on each token refresh and invalidates tokens whose `token_generation` is older than the DB row — but already-issued access tokens remain valid until `access_token_expiry_hours`. Keep access-token TTLs short. --- ## 8. Request Size Guards & File Uploads ```toml [security] max_request_body_size = 10485760 # 10 MiB; tune to real payload size max_ws_message_size = 1048576 # 1 MiB ``` For file-accepting endpoints (multipart `FILE("name")` placeholders, exports), KalamDB: - Validates path components against an allowlist (alphanumeric, `-`, `_`, `.`) - Canonicalises paths and rejects symlink escape - Returns 403 to non-owners trying to download another user's files or exports Operators should additionally: - Put an object-storage lifecycle on the exports bucket (e.g., delete after N days) - Scan uploaded files with your AV/virus pipeline before re-serving them to users --- ## 9. Cluster RPC Hardening For anything beyond a single-node loopback cluster: ```toml [rpc_tls] enabled = true require_client_cert = true ca_cert = "/etc/kalamdb/tls/cluster-ca.pem" server_cert = "/etc/kalamdb/tls/node-1.pem" server_key = "/etc/kalamdb/tls/node-1.key" [cluster] rpc_addr = "10.0.1.15:2910" # private interface only ``` Notes: - The cluster RPC port carries Raft consensus, `ForwardSql`, `Ping`, and node-info RPCs. Treat it as equally sensitive to the data port. - `ForwardSql` additionally re-validates the caller's Bearer JWT on the receiving node — mTLS is defense-in-depth, not the only layer. - Use a private CA unique to the cluster. Do not reuse your edge-TLS certificate chain for cluster mTLS. - Rotate node certificates independently of JWT secrets. --- ## 10. Logging, Audit & Observability - KalamDB emits structured JSON logs. SQL statements are redacted before logging; do not re-enable raw SQL in log formatters. - Enable the audit stream to capture user logins, user creation/modification, role changes, impersonation events, and admin export downloads. - Ship audit logs to an append-only sink (S3 with object lock, SIEM). - Expose Prometheus metrics on the internal network only — never on the public API port. ### Suggested alerts - `auth_failed_logins_per_minute > 20` - `auth_403_forbidden_per_minute > 50` - `user_role_change_events > 0` (human review on every change) - `impersonation_events` grouped by actor - Cluster RPC connection count deviation > 3σ --- ## 11. Incident Response Playbook When credentials, a token secret, or a node are suspected compromised: 1. **Contain** — rotate `auth.jwt_secret`. All outstanding tokens become invalid immediately. 2. **Lock down** — set `auth.allow_remote_setup = false`, revoke compromised user accounts (`ALTER USER … LOCK`). 3. **Rotate** — regenerate cluster mTLS material, redeploy, force clients to re-authenticate. 4. **Narrow** — temporarily lower `max_auth_requests_per_ip_per_sec` and `max_connections_per_ip`. 5. **Preserve** — snapshot logs, audit stream, and RocksDB backup before further changes. 6. **Review** — audit `system.users` for unexpected role changes, `system.jobs` for unexpected admin jobs, exports directory for unexpected downloads. 7. **Post-mortem** — document the breach path, fix the configuration gap, add a regression check. --- ## 12. High-Risk Misconfigurations To Avoid KalamDB will refuse to start on several of these. The rest are human errors we see most often. - `server.host = "0.0.0.0"` with no edge proxy and default JWT secret - `security.cors.allowed_origins = ["*"]` combined with `allow_credentials = true` - `security.strict_ws_origin_check = false` on a public WebSocket endpoint - `auth.allow_remote_setup = true` left on after bootstrap - `auth.jwt_secret` shared across environments, or shorter than 32 bytes - `security.trusted_proxy_ranges` containing `0.0.0.0/0` or `::/0` - Running a multi-node cluster with `rpc_tls.enabled = false` - Handing out `dba` or `system` role tokens to applications - Storing secrets in `server.toml` committed to Git --- ## Baseline Production `server.toml` Drop-in starting point. Fill in real values, inject secrets via env. ```toml [server] host = "127.0.0.1" port = 2900 enable_http2 = true [auth] # jwt_secret intentionally omitted — supplied via KALAMDB_JWT_SECRET allow_remote_setup = false cookie_secure = true jwt_expiry_hours = 24 [auth.local] enabled = true min_password_length = 12 bcrypt_cost = 12 enforce_password_complexity = false [rate_limit] enable_connection_protection = true max_auth_requests_per_ip_per_sec = 10 max_connections_per_ip = 100 max_requests_per_ip_per_sec = 200 max_queries_per_sec = 100 max_messages_per_sec = 50 ban_duration_seconds = 300 [security] max_request_body_size = 10485760 max_ws_message_size = 1048576 strict_ws_origin_check = true allowed_ws_origins = ["https://app.example.com"] trusted_proxy_ranges = ["10.0.0.0/8"] [security.cors] allowed_origins = ["https://admin.example.com", "https://app.example.com"] allow_credentials = true allowed_methods = ["GET", "POST", "OPTIONS"] allowed_headers = ["Authorization", "Content-Type"] max_age = 600 [rpc_tls] enabled = true require_client_cert = true ca_cert = "/etc/kalamdb/tls/cluster-ca.pem" server_cert = "/etc/kalamdb/tls/node.pem" server_key = "/etc/kalamdb/tls/node.key" [cluster] rpc_addr = "10.0.1.15:2910" ``` --- ## Related Docs - [Security Overview](https://kalamdb.org/docs/server/security) - [Rate Limiting](https://kalamdb.org/docs/server/security/rate-limits) - [Authentication](https://kalamdb.org/docs/server/auth) - [Token Auth](https://kalamdb.org/docs/server/auth/token-auth) - [OIDC & Issuer Trust](https://kalamdb.org/docs/server/configurations/oidc) - [Advanced Configuration](https://kalamdb.org/docs/server/configurations/advanced) - [OpenTelemetry (OTEL)](https://kalamdb.org/docs/server/configurations/otel) ================================================================================ # Rate Limiting URL: https://kalamdb.org/docs/server/security/rate-limits Source: content/server/security/rate-limits.mdx > How to tune KalamDB rate-limit settings for SQL queries, auth endpoints, WebSocket connections, and connection abuse controls in production. # Rate Limiting KalamDB applies rate limiting at SQL, auth, WebSocket, and per-IP connection layers. ## Key Settings | Key | Scope | |-----|-------| | `rate_limit.max_queries_per_sec` | per-user SQL request rate | | `rate_limit.max_messages_per_sec` | per-WebSocket incoming message rate | | `rate_limit.max_subscriptions_per_user` | active live subscriptions per user | | `rate_limit.max_auth_requests_per_ip_per_sec` | login/setup/refresh abuse protection | | `rate_limit.max_connections_per_ip` | concurrent socket cap per IP | | `rate_limit.max_requests_per_ip_per_sec` | pre-auth request flood protection | | `rate_limit.request_body_limit_bytes` | request payload size guard | | `rate_limit.ban_duration_seconds` | temporary ban window | | `rate_limit.enable_connection_protection` | master switch for connection guard | ## Practical Starting Point ```toml [rate_limit] max_queries_per_sec = 100 max_messages_per_sec = 50 max_subscriptions_per_user = 10 max_auth_requests_per_ip_per_sec = 20 max_connections_per_ip = 100 max_requests_per_ip_per_sec = 200 request_body_limit_bytes = 10485760 ban_duration_seconds = 300 enable_connection_protection = true cache_max_entries = 100000 cache_ttl_seconds = 600 ``` ## Environment Strategy - development: permissive but enabled (so behavior stays realistic) - staging: mirror production values - production: strict thresholds + alerts on violations ## Tuning Workflow 1. collect baseline traffic per endpoint and user role 2. set limits just above normal peaks 3. alert on near-limit and over-limit events 4. adjust independently for auth, SQL, and WebSocket traffic ## Signals To Monitor - spikes in `429` responses - repeated bans for same source ranges - auth endpoint bursts - large WebSocket fan-out patterns with rising rejection counts ================================================================================ # Backup & Restore URL: https://kalamdb.org/docs/server/sql-reference/backup Source: content/server/sql-reference/backup.mdx > Database backup and restore commands in KalamDB. Learn how to create namespace backups, schedule exports, and restore data from backup archives. # Backup & Restore KalamDB provides built-in SQL commands for backing up and restoring the **entire database**. A backup captures everything in one compressed archive — RocksDB data, Parquet storage files, Raft snapshots, and the server configuration — so you always have a consistent, self-contained snapshot you can restore from. > **Role requirement**: `BACKUP DATABASE` and `RESTORE DATABASE` require the > **DBA** or **System** role. --- ## BACKUP DATABASE Create a full database backup. The entire data directory and `server.toml` are compressed into a single `.tar.gz` archive at the specified path. ```sql BACKUP DATABASE TO ''; ``` The command enqueues a background backup job and returns immediately with a **Job ID** you can use to monitor progress. ### Parameters | Parameter | Description | |---|---| | `backup_path` | Absolute path for the output archive. Must end in `.tar.gz`. Quotes (single or double) are required. | ### Archive contents | Path inside archive | Description | |---|---| | `data/rocksdb/` | RocksDB write-path data | | `data/storage/` | Flushed Parquet segment files | | `data/snapshots/` | Raft snapshots | | `data/streams/` | Stream commit log data | | `server.toml` | Server configuration (if present) | ### Example ```sql -- Back up the entire database to a timestamped archive BACKUP DATABASE TO '/backups/kalamdb_20260224.tar.gz'; -- Store to a date-named path BACKUP DATABASE TO '/var/backups/kalamdb/2026-02-24.tar.gz'; ``` **Response** ``` Database backup started to '/backups/kalamdb_20260224.tar.gz'. Job ID: BK-0001 ``` --- ## RESTORE DATABASE Restore the entire database from a previously created `.tar.gz` backup archive. The command stages the restored data under `_restore_pending/` without touching the live database. **A server restart is required to complete the restore** — on startup, KalamDB detects the pending directory and swaps it in. ```sql RESTORE DATABASE FROM ''; ``` > **Warning**: Once the server restarts after staging, all data written since the > backup was taken will be lost. Take a fresh backup of the current state before > running a restore if you need a rollback option. ### Parameters | Parameter | Description | |---|---| | `backup_path` | Absolute path to the `.tar.gz` backup archive to restore from. Quotes (single or double) are required. | ### Restore lifecycle 1. Issue `RESTORE DATABASE FROM '';` — returns a **Job ID**. 2. The job extracts the archive to `_restore_pending/`. 3. Job status transitions to `Completed` with the message *"Restore staged from '…'. RocksDB restore is pending server restart."* 4. **Restart the KalamDB server** — it detects the pending directory, swaps it in, and starts fresh. ### Example ```sql -- Stage a restore from a specific timestamped backup RESTORE DATABASE FROM '/backups/kalamdb_20260224.tar.gz'; ``` **Response** ``` Database restore started from '/backups/kalamdb_20260224.tar.gz'. Job ID: RS-0001 ``` After the job completes: ```sql -- Confirm the restore job reached Completed before restarting SELECT job_id, status, message FROM system.jobs WHERE job_id = 'RS-0001'; ``` Then restart the server process to activate the restored data. --- ## Path security rules KalamDB validates the backup path on both `BACKUP` and `RESTORE` to prevent path traversal and sensitive file access: - `..` sequences are **not allowed** (blocks path traversal) - Null bytes (`\0`) are **not allowed** - Writing to `/etc/`, `/root/`, `/var/log/`, or `C:\Windows\` is **blocked** - The path must be quoted (bare paths are rejected) ```sql -- These will be rejected: BACKUP DATABASE TO '../../../tmp/evil.tar.gz'; -- path traversal BACKUP DATABASE TO '/etc/shadow'; -- sensitive directory BACKUP DATABASE TO /backups/app.tar.gz; -- unquoted path ``` --- ## Async job execution Backup and restore operations run as **background jobs** managed by the `UnifiedJobManager`. The SQL command returns immediately with a job ID; the work happens asynchronously so the server remains responsive. You can monitor job status via the `system.jobs` table: ```sql SELECT job_id, status, message FROM system.jobs WHERE job_id = 'BK-0001'; ``` | `status` value | Meaning | |---|---| | `Queued` | Job is waiting to start | | `Running` | Backup/restore is in progress | | `Completed` | Finished successfully | | `Failed` | An error occurred — see `message` for details | --- ## Backup strategy For production deployments, consider: 1. **Scheduled backups** — Run `BACKUP DATABASE` on a cron or application-level schedule (daily, hourly, etc.) 2. **Timestamped archives** — Include a date/time in the path so each backup is uniquely named and old ones are not silently overwritten 3. **Remote storage** — Copy the `.tar.gz` archive to S3, GCS, or Azure Blob Storage after creation for off-site durability 4. **Snapshot + backup** — Combine `CLUSTER SNAPSHOT` with `BACKUP DATABASE` for a fully consistent cluster-wide state capture 5. **Pre-restore snapshot** — Take a backup of the current state before running `RESTORE DATABASE` so you can roll back if needed ================================================================================ # Cluster Operations URL: https://kalamdb.org/docs/server/sql-reference/cluster Source: content/server/sql-reference/cluster.mdx > CLI-first cluster inspection and administration in KalamDB, including list, snapshots, elections, purging, rebalance, join, and snapshot cleanup. # Cluster Operations Cluster inspection and administration are documented on the CLI page first. The CLI renders the structured SQL results returned by the server, which keeps operational output out of backend string-formatting paths. See [Interactive Commands — cluster](https://kalamdb.org/docs/cli/interactive-commands) for the supported operator commands and [SQL Workflows — follower write forwarding](https://kalamdb.org/docs/cli/workflows) for the end-to-end flow when a write lands on a follower node. For the Multi-Raft architecture, see [/docs/server/architecture/clustering](https://kalamdb.org/docs/server/architecture/clustering). ## Inspect cluster state Use the CLI for node and group views: ```bash kalam --command "\\cluster list" kalam --command "\\cluster list groups" ``` For raw tabular access, query the system views directly: ```sql SELECT * FROM system.cluster ORDER BY is_leader DESC, node_id ASC; SELECT * FROM system.cluster_groups ORDER BY group_id ASC; ``` `CLUSTER LIST`, `CLUSTER STATUS`, and `CLUSTER LS` are no longer documented SQL commands. Use `\cluster list` or the `system.cluster` views instead. ## Run cluster operations Use the CLI cluster meta-commands documented in [Interactive Commands](https://kalamdb.org/docs/cli/interactive-commands). Those commands still use the SQL transport under the hood, but the CLI owns the operator-facing rendering. ## Automation note The underlying SQL transport is still what the CLI calls, so automation can keep using the cluster SQL statements when you want the raw row payload instead of CLI rendering. ## Unsupported or removed commands ```sql CLUSTER LEAVE; -- unsupported ``` `CLUSTER LEAVE` is still unsupported. For list-style inspection, use `\cluster list` or the system views instead of old `CLUSTER LIST`/`STATUS`/`LS` SQL forms. `CLUSTER TRANSFER LEADER` is still routed by the backend, but some builds can report it as unsupported at runtime depending on the OpenRaft version in use. ## Related Architecture - [/docs/server/architecture/clustering](https://kalamdb.org/docs/server/architecture/clustering) - [/docs/server/architecture/live-query](https://kalamdb.org/docs/server/architecture/live-query) - [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types) ================================================================================ # Schema & Query Diagnostics URL: https://kalamdb.org/docs/server/sql-reference/diagnostics Source: content/server/sql-reference/diagnostics.mdx > Inspect table schemas with DESCRIBE and inspect query plans with EXPLAIN, ANALYZE, and VERBOSE. Includes KalamDB hot/cold storage scan metrics. # Schema & Query Diagnostics Use these commands to inspect table schemas and query execution plans. They are intended for operators, DBAs, and developers tuning SQL against KalamDB tables. For storage-tier background on plan metrics, see [/docs/server/architecture/storage-tiers](https://kalamdb.org/docs/server/architecture/storage-tiers). ## DESCRIBE — table schema KalamDB supports two DESCRIBE styles. Both answer “what columns does this table have?”, but they use different syntax, authorization, and result shapes. ### `DESCRIBE TABLE` (KalamDB native) ```sql DESCRIBE TABLE [.]; DESC TABLE [.]; -- Schema version history DESCRIBE TABLE [.] HISTORY; ``` **Access:** authenticated users with an elevated role (DBA or System). Regular `User` role statements are rejected at execution time. **Returns:** rich column metadata from the schema registry: | Column | Description | |--------|-------------| | `column_name` | Column name | | `ordinal_position` | 1-based position in the table | | `column_id` | Parquet field id | | `data_type` | Kalam SQL type name (for example `TEXT`, `BIGINT`, `TIMESTAMP`) | | `is_nullable` | Whether the column allows `NULL` | | `is_primary_key` | Whether the column is part of the primary key | | `column_default` | Default expression, if any | | `column_comment` | Column comment, if any | | `schema_version` | Schema version when the column was added | **Example:** ```sql DESCRIBE TABLE dba.notifications; ``` ### `DESCRIBE
` / `DESC
` (DataFusion shorthand) ```sql DESCRIBE [.]; DESC [.]; ``` **Access:** DBA or System only (admin meta command). **Behavior:** KalamDB rewrites the statement to query `information_schema.columns` and returns Kalam SQL type names (`kdb_data_type`) instead of Arrow physical types: | Column | Description | |--------|-------------| | `column_name` | Column name | | `data_type` | Kalam SQL type (`kdb_data_type`) | | `is_nullable` | `YES` / `NO` | Unqualified table names resolve against the session’s current namespace (`USE NAMESPACE`, CLI `ns:` context, or API `namespace_id`). **Example:** ```sql USE NAMESPACE dba; DESCRIBE notifications; ``` ### CLI shortcut In the interactive shell, `\d
` and `\describe
` run an `information_schema.columns` query with Kalam type names — the same columns as the shorthand above, plus `namespace`, `column_default`, and `position`: ```sql \d dba.notifications \describe notifications ``` See [CLI querying](https://kalamdb.org/docs/cli/querying) for output formats. --- ## EXPLAIN — query plans `EXPLAIN` shows how DataFusion plans a query without executing it (unless `ANALYZE` is present). **Access:** DBA or System only. ### Syntax (keyword order matters) DataFusion expects this order: ```sql EXPLAIN [ANALYZE] [VERBOSE] [FORMAT ] ; ``` | Keyword | Position | Effect | |---------|----------|--------| | `EXPLAIN` | required | Start the diagnostic command | | `ANALYZE` | optional, **before** `VERBOSE` | Execute the query and attach runtime metrics | | `VERBOSE` | optional, after `ANALYZE` | Add extra result rows (full metrics, row count, duration) when combined with `ANALYZE` | | `FORMAT` | optional | Plan rendering style (`indent` default, `tree`, `pgjson`) | **Common mistake:** `EXPLAIN VERBOSE ANALYZE` is **not** valid. Use `EXPLAIN ANALYZE VERBOSE` instead. With the wrong order, the parser treats `ANALYZE` as a separate statement and fails. ### `EXPLAIN` (plan only) Returns two rows — logical plan and physical plan — without running the query: ```sql EXPLAIN SELECT id, user_id FROM dba.notifications WHERE user_id = 'test' LIMIT 3; ``` Example output columns: | plan_type | plan | |-----------|------| | `logical_plan` | Optimized logical plan tree | | `physical_plan` | Physical operators (`FilterExec`, `GlobalLimitExec`, `DeferredBatchExec`, …) | ### `EXPLAIN ANALYZE` (plan + runtime metrics) Executes the query, discards result rows to the client, and returns an annotated physical plan with per-operator metrics: ```sql EXPLAIN ANALYZE SELECT id, user_id, _deleted FROM dba.notifications WHERE user_id = 'test' LIMIT 3; ``` Returns one primary row: | plan_type | plan | |-----------|------| | `Plan with Metrics` | Physical plan tree with timing and counter metrics | On `USER` and `SHARED` tables, KalamDB enables **scan diagnostics** for `EXPLAIN ANALYZE`. Look for `DeferredBatchExec` nodes and metrics such as: | Metric | Meaning | |--------|---------| | `hot_rows_scanned` | Rows read from the hot tier (RocksDB) | | `cold_rows_scanned` | Rows read from cold Parquet files | | `cold_files_total` | Cold files considered by manifest pruning | | `cold_files_skipped` | Cold files skipped by pruning | | `cold_files_scanned` | Cold files actually read | | `cold_file_visited{file=…}` | Individual Parquet files touched (up to 16 recorded) | Plan text also includes static scan context on deferred MVCC sources, for example: `storage_tiers=[hot=rocksdb,cold=parquet], mvcc=true`. ### `EXPLAIN ANALYZE VERBOSE` (full metrics + summary) Adds extra rows after the annotated plan: ```sql EXPLAIN ANALYZE VERBOSE SELECT id, user_id, _deleted FROM dba.notifications WHERE user_id = 'test' LIMIT 3; ``` | plan_type | plan | |-----------|------| | `Plan with Metrics` | Annotated plan with standard metrics | | `Plan with Full Metrics` | Plan with all available metric detail | | `Output Rows` | Rows produced by the query | | `Duration` | Total execution time | ### Reading plans for KalamDB tables Typical physical plans on application tables include: - **`DeferredBatchExec`** — deferred MVCC scan (hot RocksDB + cold Parquet merge) - **`FilterExec`** — predicate evaluation (pushdown where possible) - **`ProjectionExec`** — column selection - **`GlobalLimitExec`** / **`LocalLimitExec`** — `LIMIT` handling - **`CooperativeExec`** — stream table scans Use plain `EXPLAIN` to compare logical vs physical shape without execution cost. Use `EXPLAIN ANALYZE` when you need to see whether filters hit the hot tier, how many cold files were scanned, and operator timings. --- ## CLI output tips In default **table** format, the CLI renders `plan_type` / `plan` result sets specially: - Multi-line plan text is expanded across table rows (continuation lines leave `plan_type` blank) - The `plan` column uses the full terminal width instead of the generic 32-character cap For machine-readable output, use JSON: ```bash kalam --json -c "EXPLAIN ANALYZE SELECT 1;" ``` Or in the interactive shell: ```sql \format json EXPLAIN ANALYZE VERBOSE SELECT id FROM dba.notifications LIMIT 3; ``` ## Related - [Table DDL — DESCRIBE TABLE](https://kalamdb.org/docs/server/sql-reference/tables#describe-table) - [Query Data (SELECT & JOIN)](https://kalamdb.org/docs/server/sql-reference/query-data) - [Storage tiers](https://kalamdb.org/docs/server/architecture/storage-tiers) - [CLI querying](https://kalamdb.org/docs/cli/querying) ================================================================================ # Data Manipulation URL: https://kalamdb.org/docs/server/sql-reference/dml Source: content/server/sql-reference/dml.mdx > KalamDB data manipulation language (DML) reference. Learn INSERT, UPDATE, DELETE, PostgreSQL-style upsert with ON CONFLICT DO UPDATE, and RETURNING syntax for USER, SHARED, and STREAM tables. # Data Manipulation (DML) Standard SQL data manipulation commands for reading and writing data. DML permission and routing behavior depends on table type. For `USER`, `SHARED`, `STREAM`, and `SYSTEM` access rules, see [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types). ## INSERT Insert one or more rows into a table: ```sql INSERT INTO [.] (, , ...) VALUES (, , ...); ``` ### Batch Insert ```sql INSERT INTO [.] (, , ...) VALUES (, , ...), (, , ...); ``` ### Examples ```sql -- Single insert with auto-generated ID INSERT INTO chat.messages (conversation_id, sender, content) VALUES (1, 'alice', 'Hello!'); -- Batch insert INSERT INTO chat.messages (conversation_id, sender, role, content) VALUES (1, 'alice', 'user', 'What is KalamDB?'), (1, 'assistant', 'assistant', 'KalamDB is a SQL-first realtime database.'); ``` ## Upsert with `ON CONFLICT` KalamDB supports PostgreSQL-style upsert for literal `INSERT ... VALUES` statements on `USER`, `SHARED`, and `STREAM` tables: ```sql INSERT INTO [.] (, , ...) VALUES (, , ...) ON CONFLICT () DO UPDATE SET = EXCLUDED., = ; ``` If the primary key already exists, KalamDB updates the row with the `DO UPDATE SET` assignments. If the primary key is new, KalamDB inserts the row from `VALUES`. ### Examples ```sql -- Update an existing shared row by primary key INSERT INTO app.items (id, name) VALUES (1, 'beta') ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name; -- Insert when the primary key is missing, update when it already exists INSERT INTO app.items (id, name) VALUES (42, 'gamma') ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name; ``` `DO UPDATE SET` assignments support: - `EXCLUDED.` to read the value from the attempted insert - literal constants such as `'published'`, `42`, or `NULL` ### Upsert inside explicit transactions Upsert works inside `BEGIN` / `COMMIT` blocks and sees rows staged earlier in the same transaction, including on `SHARED` tables: ```sql BEGIN; INSERT INTO app.items (id, name) VALUES (2, 'alpha'); INSERT INTO app.items (id, name) VALUES (2, 'gamma') ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name; COMMIT; ``` Outside an explicit transaction, KalamDB wraps each upsert in its own internal transaction and commits it automatically. ### Upsert rules and limits - Requires a single-column primary key. The conflict target must be that primary key column. - Literal `VALUES` inserts only. `INSERT ... SELECT` upserts are not supported on this path. - `ON CONFLICT DO NOTHING`, `ON CONFLICT ON CONSTRAINT`, and `ON CONFLICT DO UPDATE WHERE` are not supported. - Tuple assignments in `DO UPDATE SET` are not supported. - `system.*` tables are rejected. ## `RETURNING` on upsert Add `RETURNING` to an upsert to get back the inserted or updated row instead of only an affected-row count. This follows PostgreSQL's `INSERT ... RETURNING` shape: the clause lists the output columns, and aliases are supported. ```sql INSERT INTO [.] () VALUES () ON CONFLICT () DO UPDATE SET RETURNING [, AS ...]; ``` ### Examples ```sql -- Return the final row after an update INSERT INTO app.items (id, name) VALUES (1, 'beta') ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name RETURNING id, name; -- Return a column alias INSERT INTO app.items (id, name) VALUES (1, 'beta') ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name RETURNING id, name AS returned_name; -- Return the inserted row when no conflict occurred INSERT INTO app.items (id, name) VALUES (42, 'alpha') ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name RETURNING id, name; ``` ### Response shape - Without `RETURNING`, the SQL API reports an insert result with `rows_affected`. - With `RETURNING`, the SQL API returns a query result with `schema`, `rows`, and `row_count`, the same success shape as `SELECT`. `RETURNING` is currently supported on the upsert path above. Plain `INSERT ... RETURNING` without `ON CONFLICT`, and `UPDATE` / `DELETE ... RETURNING`, are not supported yet. ## UPDATE ```sql UPDATE [.] SET = , = WHERE ; ``` ### Example ```sql UPDATE chat.messages SET content = 'Updated message content' WHERE id = 42; ``` ## DELETE ```sql DELETE FROM [.] WHERE ; ``` ### Example ```sql DELETE FROM chat.messages WHERE conversation_id = 1 AND sender = 'bot'; ``` ## Explicit Transactions KalamDB stays in autocommit mode by default. Use explicit transaction blocks when you need PostgreSQL-style atomic multi-statement writes. ### Syntax ```sql BEGIN; START TRANSACTION; COMMIT; COMMIT WORK; ROLLBACK; ROLLBACK WORK; ``` ### Commit example ```sql BEGIN; INSERT INTO chat.messages (conversation_id, sender, content) VALUES (7, 'alice', 'draft'); UPDATE chat.messages SET content = 'published' WHERE conversation_id = 7 AND sender = 'alice'; COMMIT; ``` ### Rollback example ```sql BEGIN; INSERT INTO chat.messages (conversation_id, sender, content) VALUES (8, 'alice', 'temporary'); SELECT * FROM chat.messages WHERE conversation_id = 8; ROLLBACK; ``` Within the open transaction, reads see the staged writes from that same transaction. After `ROLLBACK`, those staged changes are discarded. ### Rules and limits - Explicit transactions currently support `USER` and `SHARED` tables. - `STREAM` tables and `system.*` tables are rejected inside explicit transactions. - DDL statements are not supported inside explicit transactions. - Nested `BEGIN` and savepoints are not supported in this phase. - A single `/v1/api/sql` request can contain multiple sequential `BEGIN ... COMMIT` or `BEGIN ... ROLLBACK` blocks. - If a `/v1/api/sql` request ends with an open transaction, KalamDB rolls it back automatically. To inspect live transaction state, query `system.transactions`. For pg-extension-specific session state, use `system.sessions`. See [/docs/server/sql-reference/system-views](https://kalamdb.org/docs/server/sql-reference/system-views). For deeper read-query patterns (joins, CTEs, and DataFusion-compatible advanced SELECT usage), see [/docs/server/sql-reference/query-data](https://kalamdb.org/docs/server/sql-reference/query-data). ## Related Architecture - [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types) - [/docs/server/architecture/storage-tiers](https://kalamdb.org/docs/server/architecture/storage-tiers) - [/docs/server/architecture/live-query](https://kalamdb.org/docs/server/architecture/live-query) ================================================================================ # FILE Datatype URL: https://kalamdb.org/docs/server/sql-reference/file-datatype Source: content/server/sql-reference/file-datatype.mdx > How to define FILE columns in KalamDB tables, upload files with the FILE() function, and read or download stored file references using SQL and the HTTP API. # FILE Datatype KalamDB supports a `FILE` datatype for storing file references in table rows while the binary content is managed by the file service. Use this page to understand SQL usage. For architecture and storage-path layout, see [/docs/server/architecture/file-upload-datatype](https://kalamdb.org/docs/server/architecture/file-upload-datatype). ## Define FILE Columns Create a table with a `FILE` column: ```sql CREATE TABLE app.documents ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), title TEXT NOT NULL, attachment FILE, created_at TIMESTAMP DEFAULT NOW() ) WITH (TYPE = 'USER'); ``` `FILE` columns are supported on `USER` and `SHARED` tables. ## Insert with FILE(...) Use `FILE("placeholder")` in SQL and send multipart form parts named `file:`. ```sql INSERT INTO app.documents (id, title, attachment) VALUES (1001, 'Contract', FILE("contract")); ``` Multipart body parts: - `sql` - `params` (optional JSON array string) - `namespace_id` (optional) - `file:contract` (binary part for `FILE("contract")`) Backend behavior (API + file utils): - SQL multipart parser enforces file limits (`max_files_per_request`, `max_size_bytes`). - Placeholder names in `FILE("name")` must match multipart keys `file:name`. - On SQL failure after staging/finalization, cleanup routines attempt to remove uploaded files. ## Query FILE Columns Querying returns file-reference metadata in the row. ```sql SELECT id, title, attachment FROM app.documents WHERE id = 1001; ``` In SDKs, parse the returned file reference and bind table context before downloading: - **TypeScript:** `queryRows(..., 'app.documents')` then `row.file('attachment')` for a `BoundFileRef` with no-arg `downloadUrl()` - **Rust:** `TableId::from_strings("app", "documents")` then `cell.as_bound_file(&table_id)` and `download_bound_file()` - **Dart:** `KalamFileRef.getDownloadUrl(...)` with explicit namespace and table (see [Dart SDK querying](https://kalamdb.org/docs/dart-sdk/querying#file-columns)) ## Download Files Use the file endpoint: `GET /v1/files/{namespace}/{table_name}/{subfolder}/{file_id}` Notes: - Bearer token is required. - `SYSTEM` and `STREAM` tables are rejected for file paths. - `user_id` may be required for user-table scope resolution. Path model summary: - `USER` table downloads resolve using user scope (`user_id` effective context). - `SHARED` table downloads resolve without user scope. - Effective folder layout is controlled by storage templates (see architecture page). ## Best Practices - Use `USER` tables for tenant-private files. - Use `SHARED` tables for global/shared assets. - Store descriptive metadata in additional columns (e.g., `mime`, `label`, `tags`). - Fetch only required columns where possible (for example `SELECT id, attachment`). ## Related Docs - [/docs/server/api/http-reference](https://kalamdb.org/docs/server/api/http-reference) - [/docs/ts-sdk/file-columns](https://kalamdb.org/docs/ts-sdk/file-columns) - [/docs/rust-sdk/file-columns](https://kalamdb.org/docs/rust-sdk/file-columns) - [/docs/server/architecture/file-upload-datatype](https://kalamdb.org/docs/server/architecture/file-upload-datatype) - [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types) ================================================================================ # Built-in Functions URL: https://kalamdb.org/docs/server/sql-reference/functions Source: content/server/sql-reference/functions.mdx > KalamDB built-in SQL functions plus DataFusion-compatible scalar, aggregate, window, and PostgreSQL-style JSON query support. Includes SNOWFLAKE_ID, NOW, COSINE_DISTANCE, and more. # Built-in Functions KalamDB provides built-in functions for ID generation, user context, and timestamps. KalamDB query execution also supports DataFusion SQL functions. For advanced query-time function usage, see: - [DataFusion SQL Functions Overview](https://datafusion.apache.org/user-guide/sql/index.html) - [DataFusion Scalar Functions](https://datafusion.apache.org/user-guide/sql/scalar_functions.html) - [DataFusion Aggregate Functions](https://datafusion.apache.org/user-guide/sql/aggregate_functions.html) - [DataFusion Window Functions](https://datafusion.apache.org/user-guide/sql/window_functions.html) The functions on this page are KalamDB-specific functions you will use most often. ## ID Generation ### SNOWFLAKE_ID() Generate a unique 64-bit snowflake ID (time-ordered, globally unique): ```sql SELECT SNOWFLAKE_ID(); -- Returns: 7302451094528000001 ``` Commonly used as the default for primary keys: ```sql CREATE TABLE app.messages ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), content TEXT NOT NULL ); ``` ### UUID_V7() Generate a UUIDv7 (time-ordered UUID): ```sql SELECT UUID_V7(); -- Returns: 0193a5e0-7a1b-7000-8000-000000000001 ``` ### ULID() Generate a ULID (Universally Unique Lexicographically Sortable Identifier): ```sql SELECT ULID(); -- Returns: 01HQJK5M5R3YJQWXHN4QWXYN01 ``` ## Context Functions ### CURRENT_USER() Returns the username of the currently authenticated user: ```sql SELECT CURRENT_USER(); -- Returns: 'alice' ``` Useful for audit columns: ```sql CREATE TABLE app.audit_log ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), action TEXT NOT NULL, performed_by TEXT DEFAULT CURRENT_USER(), created_at TIMESTAMP DEFAULT NOW() ); ``` ## Date & Time ### NOW() Returns the current timestamp: ```sql SELECT NOW(); -- Returns: 2026-02-18T10:30:00.000Z ``` Commonly used as a default for timestamp columns: ```sql CREATE TABLE app.events ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), event_type TEXT NOT NULL, created_at TIMESTAMP DEFAULT NOW() ); ``` ## Vector Search ### COSINE_DISTANCE() Use `COSINE_DISTANCE(embedding_column, '[...]')` to rank rows by vector similarity. This is the primary KalamDB pattern for `EMBEDDING(n)` columns: pass the stored column as the first argument and a JSON array literal as the query vector. ```sql SELECT id, title FROM rag.documents_vectors ORDER BY COSINE_DISTANCE(doc_embedding, '[1.0,0.0,0.0]') LIMIT 5; ``` Typical flow: 1. Store embeddings in an `EMBEDDING(n)` column. 2. Create a cosine index with `ALTER TABLE ... CREATE INDEX ... USING COSINE`. 3. Use `ORDER BY COSINE_DISTANCE(...) LIMIT k` to fetch nearest matches. Notes: - The query vector must match the column dimension. - Lower distance means a closer match. - JSON query literals (for example `'[1.0,0.0,0.0]'`) use KalamDB's vector-search path and return `FLOAT`. - When both arguments are array/list values, KalamDB delegates to DataFusion 54's native `cosine_distance` and returns `DOUBLE`. - This is the main SQL entry point for vector search in KalamDB today. ## JSON Operators and Functions KalamDB registers DataFusion's JSON function package in every SQL session. That gives JSON columns the common PostgreSQL-style query syntax most users expect for extraction and existence checks. ```sql SELECT doc->'profile' AS profile_json, doc->>'status' AS status_text, doc->'items'->0 AS first_item, doc ? 'customer_id' AS has_customer_id FROM app.orders WHERE doc->>'status' = 'paid'; ``` Supported PostgreSQL-style operators in KalamDB SQL: | Syntax | Equivalent helper | Result | |---|---|---| | `json_col -> 'key'` or `json_col -> 0` | `json_get(...)` | JSON value | | `json_col ->> 'key'` or `json_col ->> 0` | `json_as_text(...)` | text | | `json_col ? 'key'` | `json_contains(...)` | `BOOLEAN` | Available JSON helpers from the registered DataFusion package: | Function | Use | |---|---| | `json_get` | Return a JSON value at a path | | `json_as_text` | Return a value at a path as text | | `json_get_json` | Return nested JSON as raw JSON text | | `json_get_str` | Return a string value | | `json_get_int` | Return an integer value | | `json_get_float` | Return a floating-point value | | `json_get_bool` | Return a boolean value | | `json_get_array` | Return an array value | | `json_contains` | Test whether a key or index exists | | `json_length` | Return the length of an array or object | | `json_object_keys` | Return the keys of a JSON object as an array | | `json_from_scalar` | Convert a scalar into a JSON value for composed expressions | Scope note: - KalamDB plans PostgreSQL-style `->`, `->>`, and `?` operators natively through DataFusion 54 and `datafusion-functions-json`. No SQL rewrite pass is applied for these operators. - Do not assume full PostgreSQL `jsonb` operator parity for operators such as `#>`, `#>>`, or `@>` yet. ## DataFusion 54 Query Extensions KalamDB runs on DataFusion 54 and enables additional query-time SQL beyond the KalamDB-specific functions above. ### Lambda array functions Use SQL lambda syntax with higher-order array functions: ```sql SELECT array_transform([1, 2, 3, 4, 5], x -> x * 10) AS scaled; SELECT array_transform( array_filter([1, 2, 3, 4, 5], x -> x > 2), x -> x * 10 ) AS filtered_scaled; SELECT array_any_match([1, 2, 3], x -> x > 2) AS has_large; ``` These work in general `SELECT` queries. They are not supported in `SUBSCRIBE TO` / live-query SQL, which keeps a narrower surface. ### Lateral joins DataFusion 54 adds basic lateral join support for correlated subqueries in the `FROM` clause: ```sql SELECT u.id, expanded.value FROM app.users AS u CROSS JOIN LATERAL ( SELECT array_transform(u.tags, t -> upper(t)) AS value ) AS expanded; ``` ### Comparison and subquery semantics DataFusion 54 also changes a few comparison behaviors worth knowing when porting SQL: - Numeric columns compared to string literals (for example `amount > '100'`) are coerced numerically, not lexicographically. - Uncorrelated scalar subqueries that return more than one row fail at execution with a cardinality error. For the full upstream function catalog, use the [DataFusion SQL docs](https://datafusion.apache.org/user-guide/sql/index.html). ## Function Summary | Function | Return Type | Description | |----------|-------------|-------------| | `SNOWFLAKE_ID()` | `BIGINT` | Time-ordered 64-bit unique ID | | `UUID_V7()` | `TEXT` | Time-ordered UUID v7 | | `ULID()` | `TEXT` | Lexicographically sortable unique ID | | `CURRENT_USER()` | `TEXT` | Authenticated username | | `NOW()` | `TIMESTAMP` | Current server timestamp | | `COSINE_DISTANCE(vector, query)` | `FLOAT` or `DOUBLE` | Vector similarity distance. JSON query literals return `FLOAT`; array/array inputs use native DataFusion 54 and return `DOUBLE`. | ## DataFusion Function Support KalamDB supports DataFusion SQL function execution for SELECT/query workloads, including scalar, aggregate, and window functions documented in DataFusion. If you need advanced expression behavior beyond KalamDB-specific built-ins, use the DataFusion function references linked above as the canonical guide. For end-to-end schema and index examples, see [/docs/server/architecture/vector-search](https://kalamdb.org/docs/server/architecture/vector-search). ================================================================================ # Execute As URL: https://kalamdb.org/docs/server/sql-reference/impersonation Source: content/server/sql-reference/impersonation.mdx > Use KalamDB EXECUTE AS '' wrapper syntax with the role-based delegation matrix. # Execute As `EXECUTE AS` is wrapper syntax for a single SQL statement. It switches USER-table or STREAM-table execution to a resolved target user ID only when the authenticated actor role is allowed to target that user's role. ## EXECUTE AS ```sql EXECUTE AS '' ( ); ``` ### Rules 1. The wrapper must contain **exactly one** SQL statement 2. The target user ID must be **single-quoted** 3. System users may target system, DBA, service, and user accounts 4. DBA users may target DBA, service, and user accounts 5. Service users may target service and user accounts 6. Regular users may only target themselves 7. The wrapper is valid for USER and STREAM tables; shared tables use their table policy directly 8. Legacy inline `... AS USER 'name'` syntax is **not supported** ### Examples **Authorized delegated query:** ```sql EXECUTE AS 'user_123' ( SELECT * FROM app.messages WHERE conversation_id = 42 ); ``` **Authorized delegated insert:** ```sql EXECUTE AS 'user_123' ( INSERT INTO app.messages (conversation_id, sender, role, content) VALUES (42, 'user_123', 'assistant', 'Processing complete') ); ``` **Authorized delegated delete:** ```sql EXECUTE AS 'user_123' ( DELETE FROM app.messages WHERE id = 123 ); ``` ## Use Cases | Scenario | Description | |----------|-------------| | **Service workers** | Write rows into a user's USER-table or STREAM-table partition through an explicit delegation boundary | | **Testing** | Assert allowed and denied role-matrix edges consistently | | **Audit trails** | Record the actor and target user for delegated operations | ================================================================================ # SQL Reference URL: https://kalamdb.org/docs/server/sql-reference Source: content/server/sql-reference/index.mdx > Complete SQL syntax reference for KalamDB. Learn how to manage namespaces, tables, DML, realtime subscriptions, users, and storage. # SQL Reference KalamDB is SQL-first with additional commands for realtime subscriptions, storage lifecycle, topics, and cluster operations. This chapter is organized by command category so you can jump directly to implementation details. For the architecture behind common SQL choices, start with [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types), [/docs/server/architecture/storage-tiers](https://kalamdb.org/docs/server/architecture/storage-tiers), and [/docs/server/architecture/live-query](https://kalamdb.org/docs/server/architecture/live-query). ## SQL Categories | Category | Description | |----------|-------------| | [/docs/server/sql-reference/namespaces](https://kalamdb.org/docs/server/sql-reference/namespaces) | create/drop/select namespace context | | [/docs/server/sql-reference/tables](https://kalamdb.org/docs/server/sql-reference/tables) | create/alter/drop tables and views | | [/docs/server/sql-reference/dml](https://kalamdb.org/docs/server/sql-reference/dml) | `INSERT`, `UPDATE`, `DELETE`, `SELECT`, upsert (`ON CONFLICT DO UPDATE`), `RETURNING` | | [/docs/server/sql-reference/query-data](https://kalamdb.org/docs/server/sql-reference/query-data) | query patterns, joins, CTEs, and DataFusion-compatible SELECT usage | | [/docs/server/sql-reference/diagnostics](https://kalamdb.org/docs/server/sql-reference/diagnostics) | `DESCRIBE` / `DESC` schema inspection and `EXPLAIN` / `ANALYZE` / `VERBOSE` query plans | | [/docs/server/sql-reference/system-views](https://kalamdb.org/docs/server/sql-reference/system-views) | `system.*` observability views including `system.sessions` and `system.transactions` | | [/docs/server/sql-reference/subscriptions](https://kalamdb.org/docs/server/sql-reference/subscriptions) | `SUBSCRIBE TO`, `KILL LIVE QUERY` | | [/docs/server/sql-reference/topic-pubsub](https://kalamdb.org/docs/server/sql-reference/topic-pubsub) | topic pub/sub SQL syntax for `CREATE TOPIC`, `CONSUME`, `ACK`, and consumer groups | | [/docs/server/sql-reference/users](https://kalamdb.org/docs/server/sql-reference/users) | create/alter/drop users and roles | | [/docs/server/sql-reference/storage](https://kalamdb.org/docs/server/sql-reference/storage) | storage backends, flush, compact, manifests | | [/docs/server/sql-reference/storage-id-usage](https://kalamdb.org/docs/server/sql-reference/storage-id-usage) | table-to-storage mapping patterns | | [/docs/server/sql-reference/cluster](https://kalamdb.org/docs/server/sql-reference/cluster) | raft and node operations | | [/docs/server/sql-reference/backup](https://kalamdb.org/docs/server/sql-reference/backup) | namespace backup/recovery commands | | [/docs/server/sql-reference/impersonation](https://kalamdb.org/docs/server/sql-reference/impersonation) | `EXECUTE AS ''` wrapper syntax | | [/docs/server/sql-reference/functions](https://kalamdb.org/docs/server/sql-reference/functions) | KalamDB built-ins plus DataFusion SQL function support | ## Statement Separator ```sql SELECT 1; SELECT 2; ``` ## Quick Example ```sql CREATE NAMESPACE IF NOT EXISTS chat; CREATE TABLE chat.messages ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), sender TEXT NOT NULL, content TEXT NOT NULL, created_at TIMESTAMP DEFAULT NOW() ) WITH (TYPE = 'USER', FLUSH_POLICY = 'rows:1000'); INSERT INTO chat.messages (sender, content) VALUES ('alice', 'Hello world!'); SELECT * FROM chat.messages ORDER BY created_at DESC LIMIT 20; ``` ================================================================================ # Namespaces URL: https://kalamdb.org/docs/server/sql-reference/namespaces Source: content/server/sql-reference/namespaces.mdx > SQL commands for managing KalamDB namespaces. Learn how to create, drop, list, and select namespace context for organizing tables and data. # Namespace Commands Namespaces provide logical grouping for tables, similar to schemas in other databases. ## CREATE NAMESPACE ```sql CREATE NAMESPACE ; CREATE NAMESPACE IF NOT EXISTS ; ``` **Example:** ```sql CREATE NAMESPACE IF NOT EXISTS chat; CREATE NAMESPACE IF NOT EXISTS analytics; ``` ## DROP NAMESPACE ```sql DROP NAMESPACE ; DROP NAMESPACE IF EXISTS ; DROP NAMESPACE CASCADE; DROP NAMESPACE IF EXISTS CASCADE; ``` The `CASCADE` option drops all tables within the namespace. **Example:** ```sql -- Drop only if empty DROP NAMESPACE analytics; -- Drop with all contained tables DROP NAMESPACE IF EXISTS analytics CASCADE; ``` ## ALTER NAMESPACE ```sql ALTER NAMESPACE SET DESCRIPTION ''; ``` **Example:** ```sql ALTER NAMESPACE chat SET DESCRIPTION 'Chat application tables'; ``` ## USE / SET NAMESPACE Set the default namespace for the current request or multi-statement batch: ```sql USE ; USE NAMESPACE ; SET NAMESPACE ; ``` In the interactive CLI, a successful `USE` also updates the CLI's local namespace so later requests automatically send `namespace_id`. **Example:** ```sql USE chat; -- Now you can reference tables without the namespace prefix SELECT * FROM messages; ``` ## SHOW NAMESPACES List all available namespaces: ```sql SHOW NAMESPACES; ``` ================================================================================ # Query Data (SELECT & JOIN) URL: https://kalamdb.org/docs/server/sql-reference/query-data Source: content/server/sql-reference/query-data.mdx > How to read data in KalamDB with SELECT queries, WHERE filters, GROUP BY, JOINs, CTEs, PostgreSQL-style JSON queries, and DataFusion-compatible advanced query patterns. # Query Data (SELECT & JOIN) Use this page for query-only SQL patterns in KalamDB. KalamDB supports the DataFusion SELECT query surface. For advanced query syntax and planner behavior, use the DataFusion SQL docs as the primary reference: - [https://datafusion.apache.org/user-guide/sql/select.html](https://datafusion.apache.org/user-guide/sql/select.html) - [https://datafusion.apache.org/user-guide/sql/index.html](https://datafusion.apache.org/user-guide/sql/index.html) The visible rows and join targets still follow KalamDB table-type authorization. `USER` and `STREAM` tables are scoped by effective user, `SHARED` tables use `ACCESS_LEVEL`, and `system.*` access is restricted. See [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types). ## DataFusion Statement Scope KalamDB uses DataFusion as the engine for query-time SQL, especially read/query workloads: - `SELECT` and projection/filter/order/limit patterns - joins (`INNER`, `LEFT`, `RIGHT`, `FULL`, `CROSS`, `USING`) - subqueries and `WITH` (CTE) - set operations such as `UNION` / `UNION ALL` - scalar/aggregate/window function execution in query plans KalamDB-specific operational SQL surfaces are implemented by KalamDB handlers instead of raw DataFusion SQL semantics, including: - DDL (`CREATE/ALTER/DROP` namespaces, tables, users, storage, topics) - topic/consumer commands (`CREATE TOPIC`, `ALTER TOPIC`, `CONSUME`, `ACK`, `RESET CONSUMER GROUP`) - live-query controls (`SUBSCRIBE TO`, `KILL LIVE QUERY`) ## Basic SELECT ```sql SELECT FROM [.] [WHERE ] [ORDER BY [ASC|DESC]] [LIMIT ]; ``` Example: ```sql SELECT id, sender, content, created_at FROM chat.messages WHERE conversation_id = 42 ORDER BY created_at DESC LIMIT 50; ``` ## Filtering, Grouping, and Aggregation ```sql SELECT sender, COUNT(*) AS message_count FROM chat.messages WHERE conversation_id = 42 GROUP BY sender HAVING COUNT(*) >= 10 ORDER BY message_count DESC; ``` ## JOIN Support KalamDB supports standard DataFusion join patterns, including: - `INNER JOIN` - `LEFT JOIN` - `RIGHT JOIN` - `FULL JOIN` - `CROSS JOIN` - `JOIN ... USING (...)` Example (`INNER JOIN`): ```sql SELECT m.id, m.content, u.email FROM chat.messages m INNER JOIN system.users u ON m.user_id = u.user_id WHERE m.conversation_id = 42; ``` Example (`LEFT JOIN`): ```sql SELECT c.id, c.title, m.content FROM chat.conversations c LEFT JOIN chat.messages m ON c.id = m.conversation_id ORDER BY c.id; ``` ## Subqueries and CTEs Common DataFusion query constructs are supported, including subqueries and CTEs. ```sql WITH ranked AS ( SELECT sender, COUNT(*) AS total_messages FROM chat.messages GROUP BY sender ) SELECT * FROM ranked WHERE total_messages >= 100 ORDER BY total_messages DESC; ``` ## Set Operations You can use DataFusion set operations such as `UNION` and `UNION ALL`. ```sql SELECT sender FROM chat.messages_2025 UNION ALL SELECT sender FROM chat.messages_2026; ``` ## PostgreSQL-style JSON Queries `JSON` and `JSONB` columns can be queried with the same common extraction syntax PostgreSQL users already know. ```sql SELECT doc->>'status' AS status, doc->'items'->0 AS first_item FROM app.orders WHERE doc->>'status' = 'paid' AND doc ? 'customer_id'; ``` Use `->` when you want a JSON value, `->>` when you want text, and `?` when you only need to test key existence. KalamDB also exposes DataFusion JSON helpers such as `json_get`, `json_as_text`, `json_length`, and `json_object_keys` for function-style access. For the full JSON function list and current operator coverage, see [/docs/server/sql-reference/functions](https://kalamdb.org/docs/server/sql-reference/functions). ## DataFusion 54 Query Features KalamDB 0.5.x runs on DataFusion 54. In addition to the core `SELECT` / `JOIN` / CTE surface above, general SQL queries can use: - **Lambda array functions** such as `array_transform`, `array_filter`, and `array_any_match` with `x -> expr` syntax - **Lateral joins** such as `CROSS JOIN LATERAL`, `INNER JOIN LATERAL`, and `LEFT JOIN LATERAL` - **Native JSON operators** (`->`, `->>`, `?`) planned directly by DataFusion without a KalamDB rewrite pass Subscription and live-query SQL intentionally keeps a narrower surface. Advanced DataFusion 54 features such as lambda arrays, lateral joins, `GROUP BY`, and joins are not available in `SUBSCRIBE TO` statements. See [/docs/server/sql-reference/subscriptions](https://kalamdb.org/docs/server/sql-reference/subscriptions). When porting SQL from other engines, note that DataFusion 54 compares numeric columns to string literals numerically (for example `5 > '100'` is false) and rejects uncorrelated scalar subqueries that return multiple rows. ## Advanced Query Reference For advanced SELECT features, rely on DataFusion SQL documentation: - [https://datafusion.apache.org/user-guide/sql/select.html](https://datafusion.apache.org/user-guide/sql/select.html) - [https://datafusion.apache.org/user-guide/sql/scalar_functions.html](https://datafusion.apache.org/user-guide/sql/scalar_functions.html) - [https://datafusion.apache.org/user-guide/sql/index.html](https://datafusion.apache.org/user-guide/sql/index.html) ## Related Architecture - [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types) - [/docs/server/architecture/datatypes](https://kalamdb.org/docs/server/architecture/datatypes) - [/docs/server/architecture/vector-search](https://kalamdb.org/docs/server/architecture/vector-search) ================================================================================ # Storage ID Usage URL: https://kalamdb.org/docs/server/sql-reference/storage-id-usage Source: content/server/sql-reference/storage-id-usage.mdx > How to register storage backends and assign STORAGE_ID in CREATE TABLE for shared and user tables. Includes S3, local, and multi-backend configuration. # Storage ID Usage Use this guide when you want to control where table data is stored, pick storage per table, and understand user-table routing behavior. For MinIO-specific S3-compatible setup, see [/docs/server/integrations/minio](https://kalamdb.org/docs/server/integrations/minio). For the storage architecture, see [/docs/server/architecture/storage-tiers](https://kalamdb.org/docs/server/architecture/storage-tiers), [/docs/server/architecture/manifests](https://kalamdb.org/docs/server/architecture/manifests), and [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types). ## Create storage backends first Before using `STORAGE_ID` in `CREATE TABLE`, the storage must exist in `system.storages`. ### Filesystem storage ```sql CREATE STORAGE local_archive TYPE filesystem NAME 'Local Archive' DESCRIPTION 'Filesystem storage for archived tables' PATH './data/storage/local-archive' SHARED_TABLES_TEMPLATE 'shared/{namespace}/{tableName}' USER_TABLES_TEMPLATE 'users/{namespace}/{tableName}/{userId}'; ``` `PATH` is supported for filesystem storage. `BASE_DIRECTORY` is also accepted. ### S3 storage ```sql CREATE STORAGE s3_prod TYPE s3 NAME 'Production S3' DESCRIPTION 'S3 bucket for production data' BUCKET 'my-kalamdb-prod-bucket' REGION 'us-east-1' SHARED_TABLES_TEMPLATE 'shared/{namespace}/{tableName}' USER_TABLES_TEMPLATE 'users/{namespace}/{tableName}/{userId}'; ``` For S3, you can use either: - `BUCKET 'bucket-name'` (optionally with `REGION`) - `BASE_DIRECTORY 's3://bucket/prefix'` Useful checks: ```sql SHOW STORAGES; SELECT storage_id, storage_type, storage_name FROM system.storages; ``` ## Use STORAGE_ID in CREATE TABLE KalamDB accepts `STORAGE_ID` in `WITH (...)` table options. ### Shared table with explicit storage ```sql CREATE TABLE app.config ( key TEXT PRIMARY KEY, value TEXT ) WITH ( TYPE = 'SHARED', STORAGE_ID = 's3_prod' ); ``` ### User table with explicit storage and flush policy ```sql CREATE TABLE app.messages ( id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), content TEXT, created_at TIMESTAMP DEFAULT NOW() ) WITH ( TYPE = 'USER', STORAGE_ID = 'local_archive', FLUSH_POLICY = 'rows:5000' ); ``` ### If STORAGE_ID is omitted If omitted, KalamDB resolves storage to `local` by default. ```sql CREATE TABLE app.metrics ( id BIGINT PRIMARY KEY, value DOUBLE ) WITH (TYPE = 'SHARED'); ``` ## Verify table storage metadata Inspect table metadata in `system.schemas`: ```sql SELECT namespace_id, table_name, table_type, storage_id, use_user_storage FROM system.schemas WHERE namespace_id = 'app' AND is_latest = true ORDER BY table_name; ``` ## Change table storage metadata For `USER` and `SHARED` tables, admins can update `STORAGE_ID` with `ALTER TABLE ... SET TBLPROPERTIES`: ```sql ALTER TABLE app.messages SET TBLPROPERTIES (STORAGE_ID = 's3_prod'); ``` The new storage ID must already exist in `system.storages`. ## Per-user storage routing (USE_USER_STORAGE) For `USER` tables, you can enable per-user storage routing metadata: ```sql CREATE TABLE app.geo_events ( event_id BIGINT PRIMARY KEY DEFAULT SNOWFLAKE_ID(), payload JSON, created_at TIMESTAMP DEFAULT NOW() ) WITH ( TYPE = 'USER', STORAGE_ID = 's3_prod', USE_USER_STORAGE = true ); ``` Rules: - `USE_USER_STORAGE` is valid only for `TYPE = 'USER'` - `STORAGE_ID` remains fallback storage for the table - User preference fields exist in `system.users` (`storage_mode`, `storage_id`) You can also toggle it after creation: ```sql ALTER TABLE app.geo_events SET TBLPROPERTIES (USE_USER_STORAGE = false); ``` ## Changing storage per user (current status) Current SQL behavior: - DML against `system.*` tables is blocked - `ALTER USER` currently supports only `SET PASSWORD`, `SET ROLE`, and `SET EMAIL` So there is no public SQL command yet to directly update a user's `storage_mode` or `storage_id`. Current developer workflow: - Set table-level routing with `STORAGE_ID` (optionally `USE_USER_STORAGE`) - For user-level overrides, use internal/admin backend flows ## Related Architecture - [/docs/server/architecture/storage-tiers](https://kalamdb.org/docs/server/architecture/storage-tiers) - [/docs/server/architecture/manifests](https://kalamdb.org/docs/server/architecture/manifests) - [/docs/server/architecture/table-types](https://kalamdb.org/docs/server/architecture/table-types) ## Common failures ### Unknown storage ID ```sql CREATE TABLE app.bad_example ( id INT PRIMARY KEY ) WITH (TYPE = 'SHARED', STORAGE_ID = 'does_not_exist'); ``` Expected error pattern: ```text Storage 'does_not_exist' does not exist ``` ### Invalid USE_USER_STORAGE on non-user table ```sql CREATE TABLE app.bad_shared ( id INT PRIMARY KEY ) WITH (TYPE = 'SHARED', USE_USER_STORAGE = true); ``` Expected error pattern: ```text USE_USER_STORAGE is only supported for USER tables ``` ================================================================================ # Storage Commands URL: https://kalamdb.org/docs/server/sql-reference/storage Source: content/server/sql-reference/storage.mdx > Managing storage backends in KalamDB. Learn commands for registering storage, flushing hot data to cold tier, compacting Parquet files, and manifests. # Storage Commands KalamDB supports multiple storage backends for cold-tier data. These commands manage storage configuration, health checks, and maintenance. For practical table-level storage selection (`STORAGE_ID`, `USE_USER_STORAGE`, and verification queries), see [/docs/server/sql-reference/storage-id-usage](https://kalamdb.org/docs/server/sql-reference/storage-id-usage). For S3-compatible local deployment setup, see [/docs/server/integrations/minio](https://kalamdb.org/docs/server/integrations/minio). For the underlying hot/cold architecture and manifest state machine, see [/docs/server/architecture/storage-tiers](https://kalamdb.org/docs/server/architecture/storage-tiers) and [/docs/server/architecture/manifests](https://kalamdb.org/docs/server/architecture/manifests). ## CREATE STORAGE ```sql CREATE STORAGE TYPE '' [NAME ''] [DESCRIPTION ''] [PATH ''] [BUCKET ''] [REGION ''] [BASE_DIRECTORY ''] [SHARED_TABLES_TEMPLATE '