Skip to main content

Append-only PostgreSQL AuditStore for audit-framework — the authoritative, queryable audit log.

Project description

audit-framework-postgres

The append-only PostgreSQL AuditStore for audit-framework — the authoritative, queryable system of record for the audit log (as opposed to the best-effort ExternalSink fan-out). Ships the audit_log schema, a compact LISTEN/NOTIFY trigger, and append-only guards.

Install

pip install audit-framework-postgres            # bring your own executor
pip install audit-framework-postgres[asyncpg]   # + asyncpg for a real pool

Use

import asyncpg
from audit_framework_postgres import PostgresAuditStore, apply_schema
from audit_framework.core.middlewares.store import StoreMiddleware

pool = await asyncpg.create_pool(dsn, min_size=5, max_size=20)
await apply_schema(pool, app_role="app_user")     # create table + triggers (run once, as a privileged role)

store = PostgresAuditStore(pool)                  # the asyncpg pool *is* the executor
pipeline.use(StoreMiddleware(store))              # now events are persisted authoritatively

Query it back (this is what backs an admin audit-log view — sinks can't do this):

await store.query({"actor_id": "alice", "action": "DELETE", "from": "2026-06-01", "to": "2026-07-01"},
                  offset=0, limit=50)
await store.get_by_resource("contract", "c-42")

It advertises itself as the postgres audit store via the audit_framework.plugins entry point, so it's discoverable through the registry.

No hard driver dependency

All DB access goes through an injected Executor — anything exposing fetchval / fetch / execute (an asyncpg pool or connection satisfies this structurally). So the SQL logic is fully unit-testable without a database (14 stdlib-only tests use a fake executor), and you control pooling.

Schema design (validated against PostgreSQL guidance)

schema_sql() / apply_schema() emit:

  • an append-only audit_log table — REVOKE UPDATE, DELETE from the app role and a BEFORE UPDATE OR DELETE guard trigger that raises, so the log is immutable even for the table owner (only a superuser can bypass it). Run the app under an unprivileged, non-owning role that can only INSERT/SELECT;
  • a compact AFTER INSERT pg_notify trigger on channel audit_events that sends identifiers only — never the unbounded changes diff — so every payload stays well under PostgreSQL's hard 8000-byte NOTIFY limit. A LISTEN consumer fetches the full row by id when it needs detail (this is also the upgrade path to the multi-worker event bus, AD-6);
  • request_id / ip_address as TEXT (not UUID/INET), because the event model treats them as free-form strings and redaction can replace ip_address with a mask.

Tamper-evidence: REVOKE + guards make the log append-only, but a superuser can still rewrite history. For tamper-detection, add per-row hash chaining (prev_hash/hash) on top — a natural follow-up.

SQL-injection posture

The table name is validated against a strict identifier whitelist at construction; every value is a bound $n parameter; query filters are mapped through a fixed column allow-list (unknown keys raise ValueError). The only interpolation is the validated table name (Postgres can't bind an identifier as a parameter).

Development

pip install -e ".[dev]"
pytest        # 14 stdlib-only tests (fake executor; no database needed)

A live-DB integration test (real asyncpg + a throwaway Postgres) is the recommended next layer; the unit suite already pins the generated SQL, the parameter binding, the append-only DDL, and the compact-notify payload.

License

MIT

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

audit_framework_postgres-0.1.0.tar.gz (8.7 kB view details)

Uploaded Source

Built Distribution

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

audit_framework_postgres-0.1.0-py3-none-any.whl (9.1 kB view details)

Uploaded Python 3

File details

Details for the file audit_framework_postgres-0.1.0.tar.gz.

File metadata

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

File hashes

Hashes for audit_framework_postgres-0.1.0.tar.gz
Algorithm Hash digest
SHA256 fc8334e6c2c75a2a084525b40dc44d2aec5ce88147f21a9aafc46360423ab04a
MD5 4aea31ae6520574f0fd80119f795f4b0
BLAKE2b-256 081bf4e14f4534443825a7f7d4e80ed0e2d68aaf4e725c5f7a3e26bb6eef5d7a

See more details on using hashes here.

Provenance

The following attestation bundles were made for audit_framework_postgres-0.1.0.tar.gz:

Publisher: release.yml on vanmarkic/audit-logger

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

File details

Details for the file audit_framework_postgres-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for audit_framework_postgres-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a70d44d15867ae749055bb72b0efd7d58ffeb11e6bd1d1810fa7a6776f07f668
MD5 bde2bbb8659c2ef9d48307f9a4628851
BLAKE2b-256 7a94f3e361d5f41ba209625bd27cbf7c15214cfe9dc80ef83481c945efb008d9

See more details on using hashes here.

Provenance

The following attestation bundles were made for audit_framework_postgres-0.1.0-py3-none-any.whl:

Publisher: release.yml on vanmarkic/audit-logger

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