Flint KV speaks the DynamoDB API, so an unmodified AWS SDK points at it and keeps working — whether you are already on DynamoDB or just need a key-value store and would rather use an API your tools, your libraries and your engineers already know. Underneath, it is a different shape: every version of every row lives in an object store you own — S3, MinIO, Ceph, or whatever your cloud provides — with compute separate from storage. That is what makes a clone free, a restore a pointer move, and the bill scale with what you keep rather than with what a managed service charges to keep it.
Questions, or want to try it? info@crestwayai.com
PutItem p50 — log quorum across us-east-1a/b/cGetItem p50 warm — client off-box, 2,000 samplesStatus: pre-production. The core works end to end — an unmodified AWS SDK creates a table, writes items, reads them back after the machine is destroyed, and drives transactions, indexes, streams and restore. We are hardening it ahead of general availability, so talk to us before you put production traffic on it. Everything on this page is either measured or modelled from published prices, and each section says which.
import boto3
ddb = boto3.client("dynamodb", endpoint_url="http://flint:8000")
ddb.put_item(TableName="orders", Item={"id": {"S": "o-1"}, "total": {"N": "42"}})
No driver to swap, no query language to learn, no data model to redesign. The same call, the same request shape, the same error codes — including the ones your retry logic depends on.
PutItem, GetItem, UpdateItem,
DeleteItem, Query, Scan with
parallel segments, BatchGetItem and
BatchWriteItem — the batches non-atomic and returning
UnprocessedItems, exactly as DynamoDB's do.
TransactWriteItems within a table, across tables, and
across nodes: all-or-nothing, with condition checks on rows the
transaction does not touch, and idempotent under
ClientRequestToken. Reports two capacity units per
transactional write, exactly as DynamoDB does — and is one commit
underneath rather than two writes.
CreateTable, DescribeTable,
DeleteTable, local secondary indexes, TTL, change
streams, and RestoreTableToPointInTime — the AWS CLI
drives all of it against a Flint KV endpoint.
Anything not built is refused with the error DynamoDB would give, rather than accepted and quietly ignored. A capacity field you set is accepted and disregarded — as it is on on-demand DynamoDB — and that is said rather than left for you to discover.
| p50, concurrency 1 | Flint KV, 3-AZ quorum | Flint KV, one disk | DynamoDB, same run |
|---|---|---|---|
PutItem | 0.87 ms | 0.45 ms | 2.63–4.07 ms |
GetItem (warm) | 0.27 ms | 0.28 ms | 1.80–2.20 ms |
Query, 10 items | 0.31 ms | 0.33 ms | 2.04–2.86 ms |
The DynamoDB column is what
we observed in this run, not a figure AWS publishes — and it is a range
because it moved that much between two runs twenty minutes apart, while
ours moved by 0.02 ms. Run in
us-east-1 with the client on a separate instance from the
node, 2,000 samples per operation after a discarded warm-up, concurrency 1.
The same SDK binary talks to both targets —
signing, retries, connection pooling and the HTTP stack are therefore
identical and the only difference left is the server. The
replicated column is the one to read: DynamoDB acknowledges a write
only after it is in three availability zones, so the comparable
configuration is three Flint KV nodes in us-east-1a,
1b and 1c with the log quorum-replicated. The
+0.42 ms between our two columns is the cross-AZ round trip, measured
independently at 0.435 ms beforehand. Placing the peers in one AZ would buy
most of that back and would not be the same promise.
What this does and does not say.
It is latency, at concurrency 1. That is the right way to measure latency and the wrong way to guess at throughput — a latency number taken under load is a queueing number wearing a latency label. Throughput is a separate ceiling, measured on our side and compared against DynamoDB's published per-table limit rather than against a run of theirs.
The read comparison leans against us, and we
left it that way. DynamoDB's default GetItem is
eventually consistent; ours answers strongly consistent because giving a
weaker answer than asked for would be a pointless loss. Ours is doing
more work for that 0.27 ms, not less.
Warm means warm. GetItem p99
was 24 ms — the 3% of reads that missed cache and paid an object-store
round trip. Uncached point reads are slower than DynamoDB's, tens
of milliseconds against single digits, and no amount of engineering
closes that for a genuine miss. The design answers it with a large local
NVMe tier so misses are rare — a block costs 48 µs from local disk
against 24.6 ms from S3 — and by guaranteeing a miss costs exactly one
object-store request rather than three. If your workload is uniformly
random over a dataset far larger than any cache, that is a real cost and
you should measure it on your own data.
Nothing here measures money. The cost section below is arithmetic on published prices, not a run.
Every write publishes a new version rather than overwriting the old
one, so a reader takes a snapshot and sees one consistent
instant across every row it touches — while writes keep landing.
A long Query or a full Scan runs against a
fixed view instead of a moving one, and it slows nothing down,
because the version it reads is immutable and nobody is contending
for it.
Cloning a table writes one compare-and-swap on one pointer to existing immutable data. Instant, and free in storage until the clone is written to — after that you pay only for what diverges. Give every engineer a full-size environment. Reset a test database between runs. Debug production data without touching production. On DynamoDB the same operation copies the table and bills you for the copy.
The history is already in the object store, so recovery is retention
rather than replication: nothing is copied, there is no backup window
and no restore that takes hours. RestoreTableToPointInTime
is the ordinary AWS call, and it returns a table in the time a
pointer takes to move. One honest difference: a restore lands
on the last snapshot at or before the instant you ask for, so it can
be up to a flush interval earlier — never later, and always a state
that really existed. Matching DynamoDB's any-second granularity means
retaining a change log for the whole window, which is what makes
their point-in-time recovery a per-GB charge and ours free. The flush
interval is a flag if you want the window narrower.
DynamoDB sells storage, requests and durability as one meter, and the price of the bundle is set by the service. Flint KV puts the data in an object store you already pay for and runs compute you already choose, so each is priced by the market instead.
| data you keep, per GB-month | published rate |
|---|---|
| Amazon S3 standard — where Flint KV keeps it | $0.023 |
| DynamoDB stored data | $0.250 |
| DynamoDB point-in-time recovery, on top | $0.200 |
| What a stored gigabyte costs, with recovery on | $0.023 against $0.450 — about twenty times |
Rates from the AWS price lists for us-east-1,
read 19 August 2026:
S3 and
DynamoDB on-demand.
Prices move; read them again before quoting these.
Why the fraction falls as you grow. Requests scale with traffic; storage scales with the data. A workload whose traffic is steady while its history accumulates — which is most of them — moves more of its bill into the term above every month. Recovery is the sharpest case: DynamoDB charges $0.20 per GB-month to keep a window open, and Flint KV charges nothing for it, because the versions it would restore from are already the way the data is stored.
What Flint KV will cost. It will be listed and priced on AWS Marketplace, metered through the AWS account you already have, and the price will be published there at release rather than negotiated. Until it is listed, "about a fifth" is a model rather than a quote: it assumes a 10,000 ops/s workload at 20 TB against the published rates above, and we say about a fifth because a headline number should be one that still holds after AWS changes a price.
If you want the number for your workload rather than a modelled one, tell us the item size, the read and write mix and how much you keep, and we will run it.
Not retrofitted. Incidents, the wrong conclusions nearly drawn and the check that settled each one are captured as material an operations agent can act on, from the first day of the build. The same choice behind Flint Cache.
Each subsystem comes with a drill that breaks it on purpose and proves the guard catches it — the sort order that makes a file answer reads with silence, the sweep that reports a healthy pass while storage grows. A failure mode nobody can trigger on demand is one nobody has really tested.
For teams that cannot put their data in a public cloud — regulated, air-gapped, or simply committed to their own hardware — the source and the operations agent are licensed together, so you run it yourself with the same operational knowledge behind it that we run it with. Talk to us about terms.
The data sits in a bucket you own, in your account, in a documented format — S3, or any S3-compatible store on your own hardware. Nothing about leaving is a migration project, which is the point of keeping it somewhere you can already read it.
Flint KV is a separate product from Flint Cache. The two ship, price, version and deploy independently, and neither requires the other.
If you run a DynamoDB-shaped workload and the bill or the lock-in is the problem, we would like to hear what the workload actually looks like — item sizes, read/write mix, how much you keep, and what you have already tried. Early access is issued by hand while the core settles.