Skip to main content

PostgreSQL adapters for audit-framework — append-only AuditStore, NotificationStore, and a LISTEN/NOTIFY EventBus.

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

Uploaded Python 3

File details

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

File metadata

  • Download URL: audit_framework_postgres-0.1.1.tar.gz
  • Upload date:
  • Size: 9.6 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.1.tar.gz
Algorithm Hash digest
SHA256 5b9bff5e31146bc2d84fd6b42e85456b5a35d397bce72fa9746c7236ca1b52eb
MD5 5639f35816a60e2a071d6a9446f4c4fe
BLAKE2b-256 e3bca174871757ae9d7aa9c5874cf6e61d2ad745f8d0d53839cde9f979c12464

See more details on using hashes here.

Provenance

The following attestation bundles were made for audit_framework_postgres-0.1.1.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.1-py3-none-any.whl.

File metadata

File hashes

Hashes for audit_framework_postgres-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3f33c430b73834aad8a973f94e55511e2533d28dbd50804bd5693e26f7ccc384
MD5 901e645d527439b47918d3267eca6adb
BLAKE2b-256 8d4872b0c89cdaad2e36a584c00105ad7b41c607a92e05a0896c08ef0783f7b8

See more details on using hashes here.

Provenance

The following attestation bundles were made for audit_framework_postgres-0.1.1-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