natsio-kvcodec
Transparent key/value codecs for natsio's KV store, over the core's
KeyCodec / ValueCodec seam. Encode keys and values on the way in, decode
them on the way out — get(), keys(), watch(), and history() all speak the
decoded form while the bucket stores the encoded form.
Mirrors orbit.go/kvcodec,
pythonized. Stdlib only; no runtime dependencies beyond natsio.
pip install natsio-kvcodec
Usage
Pass codecs to any create_key_value / key_value variant — the natsio core
already accepts key_codec= and value_codec=:
from natsio.kv import KeyValueConfig
from natsio.kvcodec import PathKeyCodec, ZlibValueCodec
kv = await js.create_key_value(
KeyValueConfig(bucket="cfg"),
key_codec=PathKeyCodec(), # "/app/db/url" stored as "app.db.url"
value_codec=ZlibValueCodec(), # values compressed at rest & over the wire
)
await kv.put("/app/db/url", b"postgres://localhost")
entry = await kv.get("/app/db/url") # entry.key == "/app/db/url"
await kv.keys() # ["/app/db/url"] — DECODED keys
Key and value codecs are independent; use either, both, or neither.
Codecs
Key codecs (str -> str)
| Codec | What it does | orbit.go parity |
|---|---|---|
Base64KeyCodec |
Per-token raw URL-safe base64 (dots kept as separators). Lets a key carry characters illegal in NATS subjects — modulo the core-friction caveat below. | Base64Codec (key side) |
PathKeyCodec |
Filesystem keys /a/b/c <-> NATS a.b.c. Leading / becomes the _root_ sentinel, trailing / trimmed. |
PathCodec |
NoOpKeyCodec |
Identity (the core's key_codec=None already means identity; useful as a chain filler). |
NoOpCodec |
ChainKeyCodec(*codecs) |
Apply key codecs in sequence (encode first->last, decode last->first). | KeyChainCodec |
Every key codec guarantees its encoded output satisfies
natsio.kv.validate_key (the raw URL-safe base64 alphabet and _root_
sentinel are all NATS-legal), so the core accepts the encoded key as a subject.
Base64KeyCodec, PathKeyCodec, NoOpKeyCodec, and an all-filterable
ChainKeyCodec also implement encode_filter(pattern) (the core's
runtime-checkable natsio.kv.FilterableKeyCodec protocol), which encodes a
wildcard pattern while preserving */>. The natsio core calls this from
watch(), so a wildcard watch under one of these codecs is encoded per token
(orders.> -> b3JkZXJz.>) and works end-to-end — see Core Friction.
Value codecs (bytes -> bytes)
| Codec | What it does | orbit.go parity |
|---|---|---|
ZlibValueCodec(level=-1) |
Transparent DEFLATE via stdlib zlib. |
natsio addition |
Base64ValueCodec |
Whole-value raw URL-safe base64. | Base64Codec (value side) |
NoOpValueCodec |
Identity. | NoOpCodec |
ChainValueCodec(*codecs) |
Apply value codecs in sequence. | ValueChainCodec |
ZlibValueCodec tradeoffs. It trades CPU for bytes, and small or
incompressible values come out larger (a ~6-byte zlib envelope). It is not
encryption. If you only want at-rest compression, prefer
KeyValueConfig(compression=True) (server-side S2, zero client CPU); reach for
this codec when you want the bytes compressed over the wire and counted in
max_bytes.
Errors
Codecs fail loud (like orbit.go's error returns), never silently pass corrupt
data through:
NoCodecsError— emptyChainKeyCodec()/ChainValueCodec().KeyDecodeError/ValueDecodeError— corrupt/non-encoded input (a non-base64 token, a bad zlib stream).WildcardNotSupportedError—encode_filteron a chain with a non-filterable member.
All derive from KvCodecError.
orbit.go parity notes
- Two classes per base64, not one. Go's structural typing lets a single
Base64Codecsatisfy bothKeyCodecandValueCodec. natsio's protocols have differentencodesignatures (str->strvsbytes->bytes), so this shipsBase64KeyCodecandBase64ValueCodec. - Naming. orbit uses
EncodeKey/DecodeKey; the natsio seam usesencode/decode, so codecs follow the natsio spelling. ZlibValueCodecis new. orbit ships only base64 for values; a value codec is the natural home for compression, so natsio adds one.- Exact base64/path/filter test vectors are ported from orbit's
codec_test.go/chain_codec_test.go, so behavior matches byte-for-byte (Base64KeyCodec().encode("test.key...") == "dGVzdA.a2V5...", etc.). PathKeyCodecdot caveat (shared with orbit). It maps/<->.and cannot distinguish a literal.in the input from a separator, so feed it path-style keys;a.bdecodes back asa/b.
Core friction
Building this extension stress-tested the codec seam. Two real gaps surfaced (neither is a codec bug — both are in the core, documented here so the behavior isn't surprising):
-
The raw key is validated before the codec runs, defeating Base64's main use case.
KeyValue._encode_keycallsvalidate_key(raw_key)and thenvalidate_key(codec.encode(raw_key)). Soput("Acme Inc.contact", ...)withBase64KeyCodecraisesInvalidKeyErroron the space — even though the codec would turn it into the perfectly validQWNtZSBJbmM.Y29udGFjdA. The escape- exotic-characters scenario from orbit's own README cannot work end-to-end. Proposal: when akey_codecis set, validate only the encoded key; the raw key is the user's domain and only the stored subject must be NATS-legal. -
keys()/iter_keys()(andpurge_deletes()) still run the value codec on a payload the server deliberately stripped. Those paths use a headers-only (meta_only) watch, so every delivery has an empty payload — but_entry_from_msgunconditionally callsvalue_codec.decode(payload)forPUTentries. Any value codec that can't decodeb""(ZlibValueCodec, encryption, length-prefixed framing) makeskeys()raiseValueDecodeError. Proposal: skip value decoding for headers-only deliveries (or when the payload is empty undermeta_only). Pinned by a strictxfailintest_kvcodec_live.py::test_keys_under_framing_value_codec_is_broken.
A third one — the filter hook — is now closed. orbit's FilterableKeyCodec
lets a wildcard watch encode tokens individually (orders.> -> b3JkZXJz.>).
The natsio core mirrors this: watch() recognises the runtime-checkable
natsio.kv.FilterableKeyCodec protocol (which the codecs here implement) and
calls encode_filter(), so per-token wildcard watches work end-to-end. Under a
codec the raw filter is the caller's domain (it may be in the codec's own
notation, e.g. PathKeyCodec's /a/*) — only the encoded filter must be a
legal subject filter. A wildcard watch under a non-filterable codec is still
refused (ConfigError): encoding the whole key would mangle the */>.
Example
A runnable script is at examples/basic.py — start a server with just server, then:
python extensions/natsio-kvcodec/examples/basic.py
Metadata
Release files for natsio-kvcodec 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| natsio_kvcodec-0.1.1.tar.gz | 10.0 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| natsio_kvcodec-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.0 kB
Release files / natsio_kvcodec-0.1.1.tar.gz
| Download URL | natsio_kvcodec-0.1.1.tar.gz |
|---|---|
| Size | 10.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
086c9e1207b961b32b018df2557ad84bac4b4c902c05a10420803fb1b5221a2d
|
|
BLAKE2b-256 checksum How to use checksums |
d848c53ddc1287760038b71c38783ecce1f5f5a08060fff483b13f4a176b9eb5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.
Transparency logRelease files / natsio_kvcodec-0.1.1-py3-none-any.whl
| Download URL | natsio_kvcodec-0.1.1-py3-none-any.whl |
|---|---|
| Size | 12.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3e334e02ac3f918633a155f5e8257dc6851c9ed63516c7381032687dd9485b89
|
|
BLAKE2b-256 checksum How to use checksums |
a59546748c8c2a1dddc8de50c83cf24c60e30423aa39543f569ce417cddc52ed
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.
Transparency logSecurity for natsio-kvcodec 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for natsio_kvcodec-0.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.