Skip to main content

Vaultlet

Vaultlet is an async, encrypted, multi-tenant persistent key-value store for Python, powered by Rust. It is designed for credentials, sessions, agent state, checkpoints, cached data, and other sensitive application data.

The Rust core performs key derivation, authenticated encryption, expiry enforcement, key enumeration, and durable backend transactions without handing plaintext to the storage engine. Tenant-scoped handles make isolation part of every data operation. Values can be opaque bytes or an explicit JSON-compatible subset; no pickle or Python object deserialization is used.

Requirements

  • CPython 3.12, 3.13, or 3.14
  • A supported manylinux, musllinux, macOS, or Windows native wheel
  • Redis 7.2 or newer with AOF enabled when using RedisBackend

Vaultlet uses the CPython 3.12 stable ABI. Standard GIL-enabled CPython builds are supported; free-threaded CPython builds require a separately compiled artifact.

Installation

pip install vaultlet

Quick start

import asyncio
from datetime import timedelta
from pathlib import Path
from tempfile import TemporaryDirectory

import vaultlet


async def main() -> None:
    # This walkthrough is intentionally disposable. Persistent stores must restore
    # the same key from a secret manager on every open.
    with TemporaryDirectory(prefix="vaultlet-quickstart-") as directory:
        key = vaultlet.MasterKey.generate()
        async with await vaultlet.Vaultlet.open(
            vaultlet.FileBackend(Path(directory) / "state.vaultlet"),
            key=key,
        ) as store:
            tenant = store.tenant("customer-123")
            await tenant.set("token", b"secret", ttl=timedelta(hours=1))
            await tenant.set_json("checkpoint", {"step": 4, "messages": []})
            checkpoint = await tenant.get_json("checkpoint")
            if checkpoint is None:
                raise RuntimeError("checkpoint did not round-trip")

            stored_keys: list[str] = []
            cursor = None
            while True:
                listing = await tenant.keys(limit=100, cursor=cursor)
                stored_keys.extend(listing.keys)
                if listing.next_cursor is None:
                    break
                cursor = listing.next_cursor
            if set(stored_keys) != {"checkpoint", "token"}:
                raise RuntimeError("key listing was incomplete")


asyncio.run(main())

The master key is never stored in the Vaultlet backend. export_bytes() and export_base64() are deliberately explicit because the application owns key provisioning, backup, and access control. For a persistent store, generate and save the key once, then obtain it from protected configuration and restore it with MasterKey.from_bytes(...) or MasterKey.from_base64(...). Losing the key makes the store unrecoverable.

FileBackend defaults to SQLite in WAL mode, which supports independent processes opening and coordinating through the same local store. Select redb explicitly for exclusive single-process ownership:

backend = vaultlet.FileBackend(
    Path("state.vaultlet"),
    engine=vaultlet.StorageEngine.REDB,
)

SQLite and redb files use different container formats. Reopen a file with the engine that created it. SQLite is the required file engine for multi-process deployments; redb returns StoreLockedError when another owner has the file open.

For a shared network backend, configure one stable Redis namespace and keep credentials outside the endpoint:

import os

backend = vaultlet.RedisBackend(
    os.environ["VAULTLET_REDIS_ENDPOINT"],  # redis:// or rediss://
    namespace="orders-production",
    username=os.environ.get("VAULTLET_REDIS_USERNAME"),
    password=os.environ.get("VAULTLET_REDIS_PASSWORD"),
)

rediss:// validates the server certificate against bundled public Web PKI roots; standard managed Redis certificates need no CA, client-certificate, or client-key arguments. The endpoint must not contain credentials, so exceptions and object representations cannot accidentally expose them. The selected Redis database is read from the endpoint path, such as /2.

Redis AOF is required because every mutation is followed by WAITAOF before Vaultlet acknowledges it. The Redis ACL must permit the namespace's keys plus the hash, sorted-set, scripting, and WAITAOF commands used by Vaultlet. Each namespace maps to three Redis keys in one hash slot; use a unique, stable namespace for each logical store and never point unrelated master keys at the same namespace. Connect through a standalone Redis endpoint or a compatible routing proxy.

Bytes, JSON, and atomic batches

Bytes are the primitive API. Values may be any C-contiguous Python buffer and reads return immutable bytes:

tenant = store.tenant("customer-123")
await tenant.set_many({"access": b"a", "refresh": bytearray(b"b")})
tokens = await tenant.get_many(["refresh", "access", "missing"])

JSON methods accept None, booleans, finite 64-bit numbers, strings, lists, and string-keyed dictionaries. They reject cycles, non-finite floats, oversized integers, arbitrary objects, and pickle. JSON objects and arrays are returned as immutable JsonObject and JsonArray views backed by the authenticated MessagePack buffer. The complete structure is validated off the event-loop thread, while nested views and Python scalar objects are created only when accessed:

await tenant.set_many_json(
    {
        "checkpoint": {"step": 5, "messages": []},
        "pending-writes": [],
    }
)

checkpoint = await tenant.get_json("checkpoint")
if isinstance(checkpoint, vaultlet.JsonObject):
    step = checkpoint["step"]
    mutable_checkpoint = checkpoint.to_builtin()

The decrypted buffer is zeroized after its final related view is released. Passing a returned view back to set_json or set_many_json copies its already validated MessagePack directly without traversing a Python container graph.

Each set_many, set_many_json, or delete_many call is one durable atomic transaction. Each bulk read observes one database snapshot and preserves input order in its returned dictionary; JSON values inside get_many_json results remain immutable views. Reading JSON as bytes, or bytes as JSON, raises TypeMismatchError. Batch calls accept at most MAX_BATCH_ITEMS items, and the aggregate encoded values in a write batch may not exceed MAX_BATCH_VALUE_BYTES (64 MiB). Split larger workloads into multiple operations.

await tenant.keys(limit=..., cursor=...) returns one KeyListing page containing at most that many live keys for exactly one tenant, sorted lexically within that page. The default limit is 1,000 and MAX_KEY_LIST_LIMIT is 10,000. When next_cursor is present, pass it unchanged to the next call; has_more remains as a convenience indicator. Cursors are opaque, versioned, and bound to the originating store and tenant. Each page observes its own database snapshot, so concurrent catalogue changes can affect a multi-page traversal.

The backend reads at most one encrypted lookahead row beyond the limit and decrypts at most the requested number. Expired entries in the bounded slice are omitted and cleaned, so a page can contain fewer keys than its limit while a continuation cursor is present. Catalogue changes and value mutations share one transaction, including when SQLite writers run in separate processes or Redis clients run on separate hosts.

Expiry and lifecycle

ttl accepts finite non-negative seconds or datetime.timedelta. expires_at accepts a timezone-aware datetime; the options are mutually exclusive. Expired records are immediately absent from reads. Lazy deletion, a bounded background worker, and await store.purge_expired() remove their encrypted storage.

Open stores should always be closed with async with or await store.aclose(). SQLite and Redis stores may have multiple owners; redb stores have one exclusive owner. A cancelled mutating awaitable is still atomic: cancellation can be observed by Python even though the whole transaction subsequently commits.

Security and operation

Vaultlet blinds tenant and key identifiers and encrypts each value with a distinct XChaCha20-Poly1305 nonce and a tenant-derived key. Authenticated metadata binds the store identity, record identity, encoding, expiry, and revision. The redb backend uses immediate two-phase commits; SQLite uses WAL with full synchronization. Both create new database files with mode 0600 on Unix. Windows files use the directory's inherited ACL.

The Redis backend uses atomic Lua scripts and confirms local AOF persistence with WAITAOF. Use rediss:// whenever traffic can cross an untrusted network, and apply normal Redis access controls, network isolation, persistence monitoring, and backup policy.

Choose stable, application-defined tenant IDs such as internal account UUIDs. Bearer tokens, API keys, session IDs, and other rotating credentials belong in the authentication layer; using one as a tenant ID would select a different encrypted namespace when that credential changes.

await store.rotate_master_key(replacement) atomically rewraps the stable internal data key. Existing open processes continue using the same data schedule, while future opens must use the replacement master key.

Tenant handles prevent accidental cross-tenant access; they do not authenticate callers. Applications must map authenticated callers to trusted tenant IDs. Network filesystems are not supported for file backends. See Security, Architecture, and Storage Format for the complete operational contract.

Development and benchmarks

See Development for the toolchain and validation commands. Benchmarks documents reproducible workloads and how to report results without treating unencrypted stores as security-equivalent comparisons.

License

Vaultlet is available under the MIT License.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vaultlet-0.1.0.tar.gz (87.1 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

vaultlet-0.1.0-cp312-abi3-win_amd64.whl (3.6 MB view details)

Uploaded CPython 3.12+Windows x86-64

vaultlet-0.1.0-cp312-abi3-musllinux_1_2_x86_64.whl (3.9 MB view details)

Uploaded CPython 3.12+musllinux: musl 1.2+ x86-64

vaultlet-0.1.0-cp312-abi3-musllinux_1_2_aarch64.whl (3.7 MB view details)

Uploaded CPython 3.12+musllinux: musl 1.2+ ARM64

vaultlet-0.1.0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.7 MB view details)

Uploaded CPython 3.12+manylinux: glibc 2.17+ x86-64

vaultlet-0.1.0-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (3.5 MB view details)

Uploaded CPython 3.12+manylinux: glibc 2.17+ ARM64

vaultlet-0.1.0-cp312-abi3-macosx_11_0_arm64.whl (3.3 MB view details)

Uploaded CPython 3.12+macOS 11.0+ ARM64

vaultlet-0.1.0-cp312-abi3-macosx_10_12_x86_64.whl (3.5 MB view details)

Uploaded CPython 3.12+macOS 10.12+ x86-64

File details

Details for the file vaultlet-0.1.0.tar.gz.

File metadata

  • Download URL: vaultlet-0.1.0.tar.gz
  • Upload date:
  • Size: 87.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for vaultlet-0.1.0.tar.gz
Algorithm Hash digest
SHA256 0a23e93d788e1b5e090bb6e7139281b43c7e2a0963d609dd42b263097905d12f
MD5 f53dc88e819a7eb8677499ee1e499eaa
BLAKE2b-256 7964bc4a176f48e7d06a49edb9c0b4b0ea7731c56c16d899aaa636bfc30e59ce

See more details on using hashes here.

Provenance

The following attestation bundles were made for vaultlet-0.1.0.tar.gz:

Publisher: publish.yml on s-block/vaultlet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vaultlet-0.1.0-cp312-abi3-win_amd64.whl.

File metadata

  • Download URL: vaultlet-0.1.0-cp312-abi3-win_amd64.whl
  • Upload date:
  • Size: 3.6 MB
  • Tags: CPython 3.12+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for vaultlet-0.1.0-cp312-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 93ba748caea9bed0c53e71faa3eabcbaeeab647c40516be2fd8e42b0402176c6
MD5 fd2b9e0342d8aa0e5a128c7e2e9e6a62
BLAKE2b-256 9179335a22781fb04d62599320982a48b750607bc558a6a3f63a9c6d66f30aaf

See more details on using hashes here.

Provenance

The following attestation bundles were made for vaultlet-0.1.0-cp312-abi3-win_amd64.whl:

Publisher: publish.yml on s-block/vaultlet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vaultlet-0.1.0-cp312-abi3-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for vaultlet-0.1.0-cp312-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 4de59d71e36dff45eed1af5b06d709a43e51cbb116000953ca4715e580f1f030
MD5 4d1095eeae587a13be5c709fcced7059
BLAKE2b-256 c1c5dd0eabf59f8ca59d42a96e036f4cf98400f12f9e0389f8ac0a6d7471f2db

See more details on using hashes here.

Provenance

The following attestation bundles were made for vaultlet-0.1.0-cp312-abi3-musllinux_1_2_x86_64.whl:

Publisher: publish.yml on s-block/vaultlet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vaultlet-0.1.0-cp312-abi3-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for vaultlet-0.1.0-cp312-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 23c950967f1a4d8c90e5cdfc188a4d31b6ef0fa3caeb68184a3452fda9da2c66
MD5 22143b95cbacd1bd4a9a308113defaa9
BLAKE2b-256 8715f6b2ffa961a6de01eaf172ac9b5c88cd5494b8f2662931f4eca47365ec73

See more details on using hashes here.

Provenance

The following attestation bundles were made for vaultlet-0.1.0-cp312-abi3-musllinux_1_2_aarch64.whl:

Publisher: publish.yml on s-block/vaultlet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vaultlet-0.1.0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for vaultlet-0.1.0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 e9d1258a6b1928074389f47fcad7ca6be4fe564c988a659314f8c81ad13db141
MD5 1a950bdf7f654b38d56c1701d3defde8
BLAKE2b-256 9d62ca0535fe586ecccc1b9a876f010cd9db04af9b192591b41a76c6ddcedd5f

See more details on using hashes here.

Provenance

The following attestation bundles were made for vaultlet-0.1.0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish.yml on s-block/vaultlet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vaultlet-0.1.0-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for vaultlet-0.1.0-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 3425712e519ab8c9f43b4e68fe622d1a25c073bb5515099011a66646690f5b34
MD5 5fd4f6bb48fd71776fda129f7891e1ff
BLAKE2b-256 a11b3d3907bffd887b847f70a5da5dc849a990261f000ed12665d2d8bc483b30

See more details on using hashes here.

Provenance

The following attestation bundles were made for vaultlet-0.1.0-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: publish.yml on s-block/vaultlet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vaultlet-0.1.0-cp312-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for vaultlet-0.1.0-cp312-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 1a2ac7d10ddcd5fff2e7e55894bf247f4a0c7f4f482a2d377b3ff356abebbe34
MD5 5302f4a4cd9f8d14e7ec7b77849ace5a
BLAKE2b-256 78e88abe3bb2ed8ffe1377a9e1abfd5f66b54b171dac79a8711340c8c9179d55

See more details on using hashes here.

Provenance

The following attestation bundles were made for vaultlet-0.1.0-cp312-abi3-macosx_11_0_arm64.whl:

Publisher: publish.yml on s-block/vaultlet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vaultlet-0.1.0-cp312-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for vaultlet-0.1.0-cp312-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 af44115a9def2272a1bfbf31fcb1ed5e5197fe0ed1ef539582cfb0f5d4f72066
MD5 14e0fa1c880bcfce9280ef8558d8bcfc
BLAKE2b-256 301b284751fc93979ba2952b378f0e2ccd37506a92422170eee66eef7a9741ea

See more details on using hashes here.

Provenance

The following attestation bundles were made for vaultlet-0.1.0-cp312-abi3-macosx_10_12_x86_64.whl:

Publisher: publish.yml on s-block/vaultlet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page