Skip to main content

asherah

Python bindings for the Asherah envelope encryption and key rotation library.

Native Rust implementation via PyO3/maturin. Prebuilt wheels are published to PyPI for Linux (x86_64 and aarch64, both glibc and musl), macOS (x86_64 and arm64), and Windows (x86_64 and arm64).

Installation

pip install asherah

Requires Python ≥ 3.8.

Documentation

Task-oriented walkthroughs under docs/:

Guide When to read
Getting started First-time install through round-trip encrypt/decrypt.
Framework integration FastAPI, Flask, Django, AWS Lambda, Celery.
AWS production setup KMS keys, DynamoDB, IAM policy, region routing.
Testing pytest fixtures, Testcontainers, mocking patterns, asyncio test patterns.
Troubleshooting Common errors with what to check first.

Choosing an API style

Two API styles are exposed; both are fully supported and produce the same wire format. New code should prefer the Factory / Session API.

Style When to use
Static / module-level (asherah.setup, asherah.encrypt_bytes, …) Drop-in compatibility with the canonical godaddy/asherah-python package. Simplest call surface. Singleton lifecycle (setup() once, shutdown() once).
Factory / Session (asherah.SessionFactory, factory.get_session(...)) Recommended for new code. Explicit lifecycle, no hidden singleton, multi-tenant isolation is obvious in code. Context-manager friendly.

A complete runnable example exercising both styles plus async, log hook, and metrics hook is in samples/python/sample.py.

Quick start (static API)

import os
import asherah

os.environ["STATIC_MASTER_KEY_HEX"] = "22" * 32  # testing only

asherah.setup({
    "ServiceName": "my-service",
    "ProductID":   "my-product",
    "Metastore":   "memory",   # testing only — use "rdbms" or "dynamodb" in production
    "KMS":         "static",   # testing only — use "aws" in production
})

ct = asherah.encrypt_string("user-42", "secret")
pt = asherah.decrypt_string("user-42", ct)
assert pt == "secret"

asherah.shutdown()

Quick start (factory / session API)

import asherah

config = {
    "ServiceName": "my-service",
    "ProductID":   "my-product",
    "Metastore":   "memory",
    "KMS":         "static",
    "StaticMasterKeyHex": "22" * 32,
}

with asherah.SessionFactory(config) as factory:
    with factory.get_session("user-42") as session:
        ct = session.encrypt_text("secret")
        pt = session.decrypt_text(ct)
        assert pt == "secret"

SessionFactory(config) and SessionFactory.from_config(config) construct from an explicit config dict. SessionFactory() and SessionFactory.from_env() read config from environment variables; set them with asherah.setenv({...}) or via os.environ before constructing the factory.

Async API

There are two flavors of async to choose from depending on your call pattern:

  • Module-level async (encrypt_string_async, decrypt_string_async, setup_async, shutdown_async) — wraps the sync calls with loop.run_in_executor. Lowest setup, but the sync work runs on the default thread pool executor.

  • Session-level async (session.encrypt_bytes_async, session.decrypt_bytes_async) — true async PyO3 coroutines that run on the Rust tokio runtime. The asyncio event loop is not blocked, and there is no thread pool overhead.

import asyncio
import asherah

async def main():
    # Module-level
    await asherah.setup_async({...})
    ct = await asherah.encrypt_string_async("user-42", "secret")
    pt = await asherah.decrypt_string_async("user-42", ct)
    await asherah.shutdown_async()

    # Session-level (true async)
    with asherah.SessionFactory() as factory:
        session = factory.get_session("user-42")
        ct = await session.encrypt_bytes_async(b"secret")
        pt = await session.decrypt_bytes_async(ct)

asyncio.run(main())

Observability hooks

Log hook

Receive every log event from the Rust core (encrypt/decrypt path, metastore drivers, KMS clients).

def on_log(event):
    # event = {"level": "trace"|"debug"|"info"|"warn"|"error",
    #          "message": str, "target": str}
    if event["level"] in ("warn", "error"):
        print(f"[asherah {event['level']}] {event['message']}")

asherah.set_log_hook(on_log)

# later, to deregister:
asherah.set_log_hook(None)

The callback may fire from any thread (Rust tokio worker threads, DB driver threads). PyO3 acquires the GIL before invoking the callback, so the callback runs single-threaded from Python's perspective.

Metrics hook

Receive timing events for encrypt/decrypt/store/load and counter events for cache hit/miss/stale.

def on_metric(event):
    if event["type"] in ("encrypt", "decrypt", "store", "load"):
        # event = {"type": ..., "duration_ns": int}
        my_histogram.observe(event["type"], event["duration_ns"] / 1e6)
    else:
        # event = {"type": "cache_hit"|"cache_miss"|"cache_stale", "name": str}
        my_counter.inc(result=event["type"], cache=event["name"])

asherah.set_metrics_hook(on_metric)

# later:
asherah.set_metrics_hook(None)

Metrics collection is enabled automatically when a hook is installed and disabled when cleared.

Input contract

Partition ID (None, ""): always rejected as programming errors with TypeError (None) or ValueError/Exception ("partition id cannot be empty"). No row is ever written to the metastore under a degenerate partition ID.

Plaintext to encrypt:

  • None → TypeError from PyO3 type conversion before any native call.
  • Empty str ("") and empty bytes (b"") are valid plaintexts. encrypt_string / encrypt_bytes produce a real DataRowRecord envelope; decrypt_string / decrypt_bytes return exactly "" or b"".

Ciphertext to decrypt:

  • None → TypeError.
  • Empty str / bytes → exception from JSON parse (not valid DataRowRecord).

Do not short-circuit empty plaintext encryption in caller code — empty data is real data, encrypting it produces a genuine envelope, and skipping encryption leaks the fact that the value was empty. See docs/input-contract.md for the full rationale.

Configuration

setup() accepts a dict (or any JSON-serializable object) using PascalCase keys to match the canonical Go/Java/.NET API:

Key Type Required Description
ServiceName str yes Service identifier for the key hierarchy.
ProductID str yes Product identifier for the key hierarchy.
Metastore str yes "memory", "rdbms", or "dynamodb". "memory" is testing-only.
KMS str "static" (default; testing) or "aws".
ConnectionString str SQL connection string for rdbms.
SQLMetastoreDBType str "mysql" or "postgres" (paired with Metastore: "rdbms").
EnableSessionCaching bool Cache Session objects by partition ID. Default True.
SessionCacheMaxSize int Max cached sessions. Default 1000.
SessionCacheDuration int Session cache TTL in seconds.
RegionMap dict[str,str] AWS KMS multi-region key-ARN map.
PreferredRegion str Preferred region from RegionMap.
AwsProfileName str AWS shared-credentials profile name for KMS, DynamoDB, and Secrets Manager clients.
EnableRegionSuffix bool Append AWS region suffix to key IDs.
ExpireAfter int Intermediate-key expiration in seconds. Default 90 days.
CheckInterval int Revoke-check interval in seconds. Default 60 minutes.
DynamoDBEndpoint str DynamoDB endpoint URL (for local DynamoDB).
DynamoDBRegion str AWS region for DynamoDB.
DynamoDBTableName str DynamoDB table name. Default EncryptionKey.
ReplicaReadConsistency str DynamoDB consistency.
Verbose bool Emit verbose log events (use a log hook to consume).
EnableCanaries bool Enable in-memory canary buffers around plaintexts.

Both PascalCase and snake_case keys are accepted; PascalCase is canonical.

Environment variables

Variable Effect
STATIC_MASTER_KEY_HEX 64 hex chars (32 bytes) for static KMS. Testing only.
SERVICE_NAME / PRODUCT_ID / Metastore / KMS Read by SessionFactory() (no-config constructor).

AWS KMS example

asherah.setup({
    "ServiceName": "payments-api",
    "ProductID": "acme-corp",
    "Metastore": "rdbms",
    "ConnectionString": "mysql://user:pass@host:3306/asherah",
    "SQLMetastoreDBType": "mysql",
    "KMS": "aws",
    "RegionMap": {"us-west-2": "arn:aws:kms:us-west-2:000:key/abc"},
    "PreferredRegion": "us-west-2",
    "EnableSessionCaching": True,
    "SessionCacheMaxSize": 1000,
})

Performance

Native Rust implementation. Typical latencies on Apple M4 Max (in-memory metastore, session caching enabled, 64-byte payload):

Operation Sync Async (session-level, true async)
Encrypt ~1 µs ~37 µs
Decrypt ~1.2 µs ~37 µs

Async overhead is from the asyncio event loop dispatch + GIL handoff. Use sync for CPU-bound batches; use async when you need non-blocking behavior in an asyncio application.

API Reference

Full docstrings live in asherah/_asherah.pyi and asherah/__init__.py and surface in your IDE on hover. The tables below summarize each API; the type stubs are the source of truth.

Static / module-level API (legacy compatibility)

Lifecycle

Function Description
setup(config: dict) Initialize the global instance. Raises if already configured.
setup_async(config: dict) Async wrapper. Returns a coroutine.
shutdown() Tear down the global instance. Idempotent.
shutdown_async() Async wrapper.
get_setup_status() -> bool True iff setup() has been called and shutdown() has not.
setenv(env: dict) Apply env vars before setup(). Values may be None to delete.
version() -> str Package version string.

Encrypt / decrypt

Function Param 1 Param 2 Returns
encrypt_bytes(partition_id, data) str (non-empty) bytes (empty OK) str (DRR JSON)
encrypt_string(partition_id, text) str str (empty OK) str (DRR JSON)
decrypt_bytes(partition_id, drr) str str bytes
decrypt_string(partition_id, drr) str str str
encrypt_bytes_async(partition_id, data) str bytes Awaitable[str]
decrypt_bytes_async(partition_id, drr) str str or bytes Awaitable[bytes]
encrypt_string_async(partition_id, text) str str Awaitable[str]
decrypt_string_async(partition_id, drr) str str Awaitable[str]

Hooks

Function Description
set_log_hook(callback) Register a (event_dict) -> None log callback. Pass None to deregister.
set_metrics_hook(callback) Register a (event_dict) -> None metrics callback. Pass None to deregister.

Factory / Session API (recommended)

class SessionFactory

Member Description
SessionFactory() Construct from environment variables.
SessionFactory(config) Construct from an explicit config dict.
SessionFactory.from_env() Same as SessionFactory() — provided for SDK parity.
SessionFactory.from_config(config) Construct from an explicit config dict.
factory.get_session(partition_id) Get a per-partition Session. Raises on null/empty partition.
factory.close() Release native resources.
with SessionFactory() as factory: Context manager — close() runs on exit.

class Session

Member Description
session.encrypt_bytes(data) bytes → DRR JSON str. Empty bytes is valid.
session.encrypt_text(text) str → DRR JSON str. Empty string is valid.
session.decrypt_bytes(drr) DRR JSON str → bytes.
session.decrypt_text(drr) DRR JSON str → str.
session.encrypt_bytes_async(data) Awaitable[str] — true async on tokio.
session.decrypt_bytes_async(drr) Awaitable[bytes] — true async on tokio.
session.close() Release native resources.
with session as ...: Context manager — close() runs on exit.

Event dict shapes

LogEvent = {
    "level": "trace" | "debug" | "info" | "warn" | "error",
    "message": str,
    "target": str,
}

# Metrics event for timing measurements:
TimingEvent = {
    "type": "encrypt" | "decrypt" | "store" | "load",
    "duration_ns": int,
}

# Metrics event for cache lifecycle:
CacheEvent = {
    "type": "cache_hit" | "cache_miss" | "cache_stale",
    "name": str,  # cache name, e.g. "session", "intermediate-key"
}

Cross-language compatibility

Wire-format compatible with all other Asherah implementations:

  • canonical godaddy/asherah (Go core via cobhan)
  • canonical godaddy/asherah-csharp
  • canonical godaddy/asherah-java
  • this repo's other bindings: Node, .NET, Java, Ruby, Go

A DataRowRecord written by any of these can be decrypted by any other, provided they share the same metastore and KMS configuration.

License

Licensed under the Apache License, Version 2.0.

Metadata

Release files for asherah 0.5.56

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for asherah 0.5.56
File Size Uploaded
asherah-0.5.56.tar.gz 314.0 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for asherah 0.5.56
File
asherah-0.5.56-cp38-abi3-win_arm64.whl CPython 3.8 abi3 Windows ARM64 Details
asherah-0.5.56-cp38-abi3-win_amd64.whl CPython 3.8 abi3 Windows x86-64 Details
asherah-0.5.56-cp38-abi3-musllinux_1_2_x86_64.whl CPython 3.8 abi3 Linux musl 1.2+ x86-64 Details
asherah-0.5.56-cp38-abi3-musllinux_1_2_aarch64.whl CPython 3.8 abi3 Linux musl 1.2+ ARM64 Details
asherah-0.5.56-cp38-abi3-manylinux_2_28_x86_64.whl CPython 3.8 abi3 Linux glibc 2.28+ x86-64 Details
asherah-0.5.56-cp38-abi3-manylinux_2_28_aarch64.whl CPython 3.8 abi3 Linux glibc 2.28+ ARM64 Details
asherah-0.5.56-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl CPython 3.8 abi3 macOS 11.0+ ARM64, macOS 10.12+ universal2 (ARM64, x86-64), macOS 10.12+ x86-64 Details

Total release size: 80.8 MB

Release files / asherah-0.5.56.tar.gz

Download URL asherah-0.5.56.tar.gz
Size 314.0 kB
Tags Source
SHA-256 checksum
How to use checksums
5be73e9c562bb27718ffdec7b1deca59d871c9b17505d20962602bab758cc146
BLAKE2b-256 checksum
How to use checksums
7a6ab7867381a54effd3d6156a9eebfc5f5fd666a272662fe35e6735a5759930
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.15

Release files / asherah-0.5.56-cp38-abi3-win_arm64.whl

Download URL asherah-0.5.56-cp38-abi3-win_arm64.whl
Size 7.2 MB
Tags CPython 3.8 Windows ARM64 abi3
SHA-256 checksum
How to use checksums
3cef50d702206b082f3add47a60989a3b73f20e83b09340cc859c22416f91544
BLAKE2b-256 checksum
How to use checksums
d30cd4a0d61a7fdb4afb4b8f44d6b641247a439621aafcf55d70c2655db4e60b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.15

Release files / asherah-0.5.56-cp38-abi3-win_amd64.whl

Download URL asherah-0.5.56-cp38-abi3-win_amd64.whl
Size 7.7 MB
Tags CPython 3.8 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
e0f59d722764c7c0d2d37ed34f0153e917c1055cf4008ae79b5954fe37aa2a7d
BLAKE2b-256 checksum
How to use checksums
979115069929dc834d6326cd4c520e82f3e14a66fa3aff4d90506537ad7fadc8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.15

Release files / asherah-0.5.56-cp38-abi3-musllinux_1_2_x86_64.whl

Download URL asherah-0.5.56-cp38-abi3-musllinux_1_2_x86_64.whl
Size 12.3 MB
Tags CPython 3.8 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
9f04e3280701e3c03a6df3fd211f38618ecc07b22aa9832b4b9578f3a1d2b8cf
BLAKE2b-256 checksum
How to use checksums
db1a5acf45c3cd98c2cdfeecc6782af375da25b00d7174ab38ebd7ab8abbcab0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.15

Release files / asherah-0.5.56-cp38-abi3-musllinux_1_2_aarch64.whl

Download URL asherah-0.5.56-cp38-abi3-musllinux_1_2_aarch64.whl
Size 12.4 MB
Tags CPython 3.8 Linux musl 1.2+ ARM64 abi3
SHA-256 checksum
How to use checksums
a1c15adaea2f8b906f274e1916d6029e63b48b7d7be688d0366f8ee22842ddd5
BLAKE2b-256 checksum
How to use checksums
71fbc7e82b29bc11e0f2e7bb1f8af8233f5def7d94632da6c613cf5d6c6fef3c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.15

Release files / asherah-0.5.56-cp38-abi3-manylinux_2_28_x86_64.whl

Download URL asherah-0.5.56-cp38-abi3-manylinux_2_28_x86_64.whl
Size 11.4 MB
Tags CPython 3.8 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
8e800bd95284508aaf8be10efda50e85fe2e7250086e16154367538ef7b5e849
BLAKE2b-256 checksum
How to use checksums
04da83227774a7ac26e53c24585d2993334b9f067403a0cf6d71028787e227b7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.15

Release files / asherah-0.5.56-cp38-abi3-manylinux_2_28_aarch64.whl

Download URL asherah-0.5.56-cp38-abi3-manylinux_2_28_aarch64.whl
Size 11.9 MB
Tags CPython 3.8 Linux glibc 2.28+ ARM64 abi3
SHA-256 checksum
How to use checksums
cddc7b969ae5c54ca96f910c1f7a5efc6700ab43ef5c203d2ee2a0ed241be6c7
BLAKE2b-256 checksum
How to use checksums
1f90abb244081f02dbdc0aab8b4920773c8988250e3152566c58218eabcdebdc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.15

Release files / asherah-0.5.56-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl

Download URL asherah-0.5.56-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Size 17.4 MB
Tags CPython 3.8 abi3 macOS 10.12+ universal2 (ARM64, x86-64) macOS 10.12+ x86-64 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
35591695b8a3cb21a70085041ae73424c056d57884ed52deb66aec6761f000da
BLAKE2b-256 checksum
How to use checksums
999fbd6b257c41571cc6c8646ca0582f58ab301bada5d9141e58ecf49a702f68
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.15

Release history Release notifications | RSS feed

This release

0.5.56 This release

8 release files

0.5.55

8 release files

0.5.54

8 release files

0.5.49

8 release files

0.5.48

8 release files

0.5.47

8 release files

0.5.46

8 release files

0.5.45

8 release files

0.5.44

8 release files

0.5.43

8 release files

0.5.42

8 release files

0.5.36

8 release files

0.5.35

8 release files

0.5.34

8 release files

0.5.33

8 release files

0.5.30

8 release files

0.5.29

8 release files

0.5.28

8 release files

0.5.27

8 release files

0.5.26

8 release files

0.5.25

8 release files

0.5.24

8 release files

0.5.23

8 release files

0.5.22

8 release files

0.5.21

8 release files

0.5.20

8 release files

0.5.19

8 release files

0.5.18

8 release files

0.5.17

8 release files

0.5.16

8 release files

0.5.15

8 release files

0.5.14

8 release files

0.5.13

8 release files

0.5.12

8 release files

0.5.11

8 release files

0.5.10

8 release files

0.5.9

8 release files

0.5.8

8 release files

0.5.7

8 release files

0.5.6

8 release files

0.5.5

8 release files

0.5.4

8 release files

0.5.3

8 release files

0.5.2

8 release files

0.5.1

8 release files

0.3.9

2 release files

0.3.7

2 release files

0.3.5

2 release files

0.3.4

2 release files

0.3.2

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page