Distributed PostgreSQL with a ledger you can verify.
KingresDB is a thin fork of PostgreSQL 18. It shards tables across servers, keeps tamper-evident ledger tables, and puts a web console on every node. One package: install it and the node has everything.
PostgreSQL LicenseRead and write on any nodeSharding lives in an extension, not the executor
kingres.buckets3 servers · 48 buckets
Illustration. In KingresDB the bucket of a key is compute_partition_hash_value(key) % bucket_count, the value PostgreSQL itself uses to store the row, so routing and storage cannot disagree.
18.7PostgreSQL base (tag REL_18_7), changed only through a short register of core patches
5,151TAP subtests behind kingres_shard 0.3, on real multi-node clusters
1package with the server, the extensions, kingresd and the console
Why KingresDB
Three additions to PostgreSQL. Everything else stays PostgreSQL.
Your SQL, types, indexes, extensions and tools keep working. KingresDB adds what one server cannot give you: more servers behind one database, a history that can be checked from the outside, and a console to run it all.
Sharding
kingres_shard
Tables are split into a fixed number of hash buckets spread over several servers. A statement that touches one key goes straight to the group that owns it. A statement that spans groups is answered by reading the others.
Ledger tables record every change in a hash chain of Merkle blocks. Keep one digest outside the database and anyone can later detect a rewritten history, or prove that a single row version was there.
Every server runs the same agent: a web console and a REST API to create or join a cluster and to manage databases, roles, servers, certificates and network access. No separate management host.
One function call turns a table into a distributed table.
Start on one node. Add servers when you need them. The application keeps talking SQL to whichever node it likes.
Distributed tables. Hash-partitioned by a column you choose; each shard group holds the partitions of its buckets.
Reference tables. Small tables kept in full on every node. Reads are local, writes reach every node in one transaction.
Colocation. Tables that share a distribution key land in the same buckets, so a join on the key runs inside one group.
DDL that travels.ALTER TABLE, CREATE INDEX and grants are propagated to every node atomically.
Create a cluster and distribute a table
-- on the first node (it becomes the map group primary)
CREATE EXTENSION kingres_shard;
SELECT kingres.init_cluster('host1', 5432);
-- each new primary is a new shard group
SELECT kingres.add_node('host2', 5432);
SELECT kingres.add_node('host3', 5432);
-- distribute a table, and keep a small one everywhere
CREATE TABLE users (id int, name text, PRIMARY KEY (id));
SELECT kingres.create_distributed_table('users', 'id');
CREATE TABLE countries (code text PRIMARY KEY, name text);
SELECT kingres.create_reference_table('countries');
-- from any node
INSERT INTO users VALUES (42, 'ada');
SELECT name FROM users WHERE id = 42; -- one group
SELECT count(*) FROM users; -- every group
EXPLAIN SELECT name FROM users WHERE id = 42; -- shows where it runs
Add a server and move buckets onto it
-- a fourth server joins as a new, empty shard group
SELECT kingres.add_node('host4', 5432);
-- which moves would even out the buckets per group?
SELECT * FROM kingres.rebalance_plan();
-- move one bucket: copy, catch up with logical replication,
-- then a two-phase switch (detach on the source, attach on the target)
SELECT kingres.move_bucket(1, 17, 3); -- colocation, bucket, target group
-- drain a server before removing it
SELECT kingres.set_node_property(4, false); -- should_have_shards
-- one row per inconsistency; no rows when the cluster is sound
SELECT * FROM kingres.check_cluster();
SELECT * FROM kingres.buckets;
Put a table under ledger control and verify it
CREATE EXTENSION kingres_ledger;
-- append-only by default; 'updatable' keeps every old version provable
SELECT kingres.ledger_enable('public.accounts');
SELECT kingres.ledger_enable('public.balances', 'updatable');
INSERT INTO accounts VALUES (1, 'ada', 100); -- captured by a trigger
-- a background worker builds a block about every 10 seconds
SELECT kingres.ledger_generate_digest(); -- store the result OUTSIDE the database
-- later: is the chain intact, and does it match the digest I kept?
SELECT * FROM kingres.ledger_verify(); -- no rows when consistent
SELECT kingres.ledger_verify_digest('{...}');
-- prove that one row version is part of the ledger
SELECT kingres.ledger_inclusion_proof('accounts', kingres.ledger_row_hash(a.*))
FROM accounts a WHERE id = 1;
Distinct counts, top-n and percentiles in bounded memory
CREATE EXTENSION kingres_sketch;
-- HyperLogLog: how many distinct visitors?
SELECT kingres_sketch.approx_count_distinct(user_id) FROM events;
-- t-digest: the 99th percentile of latency
SELECT kingres_sketch.approx_percentile(0.99, latency_ms) FROM requests;
-- Space-Saving: the ten most requested paths
SELECT * FROM kingres_sketch.topn_list(
(SELECT kingres_sketch.topn_add_agg(path) FROM requests), 10);
-- sketches are values: store one per day, merge them later
SELECT kingres_sketch.hll_cardinality(kingres_sketch.hll_union_agg(visitors))
FROM daily_rollup WHERE day >= '2026-11-01';
Create partitions ahead of time and retire old ones
CREATE EXTENSION kingres_timepart;
CREATE TABLE events (at timestamptz NOT NULL, payload jsonb)
PARTITION BY RANGE (at);
-- daily partitions, a week ahead, dropped after 90 days
SELECT kingres.timepart_set_policy('events',
partition_every => '1 day',
create_ahead => '7 days',
retention => '90 days');
SELECT * FROM kingres.timepart_plan('events'); -- what a run would do
CALL kingres.timepart_run('events'); -- do it, with a run history
SELECT * FROM kingres.timepart_partitions('events');
SELECT * FROM kingres.timepart_policies;
What is in the box
Built as extensions on a small, documented fork.
The sharding and ledger logic live in extensions. The fork itself stays thin, so following upstream PostgreSQL stays cheap.
Statement routing
A planner hook sends single-key statements to the owning group as a Kingres Remote Statement. On a one-group cluster nothing changes.
Shipped
Distributed scan
A SELECT that spans groups reads local buckets plus one fragment per other group, in batches. Every query shape is answered correctly.
Shipped
Two-phase commit
A transaction that writes on several nodes commits on all or none. A recovery worker resolves anything a crash left prepared.
Shipped
Bucket moves
move_bucket copies a bucket, replays changes with logical replication, proves the copy is complete and switches owners in two phases.
0.3, in review
Standalone verifier
kingres-verify checks digests and proofs without trusting the database. Standard-library Go, about 2,000 lines an auditor can read.
Shipped
Approximate aggregates
HyperLogLog distinct counts, Space-Saving top-n and t-digest percentiles, written from the published algorithms.
1.0, new
Time partitioning
Policies for ordinary partitioned tables: create partitions ahead, drop or detach old ones, and keep a history of every run.
0.1, new
Secure by construction
Servers talk over mutual TLS under a cluster CA. Console accounts are PostgreSQL roles. Network logins are refused until you allow them.
Shipped
How it works
Every server is a full member. Connect to any of them.
Each server hosts a PostgreSQL primary with its own buckets and a kingresd agent. The catalog is authoritative on the map group and copied to every node, so every node can plan and route.
PostgreSQL 18 primary · group 0Catalog source of truth, plus its buckets
kingres-2 group 1
kingresdSame console, same API. Forwards leader-only calls.
PostgreSQL 18 primary · group 1Copy of the catalog, plus its buckets
kingres-3 group 2
kingresdSame console, same API. Forwards leader-only calls.
PostgreSQL 18 primary · group 2Copy of the catalog, plus its buckets
SQL between nodes: libpq, as the current rolekingresd to kingresd: internal API over mutual TLSCluster state: system database kingres_system
PATH 1One key, one group
WHERE id = 42 belongs to one bucket. If this node owns it, PostgreSQL's planner runs as usual. Otherwise the statement is deparsed and run on the owner.
PATH 2Reads across groups
PostgreSQL plans the query as if all data were local. Each distributed table is read by a Kingres Distributed Scan, and restrictions are rechecked here.
PATH 3Writes across groups
An UPDATE or DELETE that can be shipped whole runs on every group that owns buckets of the table, under two-phase commit.
PATH 4Stale map? Retry.
A forwarded session never trusts the sender's placement. If a bucket has moved it raises KS001; the origin reloads the catalog and retries once.
Tamper-evident ledger
A history that can be checked from outside the database.
Put a table under ledger control and every change is hashed into a chain of blocks. Export a digest, keep it somewhere the database cannot reach, and you hold the root of trust.
block 41prev 9f2c…a1root 5ab1…0eentries 128
block 42prev c07d…3broot e4f8…92entries 64
block 43prev 71aa…d4root 0b3c…7fentries 211
digeststored outsidethe database:the root of trust
kingres-verifyproof + digest→ VERIFIEDexit status 0
Append-only or updatable
Append-only tables accept inserts only. Updatable tables allow UPDATE and DELETE, with every removed version kept in a history table and still provable.
Blocks, digests, proofs
Background workers build Merkle blocks on an interval. ledger_generate_digest exports the head; ledger_inclusion_proof shows that one row version is part of the ledger.
Hard to switch off by accident
Enabling the ledger is permanent. DROP TABLE, DROP SCHEMA … CASCADE and DROP EXTENSION are rejected while a ledger table exists.
What it promises, plainly. The ledger makes tampering detectable, not impossible. Someone who can rewrite the database files can rewrite the chain too; what they cannot do is make it match a digest you stored elsewhere. That digest, kept outside the database, is the root of trust.
Web console
Open port 8091 on any server and run the cluster from there.
The console is a Vue single-page application embedded in kingresd, generated against the same OpenAPI contract the REST API is checked with. Pick a page to see what it does.
http://127.0.0.1:8091/dashboard
A guide to the console's pages, drawn as a schematic. It is not a screenshot.
First run in a browser
A new node starts uninitialized and writes a one-time setup token. Enter it, then create a cluster or join one.
Accounts are PostgreSQL roles
Sign in with a database role. What you may do follows role membership such as kingres_admin and kingres_ops.
Pinned certificates
Adding or joining a server asks for the peer's certificate fingerprint. Unpinned joins are refused unless you say so explicitly.
Bring your own Prometheus
No bundled metrics store. kingresd keeps a short history for the charts and exposes /metrics.
Use cases
What people build on a database like this.
01
Multi-tenant SaaS
Distribute every tenant-owned table by the tenant id and colocate them. A tenant's rows share buckets, so its queries and joins run inside one shard group, and shared lookup data lives in reference tables.
Payments, balances, approvals, medical or legal records. Keep them in ledger tables, export digests on a schedule, and hand an auditor a proof they can check on their own machine.
ledger_enableledger_generate_digestkingres-verify
03
Real-time analytics
Dashboards over event streams rarely need exact answers. Count distinct users, rank the top items and read percentiles from sketches that stay small and merge across days.
Let a policy create tomorrow's partitions before the data arrives and retire last quarter's when it is no longer needed. Each run is recorded, so you can see what was created and dropped.
What KingresDB guarantees today, and what it does not yet.
A database earns trust by being exact about its limits. These come straight from the extension documentation and the independent reviews in the repository.
Guaranteed
Atomic commit across nodes.Multi-node writes use two-phase commit. The local commit is always flushed before the first remote commit is sent.
Atomic catalog changes and DDL.Adding a node, distributing a table and propagated DDL change all nodes in one transaction under a cluster-wide lock.
Correct answers for every query shape.The row-moving distributed scan stays as the path that can answer any query, whatever faster paths are added.
Text that means the same everywhere.Forwarded statements are deparsed so that session settings on either node cannot change their meaning.
Detectable tampering.A change to ledger history is found by verification against a digest held outside the database.
Not yet
No global snapshot.Each node commits at its own moment, so a cross-group read can see a transaction on one group before another. SERIALIZABLE is per node.
No distributed deadlock detection.Fan-out writes lock in node order and do not deadlock each other. For anything else, set lock_timeout.
Some statements are refused.A multi-row INSERT or INSERT … SELECT whose rows belong to several groups gets a clear "not supported yet" error.
Tables are distributed while empty.Distributing a table that already has rows is on the roadmap.
Failover is in progress.Standbys, leases and automatic failover are being built. Do not run production on it yet.
Status and roadmap
Where the project is.
Each phase ships with its tests, its documentation and an independent review. The dated work log in the repository records what was done and how it was verified.
Sharding corekingres_shard 0.1 – 0.2
Catalog and bucket map, statement routing, distributed scan, fan-out writes, reference tables, two-phase commit with a recovery worker, DDL propagation.
Shipped
Ledgerkingres_ledger 0.2 · kingres-verify
Append-only and updatable ledger tables, block-building workers, digests, inclusion proofs, recorded verifications and the standalone verifier.
Shipped
Control plane and consolekingresd · REST contract 0.7
Create or join a cluster, databases, roles and grants, network access, certificates, audit log, cluster-wide logs and alerts, SQL workbench, task engine.
Shipped
PackagingDocker image · Debian package
One reproducible build recipe, a non-root container image, a three-node compose cluster and a .deb with a systemd unit for Ubuntu 24.04.
Shipped
Bucket movementkingres_shard 0.3
move_bucket, rebalance_plan, check_cluster. Fixes for the review's findings are under way; the console's rebalance operations follow.
In review
High availabilityphase 5
A standby for every shard group with chained placement, failover inside kingresd fenced by majority leases, and a client proxy port.
Approximate aggregates and time-partition policies are in the tree. Columnar storage, repartitioned joins and MERGE on distributed tables are designed.
In progress
PostgreSQL 19one extension source, two majors
Every extension builds and is tested against PostgreSQL 18 and 19 from the same source, with a product branch per major.
In progress
Faster pathsphase 2b
Aggregate, sort and limit pushdown, colocated joins, and a row router for multi-row INSERT, INSERT … SELECT and COPY FROM.
Designed
Table lifecycle and elasticityphases 9 – 11
Distribute tables that have rows, undistribute, change the distribution column; split buckets, rebalance in the background, drain a server; schema-based sharding.
Designed
Signing and anchoringphase 7
Signed digests, external anchoring to write-once storage and timestamp authorities, and one aggregate digest across shard groups.
Planned
Get started
From a clone to a running node in three commands.
Build the image from the repository, start a node, and finish the setup in your browser. Tested on Ubuntu 24.04 and on Windows 11 with WSL2.
Nothing baked in. The setup token is created at first start, inside the volume.
Closed by default. The instance accepts no network login until you add a rule on the console's network access page.
Runs locked down. Non-root, read-only root filesystem, all capabilities dropped.
cd Kingres/kingres/packaging/docker
# kingres-1, kingres-2, kingres-3 on one network
docker compose up --build -d
# setup token and console URL of each node
./tokens.sh
# consoles: 127.0.0.1:8091 8092 8093
# databases: 127.0.0.1:5432 5433 5434
docker compose stop # clean shutdown of the databases
Ubuntu 24.04, amd64
cd Kingres/kingres/packaging
# build the release tree, then the package
build/build.sh
deb/build-deb.sh
sudo apt install ./kingres_0.1.0-*_amd64.deb
sudo systemctl status kingres.service
# then open the console on port 8091
STEP 1Open the console
Go to 127.0.0.1:8091 and enter the setup token.
STEP 2Create a cluster
kingresd creates the instance, the administrator role and the system database.
STEP 3Add servers
Join more nodes from the Servers page, then distribute your tables.
Open source, under the PostgreSQL License.
The design, the decision records, the reviews and the work log are all in the repository. Read them, run the tests, and tell us what breaks.