gemstone_utils release notes¶
Pre-release naming (gemstone_utils)¶
Pre-release versions follow PEP 440 ordering (dev < a < b < rc < final). For this project, the qualifiers mean:
dev(e.g.0.3.0.dev0): Development on the main integration line; not a promise that every commit is green. Intended for contributors and early git installs, not as a stability guarantee.a(alpha, e.g.0.3.0a2): New features and API changes are still allowed. The tree is expected to build, and happy-path tests pass under normal CI conditions—not a promise of production readiness.b(beta, e.g.0.3.0b1): API stable for that line; suitable for real-world integration testing and feedback. Further changes should be fixes and polish, not redesign.rc(release candidate, e.g.0.3.0rc1): Treated as release-ready pending last checks; feature freeze except for regressions, docs, and blockers. If nothing new surfaces, the final release matches the RC artifact.
Releases before 1.0 may skip stages (for example a minor may go straight from 0.2.x to 0.3.0 without publishing an alpha or beta) when the change set is small.
Unreleased¶
Nothing yet.
v0.5.0a1 (alpha)¶
Tag: v0.5.0a1
PyPI: pip install gemstone_utils==0.5.0a1 or pip install --pre gemstone_utils (see pre-release install behavior).
Overview¶
First alpha toward v0.5.0. Delivers security hardening for the secrets resolver and key/algorithm registration (breaking changes). API and behavior may still change before the stable release; see the pre-release naming section above. Pin gemstone_utils==0.4.1 for the current stable line without these breaking changes.
Changes since v0.4.1¶
Breaking changes¶
Removed
azexpbackend:gemstone_utils.experimental.azexp_backendis deleted. Theazexp:prefix and optional extragemstone_utils[azure]are no longer supported.Colon references require a backend: Any value containing
:must use a registered prefix (env,file,secret,literal, orregister_backend). Unknown prefixes raiseBackendNotImplemented(reason="unregistered"). Removed prefixes (e.g.azexp) raiseBackendNotImplemented(reason="removed").New built-in
literal:— returns the substring after the first colon unchanged (for URLs, connection strings, or other opaque values). Still runs encrypted-field post-processing when applicable.SYM_ALG_REGISTRYremoved: Useis_supported_sym_alg/SUPPORTED_SYM_ALGS; the symmetric registry is no longer a public mutable dict.register_kdfallowlisted: Only first-party KDF ids in_ALLOWED_KDF_NAMESmay register; third-party runtime registration is unsupported.parse_encrypted_fieldstricter: Unknown symmetric algorithm ids in the wire format raise at parse time (previously failed later at decrypt).set_kdf_paramsstricter: Rejects unsupportedparams["kdf"]at persist time.file:path allowlist: Default allowed prefix is/app/secretonly; override withset_allowed_file_path_prefixes. Paths must be absolute;~is rejected.secret:name charset: Names must start with a letter, end with a letter or digit, and contain only[A-Za-z0-9_-](no trailing-or_).
Migration¶
http://host/path→literal:http://host/pathazexp:https://vault.vault.azure.net/secrets/foo→secret:foowhen the platform mounts Key Vault secrets as files (Azure Container Apps, quadlet, etc.), orfile:/mount/path/foofor a custom mount pathOpaque legacy
azexp:...text →literal:azexp:...Plain strings without
:are unchangedApps that mutated
SYM_ALG_REGISTRYor calledregister_kdffor custom algorithms: add names to gemstone_utils allowlists in a fork, or stay on v0.4.x. No data migration is required for existingA256GCM/pbkdf2-hmac-sha256deployments.file:bootstrap: Usefile:/app/secret/...in containers, or callset_allowed_file_path_prefixes([...])at startup for bare-metal/dev paths (e.g./etc/myapp/secrets). Do not rely on relative paths or~.secret:names with dots or slashes: rename mounts to match thesecret:naming rules or usefile:under a narrow prefix.
v0.4.1¶
Tag: v0.4.1
PyPI: pip install gemstone_utils==0.4.1
Overview¶
Current stable line for the gemstone_utils package; pin gemstone_utils==0.4.1 or a compatible 0.4.x range for production use without the 0.5.x security breaking changes.
Fixes¶
init_dbmulti-worker startup: PostgreSQL and MySQL / MariaDB schema creation now runs under a dialect advisory lock so concurrent workers (e.g. gunicorn) do not race onCREATE TABLEduring ephemeral-database bootstrap.PostgreSQL lock key: Uses
pg_advisory_xact_lock(int, int)with int32 keys (the initial single-key constant exceeded PostgreSQLbigintrange).
v0.4.0 (stable)¶
Tag: v0.4.0
PyPI: pip install gemstone_utils==0.4.0
Overview¶
Stable release matching v0.4.0rc1 with no intentional API or behavior changes. Superseded by v0.4.1 for production use.
v0.4.0rc1 (release candidate)¶
Tag: v0.4.0rc1
PyPI: pip install gemstone_utils==0.4.0rc1 or pip install --pre gemstone_utils (see pre-release install behavior).
Overview¶
First release candidate toward v0.4.0. This line has been exercised in a real application; further work on 0.4.x should follow the RC rules in the pre-release naming section above (feature freeze except for regressions, documentation, and release blockers).
Changes since v0.3.0a2¶
Breaking changes¶
Removed experimental SQL KV:
gemstone_utils.experimental.sqlexpandgemstone_utils.experimental.sqlexp_backendare deleted. Thesqlexp:prefix is no longer a recognized optional backend insecrets_resolver. For bootstrap secrets, useenv:,file:,secret:, orazexp:(withgemstone_utils[azure]). For application secrets afterinit_db, useEncryptedString/encrypted_fieldsor your own tables.
v0.3.0a2 (alpha)¶
Tag: v0.3.0a2
PyPI: pip install gemstone_utils==0.3.0a2 or pip install --pre gemstone_utils (see pre-release install behavior).
Overview¶
Second alpha toward v0.3.0. API and runtime behavior are unchanged from v0.3.0a1; this release updates published package metadata so the PyPI project homepage and other [project.urls] fields reflect the repository under the gemstone-software-dev organization.
Changes since v0.3.0a1¶
Packaging / documentation: Project homepage in
pyproject.tomland release-note URLs align with the org repo (see sections below). No code or API changes.
v0.3.0a1 (alpha)¶
Tag: v0.3.0a1
PyPI: pip install gemstone_utils==0.3.0a1 or pip install --pre gemstone_utils (see pre-release install behavior).
Overview¶
First alpha toward v0.3.0. API and behavior may still change before the stable release; see the pre-release naming section above.
Encrypted field wire format¶
Five
$-separated segments:$<alg>$<key_id>$<params_b64>$<blob_b64>where<alg>is a registered symmetric id (todayA256GCM).<key_id>is a canonical UUID string (typically UUIDv7 fromgemstone_utils.key_id.new_key_id()). Legacy integer segment values are rejected at parse time; migrate stored ciphertext before upgrading.Legacy four-part strings still emit a
DeprecationWarningand are scheduled for removal in 0.9.0; segment 2 must still be a UUID string when using the new parsers.Migration: Run a data migration (application-specific). A documentation-only outline lives at
scripts/migrate_key_ids.py. Migration support is documented through v0.9.0.
API changes (breaking)¶
parse_encrypted_field(value)returns(alg_id, keyid, params, blob)wherekeyidisstr(UUID text).KeyContext.keyidandKeyRecord.keyidusestr(UUID).KeyRecord.keyidmay beNonefor a KEK-check blob only;verify_kekrequireskeyid is None;unwrap_keyrequireskeyidset.EncryptedString.set_keyctx_resolverandsecrets_resolver.set_keyctx_resolvertakeCallable[[str], KeyContext].Breaking —
gemstone_utils.crypto: symmetric registry,encrypt_alg/decrypt_alg(see prior notes);encrypt_algreturns(ciphertext, updated_params).format_encrypted_field(alg, keyid, blob, params=None)—keyidisstr(UUID).Breaking —
gemstone_utils.sqlalchemy.key_storage:GemstoneKeyKdf.key_idandGemstoneKeyRecord.key_idareString(36)UUID text (nativeUuidcolumns are planned for v0.9.0).GemstoneKeyKdfaddscanary_wrappedandapp_reencrypt_pending. The KEK canary no longer usesgemstone_key_recordkey_id == 0; useset_kek_canary.GemstoneKeyRecordholds DEKs only.rewrap_key_recordsrewraps all KEK-slot canaries and all DEK rows. Existing databases require schema migration (or recreate).is_encrypted_prefix: unchanged behavior (registered algorithm segment).
Requirements¶
uuid6is installed automatically on Python < 3.14 for UUIDv7 generation (gemstone_utils.key_id). Python 3.14+ usesuuid.uuid7()from the standard library.
key_mgmt package and KDF registry¶
verify_kek/make_kek_check_record: KEK-check records useKeyRecord.keyid is None(no sentinel integer0).Breaking:
gemstone_utils.key_mgmtis now a package (key_mgmt/__init__.py,key_mgmt/registry.py,key_mgmt/kdf/…). Imports likefrom gemstone_utils.key_mgmt import derive_kekstill work.Registry:
register_kdfandderive_kek(passphrase, params)live inkey_mgmt.registryand are re-exported fromgemstone_utils.key_mgmt.recommended_kdf_params(salt=None)— single entry point for the library’s current recommended KDF params (today delegates tokey_mgmt.kdf.pbkdf2.recommended_pbkdf2_params).gemstone_utils.key_mgmt.kdf:RecommendedKdfParamsFnandHasKdfRegistryNameprotocols plus a package docstring describing the contract for algorithm submodules (NAME,recommended_<algo>_params, optional explicit params builder).gemstone_utils.key_mgmt.kdf.pbkdf2:NAME(pbkdf2-hmac-sha256),pbkdf2_params,recommended_pbkdf2_params, and the registered derive implementation (cryptographyPBKDF2HMAC). Persisted JSON must includesalt(url-safe base64);iterations/lengthdefault when omitted (currently 600_000 iterations and 32 bytes for SHA-256).Removed from
key_mgmtroot:pbkdf2_hmac_sha256_params,KDF_NAME_PBKDF2_HMAC_SHA256,DEFAULT_PBKDF2_DERIVED_KEY_LENGTH— usekdf.pbkdf2instead.
SQL key storage and KDF defaults¶
gemstone_utils.sqlalchemy.key_storage: See API changes above. New helpers:set_kek_canary,set_app_reencrypt_pending,iter_kek_slots.crypto:derive_pbkdf2_hmac_sha256(low-level primitive),DEFAULT_PBKDF2_ITERATIONS_STRONG, symmetric registry /encrypt_alg/generate_key_by_alg, andrecommended_data_alg()/RECOMMENDED_DATA_ALGas above.
Development¶
Optional extra:
pip install 'gemstone_utils[dev]'includes pytest; runpytestfrom the project root (tests/).
v0.2.1¶
Tag: v0.2.1
Overview¶
For the current stable release on this package line, use v0.4.0 (or newer 0.4.x as published). This section documents the rename-only milestone.
This release renames the distribution and Python package from emerald_utils to gemstone_utils (top-level package directory and pip install name). Behavior and public APIs are otherwise unchanged from v0.2.0; this is a branding and clarity update.
The project homepage URL is github.com/gemstone-software-dev/gemstone_utils. If your Git remote or older docs still reference emerald_utils, update them when you migrate.
Highlights¶
Breaking — package name: PyPI/install name is
gemstone_utils; import paths use thegemstone_utilspackage (for examplegemstone_utils.types,gemstone_utils.crypto).Optional extras: Use
pip install 'gemstone_utils[azure]'instead ofemerald_utils[azure].
Migration notes (from v0.2.0 / emerald_utils)¶
pip uninstall emerald_utils(if installed) andpip install gemstone_utils(pingemstone_utils==0.2.1if you want an exact version).Replace import prefixes
emerald_utils→gemstone_utilsacross your codebase (including experimental subpackages).Update dependency declarations (for example
pyproject.toml/requirements.txt) fromemerald_utilstogemstone_utils.
Encrypted data, KeyContext, and SQLAlchemy column behavior are unchanged; only names and install targets move.
Requirements¶
Python ≥ 3.10
Core:
cryptography≥ 41,sqlalchemy≥ 2.0Optional:
pip install 'gemstone_utils[azure]'for Key Vault
Installation¶
pip install gemstone_utils
Or from a GitHub release asset (after you publish v0.2.1):
pip install https://github.com/gemstone-software-dev/gemstone_utils/releases/download/v0.2.1/gemstone_utils-0.2.1.tar.gz
License¶
v0.2.0¶
Tag: v0.2.0
PyPI / import name (that release): emerald_utils — use v0.2.1+ (gemstone_utils) for the renamed package.
Overview¶
This release extends emerald_utils with key rotation–friendly SQLAlchemy APIs, shared types for keys and records, a small database bootstrap layer, optional Azure Key Vault resolution, pluggable secret backends, and SQL-backed leader election. Crypto gains algorithm-dispatch helpers for future symmetric algorithms; PBKDF2 derivation is renamed for accuracy (KEK vs data key).
Stable areas remain crypto, encrypted fields, and SQLAlchemy integration. Experimental modules are still minimal and not the final vault design.
Highlights¶
Breaking —
KeyContext: Moved toemerald_utils.types. Fields are nowkeyid,key, and optionalalg(default"A256GCM"). The formerdkfield iskey.Breaking — KDF helper:
derive_dk_from_passphraseinemerald_utils.cryptois renamed toderive_kek_from_passphrase.Breaking —
EncryptedString: Replacesset_keyctx()withset_current_keyctx()for the active write key andset_keyctx_resolver(callable)to resolve the correctKeyContextper storedkeyidon read (supports rotation and multiple keys).Crypto:
encrypt_with_alg/decrypt_with_algfor JWA-style symmetric dispatch (currentlyA256GCM); fixes that blocked correct key rotation behavior.KeyRecord: New type inemerald_utils.typesfor encrypted key material (keyid,alg,encrypted_key).key_mgmt: KEK verification (KEKVerificationError), KDF registry (register_kdf,derive_kek), wrap/unwrap, and helpers to buildKeyContextfrom stored records (replaces the olddk.pydirection).emerald_utils.db: Central DB setup with dynamic schema registration so modules and plugins can attach their own SQLAlchemy metadata.emerald_utils.election: Optional SQL-backed leader election (candidates, heartbeats, leases, namespaces).Experimental —
azexp:: Azure Key Vault URLs viaemerald_utils.experimental.azexp_backend; installemerald_utils[azure](azure-identity,azure-keyvault-secrets).Experimental —
secrets_resolver: Pluggable backends with register/unregister; caching refined for env, file, and secret sources; README updated for current backend usage.sqlexp/sqlexp_backend: Fixes for handling older keys; backend wiring updates for the new DB and resolver patterns.
Migration notes (from v0.1.0)¶
Import
KeyContextfromemerald_utils.types(notencrypted_fields). Replacekeyctx.dkwithkeyctx.key; setalgif you need a non-default algorithm.Replace
derive_dk_from_passphrase(...)withderive_kek_from_passphrase(...).Replace
EncryptedString.set_keyctx(ctx)withEncryptedString.set_current_keyctx(ctx)and implementEncryptedString.set_keyctx_resolver(lambda keyid: ...)so reads can decrypt rows written with other key IDs.
Requirements¶
Python ≥ 3.10
Core:
cryptography≥ 41,sqlalchemy≥ 2.0Optional:
pip install 'emerald_utils[azure]'for Key Vault
Installation¶
pip install emerald_utils
Or from a GitHub release asset (after you publish v0.2.0):
pip install https://github.com/gemstone-software-dev/gemstone_utils/releases/download/v0.2.0/emerald_utils-0.2.0.tar.gz
License¶
v0.1.0¶
Tag: v0.1.0
Commit: 271bd51
Released: 15 Mar 2026 (per GitHub release)
Overview¶
First public version of emerald_utils: a small, dependency-light utility library with AES-GCM helpers, PBKDF2 key derivation, a standard encrypted-field format, transparent SQLAlchemy encrypted columns, and an experimental secret resolver plus minimal SQL backend.
Stable components (crypto, encrypted fields, SQLAlchemy) are intended for long-term use. Experimental pieces are intentionally minimal and not part of the future vault/meta-manager.
Included artifacts¶
Source distribution (
emerald_utils-<version>.tar.gz) —pip install <url>Wheel (
emerald_utils-<version>-py3-none-any.whl)
Installation¶
pip install https://github.com/gemstone-software-dev/gemstone_utils/releases/download/v0.1.0/emerald_utils-0.1.0.tar.gz
Or from a clone:
pip install .
Highlights¶
Cryptography¶
AES-256-GCM encryption and decryption
PBKDF2-HMAC-SHA256 key derivation
URL-safe base64 helpers
Encrypted fields¶
$A256GCM$keyid$base64formatKeyContextfor data key + keyidencrypt_string()/decrypt_string()
SQLAlchemy integration¶
EncryptedStringTypeDecoratorLazy decryption via
LazySecretPrevents double-encryption
Central
set_keyctx()initialization
Experimental secret resolver¶
Supports:
env:file:secret:(systemd + container orchestrators)sqlexp:Encrypted values
Experimental SQL backend (sqlexp)¶
Simple key/value table
Stores encrypted values
Intended for bootstrap use only
License¶
Mozilla Public License 2.0 (MPL-2.0). You may use this library in proprietary applications; modifications to this library must remain MPL-licensed.