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.2.tar.gz (120.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.2-py3-none-any.whl (96.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: auditrum-0.4.2.tar.gz
  • Upload date:
  • Size: 120.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.2.tar.gz
Algorithm Hash digest
SHA256 0c1d36ac8a0c3b2df690dd4d0f55941774b017964274fd78e2b4048268dfe8fe
MD5 38f5e3be94e9d52965ce62b2dd27844e
BLAKE2b-256 5559ee67ed4fd68b18b9a8c251f7edbb2cf6368893e37a3065c9f8748a665f94

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: auditrum-0.4.2-py3-none-any.whl
  • Upload date:
  • Size: 96.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.4.2-py3-none-any.whl
Algorithm Hash digest
SHA256 444c510fc101329f688daad8ede3d3923fac15d5cc7616a8313f35fbb89eb1df
MD5 11d2b9b10d73a5bb9f384539d57ae2f3
BLAKE2b-256 6662e83cc13c3115dda8fe0c6257ee7d783fc169aaf81b6d6812d259762ba75b

See more details on using hashes here.

Provenance

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