Skip to content
HexDBOpen source · Written in Rust

The document database that ships as one binary.

JSON documents over REST and GraphQL. Full-text search, ACID transactions, streams, functions and plugins. Encrypted at rest, replicated across a lattice of hexes that elects its own leader and survives losing one.

REST + GraphQL
one filter language
AES-256-GCM
every byte on disk
Auto failover
no coordinator
terminal
# Insert a document
curl -X POST http://localhost:7700/orders \
  -H "Authorization: Bearer $HEXDB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "customer": "ada", "total": 42, "status": "paid" }'

# Query it back, largest first
curl -G http://localhost:7700/orders \
  -H "Authorization: Bearer $HEXDB_TOKEN" \
  --data-urlencode 'filter={"status":"paid","total":{"$gte":10}}' \
  --data-urlencode 'sort=-total'
200 OK · application/json
{
  "documents": [
    { "id": "01JTY87RVJ9B5863KMB2YD896B",
      "customer": "ada", "total": 42, "status": "paid" }
  ],
  "total": 1,
  "plan": { "indexes": ["status"], "scanned": 1 }
}
1
binary: REST, GraphQL and the admin UI
AES-256
GCM encryption on every byte at rest
4+2
data and parity vertices holding every document in memory
4
client libraries: Node.js, Python, .NET and EF Core
0
external services required to run

The one-slide version

HexDB, Explained at a High Level

No jargon. What it does for the business, what it replaces, and why it costs so little. Print it or put it on a screen.

HexDB

In one sentence: HexDB is a database that keeps your data encrypted, copied and available, in a single program that runs anywhere, with no licence fees.

  1. 01

    Stolen data is unreadable

    Everything HexDB writes to disk is encrypted with AES-256, the standard banks and governments rely on. Without your key, a stolen disk or backup is noise.

    Keys can be changed without stopping the service.

  2. 02

    It keeps running when hardware fails

    Run it on two or more servers and they keep full copies of each other. If the one in charge dies, another takes over in seconds, by itself.

    In memory, each record is kept in six pieces with two spare. Any two can be corrupted and it is rebuilt.

  3. 03

    It grows by adding servers

    Need to serve more readers or survive more failures? Start another server with the same name and secret. It finds the others, copies the data and starts working.

    No clustering software, no coordinator, no consultant.

  4. 04

    It runs wherever your teams work

    Linux, macOS, Windows and Docker. Official libraries for JavaScript, TypeScript, Python and .NET (including Entity Framework), and plain web requests for everything else.

    Any language, any framework, any cloud or none.

  5. 05

    It costs almost nothing to own

    Open source, with no licence fees. One small program does the work of a database, a search engine, a message queue, a job scheduler and an admin console, so there is less to buy, run and secure.

    Runs comfortably on modest hardware.

  6. 06

    It stores less and finds things faster

    Records are compressed before they are encrypted, so disks and backups stay small. Indexes and full-text search find what you need without reading everything.

    Reports and real-time alerts are built in, no extra tools.

What it replaces

  • Database
  • Search engine
  • Message queue
  • Job scheduler
  • Admin console
  • Encryption layer
HexDB
one program, one process

Fewer moving parts means fewer things to patch, monitor, licence and explain to auditors.

Open source · github.com/dreaminhex/hexdbSee the code

Everything in the box

A whole data platform, not just a key-value store with ambitions.

Most databases make you bolt on search, a message bus, a scheduler, encryption and an admin console. HexDB ships them in the same process, behind the same permissions, replicated the same way.

Documents and queries
Insert, replace, merge-patch, delete, bulk writes, upserts and update-by-filter. One JSON filter language with sorting and paging, shared by REST and GraphQL.
ULID idsTTLsIdempotency keys
Indexes and search
Field, composite and unique indexes. Full-text indexes with configurable analyzers: stemming, accent folding, n-grams and autocomplete. A query advisor that suggests indexes from what it observes.
Inverted index7 analyzersAsk Claude
Aggregations and transactions
Group-by with count, distinct count, sum, average, min and max. Multi-document ACID transactions across tessellations with version and filter preconditions.
Serializable1,000 opsif_version
Versioned schemas
Optional per tessellation. Each version is compatibility-checked against the last, like a schema registry. Migrations (rename, copy, remove, default, convert, or a function you write) run in the background and resume after restarts, and any version can be rolled back.
Dry-run checkRollbackFunction steps
Change data
An ordered change feed over polling, long polling or Server-Sent Events, backed by an on-disk history. Consumers resume exactly where they stopped, even after a restart.
SSEResumable24h history
Streams
Named publish/subscribe logs with consumer groups and retention. Feed them from tessellation changes and deliver them to webhooks, at least once, with per-destination offsets.
Consumer groupsWebhooksRetention
Functions, schedules and triggers
Saved queries, aggregations, transactions and scripts in Python, TypeScript or JavaScript, run by name with typed parameters. Schedule them on an interval or a cron expression, or trigger them when documents change: before a write, to check, change or refuse it, or after it commits.
Typed paramsCronBefore/after triggers
Plugins
Change data capture to Kafka and Kinesis, ingest from PostgreSQL, SQL Server and S3, logs and metrics to OpenTelemetry, files or HTTP. A plugin is a process, a webhook or built in.
KafkaOTLPAny language
Replication and failover
Hexes discover each other, elect an Overseer and replicate by snapshot plus change stream. When the Overseer fails another takes over. Optional sync acks, write quorum and write forwarding.
Auto electionQuorummin_acks
Security
Sessions, API keys and TOTP multi-factor auth. Built-in and custom roles granted per tessellation, with row filters (only the user's region, say) and hidden fields per role. A persistent audit trail, lattice-wide sign-in throttling, and AES-256-GCM everywhere on disk.
RBACRow filtersMFAAudit trail
Resilience
A write-ahead log with group commit, SSTables with Bloom filters and compaction, and in-memory Reed-Solomon coding across six vertices that detects and repairs corrupted memory on its own.
WALSSTables4+2 coding
Operations
Online backups while serving traffic, an admin UI, a CLI, live runtime settings, six hours of metrics history, tailed logs, an OpenAPI description, drivers for Node.js, Python and .NET, and an Entity Framework Core provider.
Admin UICLIOpenAPI 3.1EF Core

Anatomy of a hex

What one hex does with your data.

A hex is one running HexDB server. Step through with the arrows, or press play and it advances every few seconds, to follow a document from the moment a request arrives, through the write-ahead log, into six error-correcting vertices, out to the change feed, and down to disk.

one hex · follow a document
clientREST · GraphQL · UIvalidateroles · schema · triggersWALencrypt · fsyncchange feedreplicas · pluginsSSTablesimmutable · Bloom filter · compactionD1D2D3D4P1P2D1D2D3D4P1P2memoryLRU{ }data vertex (D1–D4)a quarter of the documentparity vertex (P1–P2)check data that rebuilds any tworepairs 0
Press “Corrupt a vertex” at any point to watch the memory detect and repair a damaged fragment.

A request arrives

REST, GraphQL, the admin UI and the drivers all come through the same door.

  • One process serves the HTTP API, GraphQL and the admin UI, on one port.
  • Every request needs credentials: a session cookie or an hxk_ API key. There is no unauthenticated mode, and unknown paths get 401 too.
  • A write sent to a replica is forwarded to the Overseer, signed with the lattice key, and the response says where it went.

Step 1 of 7. Use the arrows to move at your own pace.

The parts, in one glance

The server
One Rust binary, hexdb_api, serving REST, GraphQL and the admin UI on a single port. The hexdb CLI starts, stops, inspects and backs it up.
Tessellations
Named collections of JSON documents, like tables. Each can have indexes, a text index with its own analyzer, and a versioned schema. Roles are granted per tessellation.
Memory
The newest unflushed documents plus an LRU cache of recent reads, within memory.ram_mb. Every document in memory is spread over six vertices: four data, two parity.
Disk
A write-ahead log for durability, immutable SSTables for bulk storage, encrypted index snapshots, a sealed catalog, and a change-history archive. All AES-256-GCM.
The catalog
Tessellations, index definitions, schemas, the change-history ID and the lattice name, in one sealed file. Replicas copy it from the Overseer and re-read it within a poll when its fingerprint changes.
Background work
TTL sweeps, vertex integrity checks, flushes, compaction, metrics every 15 seconds, discovery probes, and, on the Overseer, plugins, stream deliveries, after triggers and schedules.

Hexes, tessellations, lattices

A lattice that elects its own leader and heals itself.

Start one hex and you have a database. Start three with the same lattice name and secret, and they discover each other, elect an Overseer, replicate, and fail over on their own. No coordinator, no sidecar, no operator in the loop.

lattice · quiet-meadow
hex-1Overseerwriteshex-2Harvesterlag 0 mshex-3Replicantlag 3 ms

Three hexes, one Overseer. Replicas follow the change feed and serve reads.

Try it for real: node scripts/lattice-demo.mjs, then type stop 1.

Three roles

  • Overseer. Takes writes, publishes the change feed, runs plugins, stream sources and schedules.
  • Harvester. A read replica that follows the Overseer and can be elected if it fails.
  • Replicant. A standby copy that follows but is never elected. For backups and rebuilding Harvesters.

The vocabulary

Hex
One running HexDB server.
Tessellation
A collection of documents, like a table. Roles are granted per tessellation.
Lattice
A group of hexes sharing a name and a secret that can reach each other. A single hex is a lattice of one.
Vertex
One of the six regions of a hex's memory. Four data vertices each hold a quarter of every document; two parity vertices hold Reed-Solomon check data that can rebuild any two missing quarters.

Durability is a dial

Replication is asynchronous by default. Two settings let you require that a write exists on replicas before it's acknowledged, and that an Overseer refuses writes when it can't see a majority.

# hexdb.toml: trade a little latency for a lot of safety
[replication]
min_acks = 1          # replicas that must hold a write before it's acknowledged
ack_timeout_ms = 5000 # on timeout the write stays committed; the client gets 503
quorum = 2            # hexes the Overseer must see to accept writes (no split brain)
forward_writes = true # a write sent to a replica is forwarded to the Overseer
failover, on your laptop
node scripts/lattice-demo.mjs   # three hexes on 7800/7810/7820 with sample data
> stop 1                        # stop the Overseer and watch another hex take over
> start 1                       # it comes back as a replica

The API

Small surface. Deep features.

If you can send JSON over HTTP you already know how to use HexDB. Here's what that buys you.

Plain HTTP. Every write is atomic and durable before it returns.

  • PUT replaces, PATCH is a JSON merge patch, null removes a field.
  • Bulk writes, update-by-filter and upserts are each a single atomic write.
  • Every write takes an Idempotency-Key: a retry returns the original response, even after a crash.
  • A write to a tessellation that doesn't exist creates it.
# Create: 201 with the document and a Location header. ?ttl=<seconds> sets an expiry.
curl -X POST http://localhost:7700/articles -H "Content-Type: application/json" \
  -d '{ "title": "Quantum Tessellation", "tags": ["hexdb", "rust"], "published": true, "views": 445 }'

# Read, replace, merge-patch (null removes a field), delete
curl http://localhost:7700/articles/01JTY87RVJ9B5863KMB2YD896B        # ETag = version
curl -X PUT   http://localhost:7700/articles/01JTY... -d '{ "title": "New" }'
curl -X PATCH http://localhost:7700/articles/01JTY... -d '{ "views": 446, "tags": null }'
curl -X DELETE http://localhost:7700/articles/01JTY...                 # 204

# Bulk: one atomic write, up to 10,000 documents
curl -X POST http://localhost:7700/articles/_bulk -d '[{ "title": "One" }, { "title": "Two" }]'

# Patch everything a filter matches
curl -X POST http://localhost:7700/articles/_update \
  -d '{ "filter": { "status": "draft" }, "update": { "status": "published" } }'
# => { "matched": 10, "modified": 10 }

# Upsert by key fields
curl -X POST http://localhost:7700/customers/_upsert \
  -d '{ "key": ["source_id"], "documents": [{ "source_id": 17, "name": "Ada" }] }'

Drivers

Or skip HTTP and use a client.

Node.js, Python and .NET drivers cover documents, queries, aggregations, transactions, idempotency keys, the change feed, streams, functions and GraphQL. They retry 429 and 503 where it's safe to. On .NET there is also an Entity Framework Core provider: LINQ filters, sorting and paging run on the server, and each SaveChanges is one atomic transaction.

npm install hexdb
import { HexDB } from "hexdb"

const db = new HexDB({ url: "http://127.0.0.1:7700", apiKey: process.env.HEXDB_API_KEY })
const orders = db.tessellation("orders")

await orders.insert({ customer: "ada", total: 12 })
const page = await orders.query({ filter: { total: { $gt: 10 } }, sort: "-total" })

Storage engine

Built so you don't lose data. Or sleep.

A log-structured engine written from scratch in Rust: a write-ahead log with group commit, SSTables with compaction, encryption on every byte that touches disk, and error-correcting memory that repairs itself.

The write path

  1. 01
    Validate

    Permissions, size limits, the schema, and expected versions for read-modify-write operations.

  2. 02
    Sequence and apply

    Under one state lock the batch gets consecutive sequence numbers, is queued to the WAL and applied to memory and indexes. A query always sees its own writes.

  3. 03
    Group commit

    A dedicated thread batches whatever is waiting into one write and one fsync. Zstandard first, then AES-256-GCM.

  4. 04
    Acknowledge and publish

    Once durable, the write is acknowledged, published to the change feed, and, with min_acks, held until enough replicas confirm.

On disk and in memory

Memory
Newest unflushed documents plus an LRU cache, each spread over four data vertices and two parity vertices with Reed-Solomon coding. See the hex above.
SSTables
Immutable, per tessellation, with Bloom filters and a chunked encrypted index. About 2 bytes of RAM per document.
Compaction
Merges SSTables, drops tombstones and expired documents, and rewrites files on old formats or old keys.
Recovery
Replays the WAL in order. A torn final record is tolerated; anything that can't be decrypted stops startup rather than discarding data.
Backups
Consistent and online: SSTables are hard-linked, the WAL is rotated, and writes continue throughout.

Encrypted at rest, rotated without downtime

WAL records, SSTable bodies and indexes, the catalog, settings, metrics and index snapshots are all AES-256-GCM with random 96-bit nonces, authenticated with their IDs so files and entries can't be swapped. Only SSTable headers are plaintext. A server whose data needs a key it doesn't have refuses to start rather than discard anything.

# Every developer and every deployment has its own key. Keep it.
printf '[storage]\nencryption_key = "%s"\n' "$(hexdb secret)" > hexdb_api/hexdb.local.toml

# Rotate without downtime: add the new key, move the old one to previous_encryption_keys,
# restart, and let compaction rewrite the files. Then drop the old key.
[storage]
encryption_key = "base64:NEW..."
previous_encryption_keys = ["base64:OLD..."]

What's in the data directory

Everything a hex knows lives in one folder. Back it up online with one command, and restore by pointing a hex at the copy.

storage.path
<storage.path>/
├── catalog.hxe                tessellations, indexes, schemas, history ID, lattice name (sealed)
├── settings.hxe               runtime settings changed from the UI or API (sealed)
├── metrics-history.hxe        6 hours of metrics samples (sealed)
├── query-stats.hxe            the query advisor's statistics (sealed)
├── hexdb.pid                  PID, endpoint and shutdown token (owner-only)
├── wal/
│   ├── <first-seq>.wal        active write-ahead log segments
│   └── archive/<first-seq>.wal  the change history
├── indexes/<tessellation>/<index>.hxi   index snapshots (sealed)
└── <tessellation>/<ulid>.hxs  SSTables, including system tessellations

Security

Secure by default, not by checklist.

There is no unauthenticated mode. Every request outside a short public list needs credentials, every refusal is audited, and everything on disk is encrypted with a key only you hold.

Authentication
HttpOnly, SameSite=Strict session cookies for browsers; hxk_ API keys for scripts and services. Argon2id passwords. Role changes apply on the next request.
Multi-factor
TOTP (RFC 6238) with every authenticator app, plus ten single-use backup codes. No code is ever accepted twice.
Access control
Built-in and custom roles granted per tessellation or stream, with row filters and hidden fields per role. Every handler checks before it looks anything up, so a 403 never reveals whether something exists.
Audit trail
Sign-ins, failures, key changes, schema and index changes, refusals and maintenance, with actor, target, outcome and client address. Ship it to a SIEM with a plugin.
Brute-force protection
Failed sign-ins are throttled per login and per address, and the count is shared across the whole lattice. Unknown logins take as long as wrong passwords.
Between hexes
Discovery is a mutual challenge-response; replication requests carry single-use, time-boxed keyed-BLAKE3 signatures. The lattice key is never sent and rotates live.

Nine permissions, any combination

Built-in roles cover the usual cases (reader, writer, owner, operator, auditor, admin). Custom roles pick from the same nine permissions.

PermissionAllowsScope
readGet, list, query, count, aggregate, the change feed; consume streamsper tessellation
writeInsert, replace, patch, delete; publish to streamsper tessellation
manageDelete tessellations; manage indexes, schemas and advice; configure streamsper tessellation
statusServer status, metrics, storage and lattice detailsglobal
logsThe server logglobal
auditThe audit trailglobal
pluginsPlugins and their delivery stateglobal
maintenanceFlush, compact and back upglobal
adminEverything: users, roles, settings, functions, schedules, joining hexesglobal

Grants, in one request

A grant names a role and the tessellations it applies to. Streams use the resource name stream:<name>. A grant on * covers everything.

# A grant names a role and the tessellations it applies to
curl -X POST http://localhost:7700/users -H "Content-Type: application/json" -d '{
  "login": "ada", "password": "correct horse battery staple",
  "roles": [{ "name": "writer", "tessellations": ["orders", "customers"] },
            { "name": "reader", "tessellations": ["*"] }] }'

# Custom roles from any set of permissions
curl -X POST http://localhost:7700/roles \
  -d '{ "name": "analyst", "permissions": ["read", "status"] }'

Row filters and hidden fields

A role can be restricted per tessellation: a filter that limits which documents it sees and writes, using the caller's login, email or attributes, and a list of fields it can never see or change.

# A role that only sees and writes its own region, and never sees cost or margin
curl -X POST http://localhost:7700/roles -H "Content-Type: application/json" -d '{
  "name": "regional", "permissions": ["read", "write"],
  "restrictions": { "orders": { "filter": { "region": { "$user": "attributes.region" } },
                                "hide": ["cost", "margin"] } } }'

curl -X PATCH http://localhost:7700/users/ada -d '{ "attributes": { "region": "EU" } }'
# Queries, counts, aggregations, GraphQL and the change feed all see only matching documents.
# Filtering or sorting on a hidden field is refused, so values can't be probed.
  • • Responses carry nosniff, DENY framing, a no-referrer policy and a Content Security Policy.
  • • No CORS headers are sent, so other sites can't read responses. Cookie-authenticated changes must be same-origin (CSRF protection).
  • • TLS is two settings away, with HSTS, Secure cookies and HTTPS replication between hexes.

Operations

An admin UI, a CLI, and an API for everything they do.

The admin UI is served by every hex at /ui/ and goes through the same API as any client, with the signed-in user's permissions. Pages a user can't use are hidden, and the server refuses them anyway.

http://localhost:7700/ui/
Dashboard
Live document, memory, disk and operation figures, activity charts, vertex health, the lattice and 'Add a hex'
Queries
A GraphQL console with schema-aware completion, examples and history
Tessellations
Create and delete; manage indexes (with analyzer choice and suggestions) and schemas, with version history and rollback
Documents
Browse with filters and sorting; create, edit and delete documents
Streams
Create streams, publish, read messages, watch consumer groups and deliveries
Functions
Create and run functions; manage schedules and triggers
Users, Roles
Accounts, role grants, user attributes, custom roles with row filters and hidden fields, MFA resets
Audit Trail
Security events, filterable by user, action and time
Plugins
Loaded plugins, their state and deliveries
Logs
The server log, tailed live
Settings
Runtime settings and the effective configuration
Account
Your password, MFA and API keys
hexdb --help
hexdb start [-s]                 start the server (-s: in the background)
hexdb stop [--force]             stop it gracefully
hexdb health                     is it up?
hexdb status                     status and metrics
hexdb secret                     print a new random key
hexdb backup [--name N] [--list] back up the running server
hexdb plugins add|remove|list    manage the plugin registry
hexdb lattice spawn --count 2    start more hexes that join this lattice
hexdb lattice list|stop          list or stop them
observability and backups
curl http://localhost:7700/status                        # documents, memory, disk, vertices, lattice, lag
curl "http://localhost:7700/status/history?minutes=60"   # a sample every 15 s, kept for 6 hours
curl "http://localhost:7700/logs?level=warn&limit=100"   # tail, search and filter the log
curl -X POST http://localhost:7700/backup -d '{"name": "nightly-1"}'   # consistent, while serving writes
curl http://localhost:7700/openapi.json                  # the REST API as OpenAPI 3.1
  • • Runtime settings change live from the UI or API; the rest apply at the next restart and are listed as pending.
  • • Metrics every 15 seconds, kept for six hours across restarts. Export with the built-in OTLP plugin.
  • • Upgrades: replace the binaries and start. Older file formats are read and rewritten by compaction.
  • • Ships as archives for Linux, macOS and Windows, a Debian package, a Docker image, a systemd unit, and Homebrew, Chocolatey and winget manifests.

Ecosystem

Plugs into what you already run.

Plugins extend HexDB without changing it. Each is a folder with a manifest: a process in any language reading JSON lines on stdin, a webhook, or a built-in exporter. They run on the Overseer, so a lattice delivers each payload once, and they resume from a saved position after a restart.

PluginTypeWhat it does
@streams/kafkastreamProduces changes to Kafka, keyed by document ID
@streams/kinesisstreamPuts changes on an Amazon Kinesis stream
@sinks/otlpmetricsMetrics to an OpenTelemetry endpoint (built in)
@sinks/log-filelogsRotating JSON-lines log files
@sinks/http-logslogsLog records to Splunk HEC, Logstash and friends
@sources/postgressourceCopies a PostgreSQL table and keeps it in sync
@sources/mssqlsourceThe same for SQL Server
@sources/s3sourceImports JSON and JSON-lines objects from S3
@enrichers/ai-keywordsstreamAdds keywords to new documents with Claude
@examples/change-loggerstreamLogs each change; the minimal example
Plugin SDK and examples

Process plugins run in a clean environment with only the variables they're given. The server's own keys are never passed.

a plugin, with the dependency-free SDK
import { payloads, api, log } from "../../sdk/hexdb-plugin.mjs"

for await (const change of payloads()) {
  if (change.op === "put") log("order", change.id, change.document.total)
}

await api().post("/orders", { total: 12 })   // plugins with [access]
JavaScript / TypeScriptnpm install hexdb
Node.js 18+, Deno, Bun or a browser. No dependencies.
Pythonpip install hexdb
Python 3.9+. Standard library only.
.NET (C#, F#)dotnet add package HexDB.Client
.NET 8+.
Entity Framework Coredotnet add package HexDB.EntityFrameworkCore
EF Core 8 on .NET 8+. One entity type per tessellation; LINQ runs on the server; each SaveChanges is one atomic transaction.

Get started

From a clean machine to a running lattice in minutes.

Five steps from source, or two commands with Docker. The quick start in the README has the Windows PowerShell versions and the details.

  1. 1

    Install the prerequisites

    Git, Rust (stable), Node.js 22 or later and a C toolchain. On Windows use PowerShell.

    # Linux (Debian/Ubuntu)
    sudo apt update && sudo apt install -y build-essential git pkg-config libssl-dev curl
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
    
    # macOS
    xcode-select --install
    brew install node
  2. 2

    Build it

    The first Rust build takes a few minutes and produces the server and the CLI.

    git clone https://github.com/dreaminhex/hexdb.git && cd hexdb
    cd hexdb_admin && npm ci && npm run build && cd ..
    cargo build --release -p hexdb_api -p hexdb_cli
  3. 3

    Put hexdb on your PATH

    Installing into Cargo's bin folder works the same on every OS.

    cargo install --path hexdb_cli
    cargo install --path hexdb_api
    hexdb --help
  4. 4

    Create your encryption key

    HexDB encrypts everything it stores. The key lives in a git-ignored file.

    printf '[storage]\nencryption_key = "%s"\n' "$(hexdb secret)" > hexdb_api/hexdb.local.toml
  5. 5

    Start it and sign in

    The first start prints a generated administrator password. Open the admin UI, change it, and create an API key.

    cd hexdb_api
    hexdb start
    # open http://localhost:7700/ui/
Then make your first request. Create an API key on the Account page, export it as HEXDB_TOKEN, and run the two curl commands at the top of this page. To load sample data, run node scripts/seed.mjs.

FAQ

Questions people ask first.

Contact

Talk to the person who built it.

Questions about a deployment, a feature you need, a bug you hit, or a project you'd like help with. Messages go straight to the maintainer.

Bugs and feature requests
github.com/dreaminhex/hexdb/issues

HexDB is open source and built in the open. Stars, issues and pull requests are all welcome.

Delivered to matthew@dreaminhex.com. No newsletter, no tracking.