๐Ÿฆ€ Rust Core ๐Ÿ Python API v0.4.2 ๐Ÿ“ฆ PyPI

Audit logs you can
actually trust

Drop-in audit trail for FastAPI, Django, and any Python backend.
Every event is chained with SHA-256 โ€” tampering doesn't go unnoticed. Ship the same events to ImmutableLog when you need an external, verifiable receipt.

$ pip install rust-py-audit
python
>>> from rust_py_audit import AuditLogger
>>> audit = AuditLogger(app_name="billing-api")
>>> audit.log(action="DELETE_INVOICE", ...)
{'hash': '5eac3612da...', 'previous_hash': None}
>>> audit.verify()
{'valid': True, 'total_events': 1}
>>> โ–ˆ
88Tests passing
SHA-256Hash chain
3Delivery modes
0Runtime deps

Everything an audit trail needs

Rust handles the hashing and I/O. Python stays simple.

๐Ÿ“

Structured Events

id, timestamp, actor_id, action, resource and free-form metadata โ€” generated automatically on every log() call.

๐Ÿ”—

Hash Chain Integrity

Each event embeds the SHA-256 hash of the one before it. Edit, delete, or reorder a line and verify() catches it.

๐Ÿ“‚

JSONL Storage

One event per line, append-only. No database, no migrations โ€” just a file you can tail -f.

โšก

FastAPI Middleware

Add AuditMiddleware in one line. Logs every state-changing request (POST/PUT/PATCH/DELETE) automatically.

๐ŸŽธ

Django Middleware

Works with both WSGI and ASGI Django apps. Drop it in MIDDLEWARE and it logs the rest.

๐Ÿ”—

ImmutableLog Integration

mode="local" (default, no network), "remote", or "hybrid" โ€” ship events to ImmutableLog with retry, idempotency keys, and a flush_pending() queue for anything that didn't land.

๐Ÿฆ€

Rust Core via PyO3

Hashing, serialization, file I/O, and the ImmutableLog HTTP client run in Rust and compile to a native .so extension. No Rust needed to use it.

๐Ÿงต

Thread- & Process-safe

Share one AuditLogger across threads, or run gunicorn with N workers on the same file: a cross-process file lock keeps the hash chain linear, so verify() stays valid under load.

๐Ÿ’พ

Optional fsync

AuditLogger(..., fsync=True) returns from log() only once the event is on disk โ€” it survives a power loss, not just a crashed process. Off by default to keep log() at ~30 ยตs.

Works with your stack

One middleware line, or call the core API directly.

Python
from fastapi import FastAPI
from rust_py_audit.fastapi import AuditMiddleware

app = FastAPI()

# Logs every POST/PUT/PATCH/DELETE automatically
app.add_middleware(AuditMiddleware, app_name="billing-api")

@app.delete("/invoices/{invoice_id}")
async def delete_invoice(invoice_id: str):
    return {"deleted": invoice_id}
Python
# settings.py
MIDDLEWARE = [
    "rust_py_audit.django.AuditMiddleware",
    # ... rest of your middleware
]

# optional configuration
RUST_PY_AUDIT_APP_NAME = "billing-django"
RUST_PY_AUDIT_FILE_PATH = "./audit.jsonl"
Python
from rust_py_audit import AuditLogger

audit = AuditLogger(app_name="billing-api", file_path="./audit.jsonl")

event = audit.log(
    actor_id="user_123",
    action="DELETE_INVOICE",
    resource="invoice",
    resource_id="inv_987",
    metadata={"ip": "192.168.0.10"},
)
print(event["hash"])      # 64-char SHA-256 hex digest
print(audit.last_hash())  # same hash, O(1) lookup

result = audit.verify()
print(result)               # {"valid": True, "total_events": 1, ...}
Python
from rust_py_audit import AuditLogger

# mode="hybrid": writes audit.jsonl locally AND ships to ImmutableLog
audit = AuditLogger(
    app_name="billing-api",
    file_path="./audit.jsonl",
    mode="hybrid",
    immutablelog_url="https://api.immutablelog.com",
    immutablelog_api_key="iml_live_xxxxx",
)

event = audit.log(
    actor_id="user_123",
    action="DELETE_INVOICE",
    resource="invoice",
    resource_id="inv_987",
)
print(event["immutablelog"])
# {"status": "delivered", "tx_id": "tx_...", ...}
# or {"status": "pending", ...}  on a transient failure (queued) โ€” never raises
# or {"status": "failed", ...}   on a permanent failure (not queued)

# Retry anything still marked "pending" (e.g. on a cron):
audit.flush_pending()
# {"flushed": 1, "failed": 0, "still_pending": 0, "total": 1}

Tamper-evident by design

Every event links to the one before it. Break the chain and verify() tells you exactly where.

LOGIN
prev: null
hash: 5eac36...
โ†’
DELETE_INVOICE
prev: 5eac36...
hash: 2c5483...
โ†’
โœ• LOGOUT (edited)
prev: 2c5483...
hash: mismatch!

What gets caught

  • hash_mismatch edited event
  • broken_chain deleted event
  • broken_chain reordered events
  • broken_chain forged previous_hash

How it works

  • SHA-256 content + previous_hash
  • JSONL stable field order
  • verify() rereads & recomputes everything
audit.verify() โ€” intact chain
{
  "valid": True,
  "total_events": 10,
  "last_hash": "a1b2c3..."
}
audit.verify() โ€” tampered chain
{
  "valid": False,
  "total_events": 10,
  "error_index": 4,
  "reason": "hash_mismatch"
}

How it works

Rust hashes and persists. Python integrates. You verify.

Your App
FastAPI Django Script
โ†’
Middleware / API
AuditMiddleware AuditLogger.log() flush_pending()
โ†’
๐Ÿฆ€ Rust Core
hash.rs (SHA-256) storage.rs (JSONL) verifier.rs immutablelog_client.rs retry.rs
โ†’
Output
audit.jsonl dict / verify() ImmutableLog receipt

The native .so extension is compiled once at publish time via maturin + PyO3.
Your users just pip install โ€” no Rust toolchain required. ImmutableLog delivery is fully optional โ€” mode="local" (the default) never touches the network.

Ready in seconds

Install from PyPI. No Rust, no compilers, no configuration.

Basic

pip install rust-py-audit

With FastAPI

pip install "rust-py-audit[fastapi]"

With Django

pip install "rust-py-audit[django]"

Full stack

pip install "rust-py-audit[fastapi,django]"
Requires Python 3.10+ ยท Linux ยท macOS ยท Windows ยท MIT License
Copied!