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.5.0.tar.gz (131.9 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.5.0-py3-none-any.whl (105.2 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for auditrum-0.5.0.tar.gz
Algorithm Hash digest
SHA256 027011c5caa0db4346c120e3d4836f6744be8735b93c35397c8872712cfbae7f
MD5 c6a2590ff02014b1096e14851013c893
BLAKE2b-256 e32e566164740d080730d12578c980db8f48cb80e7b2479d0166de0b1b4c5831

See more details on using hashes here.

Provenance

The following attestation bundles were made for auditrum-0.5.0.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.5.0-py3-none-any.whl.

File metadata

  • Download URL: auditrum-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 105.2 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.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0d627f397e42071c7b7bedcef142028b45874352db5bfac06835c95eed5b0f35
MD5 1c853bdc3213a9da80faed6a973da189
BLAKE2b-256 3d87558027704d4ca297ebee22612062e82877b637d8b3f8f22eb4931567af0e

See more details on using hashes here.

Provenance

The following attestation bundles were made for auditrum-0.5.0-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