Skip to main content

Auditrum — a PostgreSQL audit system with rich contextual logging and Django integration

Project description

#auditrum

A PostgreSQL audit system for tracking database changes with rich contextual information, featuring seamless Django integration.


✨ Features

  • Automatic Change Tracking – PostgreSQL triggers to log all INSERT, UPDATE, and DELETE
  • Rich Context – Who, when, why, and from where
  • Partitioned Storage – Time-partitioned audit table
  • Django Integration – Models, admin, middleware
  • Flexible Configuration – Track/include/exclude fields, add conditions
  • Performance Optimized – Minimal database overhead

📦 Installation

With pip

pip install auditrum

To include Django support:

pip install auditrum[django]

With uv

uv add auditrum
uv add auditrum[django]

🚀 Quick Start

🔧 With Django

  1. Add to INSTALLED_APPS:
INSTALLED_APPS = [
    # ...
    'auditrum.integrations.django',
]
  1. Add middleware:
MIDDLEWARE = [
    # ...
    'auditrum.integrations.django.middleware.RequestIDMiddleware',
    'auditrum.integrations.django.middleware.AuditrumMiddleware',
]
  1. Register models:

Create a new audit.py file inside your Django app (e.g., yourapp/audit.py) and register models there:

# yourapp/audit.py

from auditrum.integrations.django.audit import register
from .models import User

register(User, track_only=["name", "email"])

The auditrum integration will automatically discover and execute this file (like admin.py), so no need to import it manually.

  1. Run migrations:
python manage.py migrate

This will create the audit table and set up triggers automatically.

  1. Set up monthly partitions:

Add a cron job to run the following command on the last day of each month. This will ensure partitions exist for upcoming months:

python manage.py audit_add_partitions --months 3

This will create audit table partitions for the next 3 months.

⏰ Cron Job Example

To automatically run the partition creation every month (e.g. on the 28th at 23:50), add the following cron entry:

50 23 28 * * /path/to/your/venv/bin/python /path/to/your/project/manage.py audit_add_partitions --months 3
  • Replace /path/to/your/venv/bin/python with the full path to your virtualenv's Python interpreter.
  • Replace /path/to/your/project/manage.py with the path to your project's manage.py file.
  • Adjust the --months 3 argument as needed to define how far ahead partitions should be created.
  1. Enable audit context for management commands (optional)

To track changes made via manage.py commands (e.g. migrate, shell) with proper source and change_reason in audit logs, you can modify your manage.py like this:

# manage.py

from auditrum import audit_tracked  # noqa (re-export from auditrum.utils)


def main():
    """Run administrative tasks."""
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "your_app.settings")

    AUDIT_COMMAND_SOURCES = {
        "shell": "shell",
        "migrate": "migration",
        "makemigrations": "migration",
    }

    cmd = sys.argv[1] if len(sys.argv) > 1 else None
    audit_source = AUDIT_COMMAND_SOURCES.get(cmd)

    if audit_source:
        try:
            audit_tracked(source=audit_source).__enter__()
        except Exception as e:
            print(f"[audit] failed to enable tracking for source='{audit_source}': {e}")

    if cmd == "shell":
        try:
            import apps.audit.shell_context  # noqa
        except ImportError:
            print("[audit] Warning: shell_context not found. Skipping shell audit.")

    ...

This ensures your migrations and shell activity are properly attributed in the audit log.


🧩 Without Django

from auditrum.schema import generate_auditlog_table_sql
from auditrum.triggers import generate_trigger_sql
import psycopg

with psycopg.connect("your_connection_string") as conn, conn.cursor() as cursor:
    # Create audit table
    cursor.execute(generate_auditlog_table_sql("auditlog"))

    # Create trigger for a table
    sql = generate_trigger_sql(
        table_name="users",
        track_only=["name", "email"]
    )
    cursor.execute(sql)
    conn.commit()

⚙️ Configuration Examples

✅ Track only specific fields

register(User, track_only=["name", "email"])

❌ Exclude specific fields

register(Product, exclude_fields=["created_at", "updated_at"])

📐 Add conditions

register(Subscription, log_conditions="NEW.is_active = TRUE")

📚 Adding Context to Changes

Using decorator:

from auditrum.context import with_change_reason


@with_change_reason("User requested password reset")
def reset_password(user_id):
    ...

Using context manager:

from auditrum.context import audit_context

with audit_context.use_change_reason("Bulk update for compliance"):
    ...

🛡️ Hardening Guide

For compliance-sensitive deployments, the audit log should be append-only and tamper-detectable. auditrum ships three building blocks that can be enabled independently:

1. Revoke mutating privileges (append-only)

auditrum harden --app-role myapp --admin-role myapp_admin

This runs REVOKE UPDATE, DELETE, TRUNCATE ON auditlog FROM PUBLIC, myapp and grants full privileges to a dedicated myapp_admin role. After this step the regular application role can only append audit rows; maintenance (partition drops, retention) must run as myapp_admin.

2. Retention / purge

Delete rows older than a cutoff or drop entire month partitions:

# DELETE-based (fine for smaller tables)
auditrum purge --older-than '2 years'

# Partition-based (fast, WAL-friendly)
auditrum purge --older-than '2 years' --drop-partitions

3. Tamper detection via SHA-256 hash chain

Enable optional row hashing + a chain of prev_hash pointers:

auditrum enable-hash-chain

BEFORE INSERT triggers on the audit log compute a SHA-256 of (id, changed_at, operation, table_name, old_data, new_data, prev_hash) using pgcrypto. Writes are serialized via pg_advisory_xact_lock, so peak insert throughput drops — pair this with careful benchmarking if your workload is high-write. To check integrity:

auditrum verify-chain

This runs a server-side recomputation across the whole log and reports any rows whose stored row_hash or prev_hash disagrees with the expected value.


📖 Documentation

Full documentation lives under docs/ and renders directly on GitHub. Start with:

Changelog: CHANGELOG.md Roadmap to 1.0: ROADMAP.md


🪪 License

This project is licensed under the MIT License. See the LICENSE file for details.

Project details


Download files

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

Source Distribution

auditrum-0.4.4.tar.gz (124.4 kB view details)

Uploaded Source

Built Distribution

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

auditrum-0.4.4-py3-none-any.whl (98.4 kB view details)

Uploaded Python 3

File details

Details for the file auditrum-0.4.4.tar.gz.

File metadata

  • Download URL: auditrum-0.4.4.tar.gz
  • Upload date:
  • Size: 124.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for auditrum-0.4.4.tar.gz
Algorithm Hash digest
SHA256 11b4bf1c89e1869d071aa99c3cb7f8dd8c4d6bad2d951cf29912ce1add7d278c
MD5 ffe28766a3df4d305b6c237be3ad5da5
BLAKE2b-256 2b90d9b8ba2c58ff735601bc0572570558eed8c6fd113097280e123bd592de67

See more details on using hashes here.

Provenance

The following attestation bundles were made for auditrum-0.4.4.tar.gz:

Publisher: publish.yml on tauvin/auditrum

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

File details

Details for the file auditrum-0.4.4-py3-none-any.whl.

File metadata

  • Download URL: auditrum-0.4.4-py3-none-any.whl
  • Upload date:
  • Size: 98.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for auditrum-0.4.4-py3-none-any.whl
Algorithm Hash digest
SHA256 df1f5644baded992631542d9b94791100e26d42ec4af9ce864c46151a692ae8f
MD5 7f8f8e5a7b5add78d97d9cc5313bd894
BLAKE2b-256 e27911ca02697c18cef895517352d7ae7ac187553b0236b9b72a7ded8322b6cb

See more details on using hashes here.

Provenance

The following attestation bundles were made for auditrum-0.4.4-py3-none-any.whl:

Publisher: publish.yml on tauvin/auditrum

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 Pingdom Monitoring Sentry Error logging StatusPage Status page