nkv is a redesigned Go client for NATS JetStream Key-Value buckets. It keeps the
standard KV wire format so buckets written by nkv and nats.go are fully
interoperable, while replacing several costly client-side paths: point reads and
enumeration use Direct Get instead of creating ephemeral consumers, watches use
client-driven pull consumers for native backpressure, atomic transactions for grouped
writes, and an API with per-operation options and typed values.
It is a deliberate alternative to the nats.go jetstream.KeyValue client, not a
drop-in replacement. The stream layout (KV_<bucket>), subjects
($KV.<bucket>.<key>), KV-Operation headers, and rollup purges remain compatible;
the public method signatures do not.
Installation
go get github.com/blizzard/nkv.go@latestExamples
See the examples guide for focused, runnable programs covering
core operations, typed JSON values, optimistic concurrency, enumeration, watches,
per-key TTL, atomic transactions, and nats.go interoperability. Each example is a
standalone Go module with instructions for starting NATS Server and running the code.
Pre-release. The API is not finalized and will change without notice.
nkv is at v0.x. Until a v1.0.0 release is tagged:
- Any release may introduce breaking changes to exported types, functions, and behavior — including within a single minor version.
- No deprecation period is guaranteed. Symbols may be renamed or removed outright.
- Pin an exact version and read the release notes before upgrading.
Wire compatibility with the standard NATS KV layout is the one thing treated as
stable: buckets written by nkv remain readable by nats.go, and vice versa. Data
written today will not be orphaned by a future API change.
Wire compatibility describes the stored subjects, headers, and values; it does not
relax bucket configuration validation. Open requires every setting listed below and
therefore rejects a bucket created with the default nats.go KV configuration,
which does not enable per-message TTL or atomic publish. Update the backing stream with
the required settings before opening it with nkv.
Once v1.0.0 is released, the module follows Semantic Versioning
and the exported API becomes subject to the usual Go compatibility guarantees.
| Minimum | Why | |
|---|---|---|
| Go | 1.26 | Declared in go.mod. |
nats-server |
2.14.0 | Supported and tested server baseline. |
nats.go |
v1.52.0 | Client support for the above. |
CreateBucket enforces and Open requires a stream configuration that enables every
feature below. Although the newest required feature was introduced in 2.12.0, nkv
supports NATS Server 2.14.0 and newer.
| Feature | Stream setting / header | Used by | Since |
|---|---|---|---|
| Direct Get | AllowDirect |
Get, List, Keys |
2.9.0 |
| Multi-subject consumer filters | FilterSubjects |
Watch with WithAdditionalKeys |
2.10.0 |
| Batched Direct Get | batch, next_by_subj, up_to_seq |
List, Keys paging |
2.11.0 |
| Per-message TTL | AllowMsgTTL, Nats-TTL |
WithTTL |
2.11.0 |
| Limit markers | SubjectDeleteMarkerTTL, Nats-Marker-Reason |
TTL/max-age tombstone detection | 2.11.0 |
| Atomic batch publish | AllowAtomicPublish, Nats-Batch-* |
Tx, GenericTx |
2.12.0 |
List and Keys page through Direct Get rather than creating ephemeral consumers, and
Watch uses a pull ordered consumer — neither leaves server-side state behind.
CreateBucket defaults SubjectDeleteMarkerTTL to one minute so TTL and max-age
expirations remain observable long enough for watchers to receive the server-generated
marker. Set a longer value through Config.StreamConfig when watchers may be disconnected
for longer periods. Values below one second are rejected by both nkv and the server.
Warning
nkv expects the KV-specific settings shown in the command below. Omitting or
changing those settings causes Open to reject the stream. Deployment-specific
settings may be changed as needed, including --replicas, --storage, resource
limits such as --max-bytes, and placement, mirror, or source configuration.
Instead of calling CreateBucket, you can create the underlying KV stream with the
NATS CLI. This command was verified against nats CLI v0.4.0
(nats stream add --help):
BUCKET_NAME=MY_BUCKET
stream_args=(
"KV_${BUCKET_NAME}"
--defaults
--subjects "\$KV.${BUCKET_NAME}.>" # required
--retention limits # required
--discard new # required
--max-msgs-per-subject 1 # required
--allow-rollup # required
--deny-delete # required
--allow-direct # required
--allow-msg-ttl # required
--subject-del-markers-ttl 1m # required; can increase
--allow-batch # required
--storage file # can change
--replicas 1 # can change
)
nats stream add "${stream_args[@]}"BUCKET_NAME supplies both the bucket name and the required naming relationship: a
bucket named MY_BUCKET uses stream KV_MY_BUCKET and subject $KV.MY_BUCKET.>.
--replicas 1 is suitable for a single-node JetStream cluster but provides no replica
redundancy.
CreateBucket automatically updates an existing bucket to the required configuration.
To perform the same upgrade with the NATS CLI while retaining the bucket's messages,
edit its backing stream:
BUCKET_NAME=MY_BUCKET
stream_args=(
"KV_${BUCKET_NAME}"
--force
--subjects "\$KV.${BUCKET_NAME}.>" # required
--retention limits # required
--discard new # required
--max-msgs-per-subject 1 # required
--allow-rollup # required
--deny-delete # required
--allow-direct # required
--allow-msg-ttl # required
--allow-batch # required
--subject-del-markers-ttl 1m # required; can increase
)
nats stream edit "${stream_args[@]}"The command enforces the nkv-specific settings without changing deployment settings such as storage, replicas, resource limits, placement, mirrors, or sources. The delete marker TTL controls how long TTL and max-age expiration markers remain available to watchers; increase it when watchers may be disconnected for longer than one minute.
See CONTRIBUTING.md for the module layout, how to run the test suite, and the dependency rules.
To report a vulnerability, see SECURITY.md. Please do not open a public issue.
See LICENSE.