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 */>.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file natsio_kvcodec-0.1.0.tar.gz.
File metadata
- Download URL: natsio_kvcodec-0.1.0.tar.gz
- Upload date:
- Size: 9.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
400d92977e8aa773a4aadf4931968d8513632be2bc6dba9d55d519d432708a5b
|
|
| MD5 |
c9736993c473d7b6280fdeffffc6968d
|
|
| BLAKE2b-256 |
898f8b63361a2d807a0656257096a3dd1a7f34e5d048bb4655c364126f8dc913
|
Provenance
The following attestation bundles were made for natsio_kvcodec-0.1.0.tar.gz:
Publisher:
release-extension.yml on corruptmane/natsio
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
natsio_kvcodec-0.1.0.tar.gz -
Subject digest:
400d92977e8aa773a4aadf4931968d8513632be2bc6dba9d55d519d432708a5b - Sigstore transparency entry: 2217857738
- Sigstore integration time:
-
Permalink:
corruptmane/natsio@5157819ff1376c36249215ef4d150e5d670404c3 -
Branch / Tag:
refs/tags/kvcodec/v0.1.0 - Owner: https://github.com/corruptmane
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-extension.yml@5157819ff1376c36249215ef4d150e5d670404c3 -
Trigger Event:
push
-
Statement type:
File details
Details for the file natsio_kvcodec-0.1.0-py3-none-any.whl.
File metadata
- Download URL: natsio_kvcodec-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e03995bb1311bee969a7277e61c6053f58b3108da1ef5a3568bda95427dc5d4f
|
|
| MD5 |
582b515c4e04fe3a12b2d0df421651ec
|
|
| BLAKE2b-256 |
5cc28d7c35cb7f33422b28ce59e06443c76f10cedd1aba2569b976fbf513d73a
|
Provenance
The following attestation bundles were made for natsio_kvcodec-0.1.0-py3-none-any.whl:
Publisher:
release-extension.yml on corruptmane/natsio
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
natsio_kvcodec-0.1.0-py3-none-any.whl -
Subject digest:
e03995bb1311bee969a7277e61c6053f58b3108da1ef5a3568bda95427dc5d4f - Sigstore transparency entry: 2217858230
- Sigstore integration time:
-
Permalink:
corruptmane/natsio@5157819ff1376c36249215ef4d150e5d670404c3 -
Branch / Tag:
refs/tags/kvcodec/v0.1.0 - Owner: https://github.com/corruptmane
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-extension.yml@5157819ff1376c36249215ef4d150e5d670404c3 -
Trigger Event:
push
-
Statement type: