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_logtable —REVOKE UPDATE, DELETEfrom the app role and aBEFORE UPDATE OR DELETEguard 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 onlyINSERT/SELECT; - a compact
AFTER INSERTpg_notifytrigger on channelaudit_eventsthat sends identifiers only — never the unboundedchangesdiff — so every payload stays well under PostgreSQL's hard 8000-byteNOTIFYlimit. ALISTENconsumer 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_addressasTEXT(notUUID/INET), because the event model treats them as free-form strings and redaction can replaceip_addresswith 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fc8334e6c2c75a2a084525b40dc44d2aec5ce88147f21a9aafc46360423ab04a
|
|
| MD5 |
4aea31ae6520574f0fd80119f795f4b0
|
|
| BLAKE2b-256 |
081bf4e14f4534443825a7f7d4e80ed0e2d68aaf4e725c5f7a3e26bb6eef5d7a
|
Provenance
The following attestation bundles were made for audit_framework_postgres-0.1.0.tar.gz:
Publisher:
release.yml on vanmarkic/audit-logger
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
audit_framework_postgres-0.1.0.tar.gz -
Subject digest:
fc8334e6c2c75a2a084525b40dc44d2aec5ce88147f21a9aafc46360423ab04a - Sigstore transparency entry: 1997469111
- Sigstore integration time:
-
Permalink:
vanmarkic/audit-logger@a6dbec1581be847a73b80ad2abb37351bf1c6a6c -
Branch / Tag:
refs/tags/audit-framework-postgres-v0.1.0 - Owner: https://github.com/vanmarkic
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a6dbec1581be847a73b80ad2abb37351bf1c6a6c -
Trigger Event:
push
-
Statement type:
File details
Details for the file audit_framework_postgres-0.1.0-py3-none-any.whl.
File metadata
- Download URL: audit_framework_postgres-0.1.0-py3-none-any.whl
- Upload date:
- Size: 9.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a70d44d15867ae749055bb72b0efd7d58ffeb11e6bd1d1810fa7a6776f07f668
|
|
| MD5 |
bde2bbb8659c2ef9d48307f9a4628851
|
|
| BLAKE2b-256 |
7a94f3e361d5f41ba209625bd27cbf7c15214cfe9dc80ef83481c945efb008d9
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
audit_framework_postgres-0.1.0-py3-none-any.whl -
Subject digest:
a70d44d15867ae749055bb72b0efd7d58ffeb11e6bd1d1810fa7a6776f07f668 - Sigstore transparency entry: 1997469226
- Sigstore integration time:
-
Permalink:
vanmarkic/audit-logger@a6dbec1581be847a73b80ad2abb37351bf1c6a6c -
Branch / Tag:
refs/tags/audit-framework-postgres-v0.1.0 - Owner: https://github.com/vanmarkic
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a6dbec1581be847a73b80ad2abb37351bf1c6a6c -
Trigger Event:
push
-
Statement type: