audit-core/Makefile
tegwick bd274f6269
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Add the PostgreSQL audit backend and a shared conformance suite
AUDIT-WP-0005-T01, built and verified against PostgreSQL 16 locally in
Docker; the Railiance cluster was not needed.

tests/test_backend_conformance.py is one suite run against every backend, so
"the Postgres backend is done" means it satisfies the same contract SQLite
already does rather than having its own green tests. It skips cleanly with no
server reachable; make pg-test-up and make test-pg run it. Suite 50 -> 71.

RetentionPolicy declares immutable=True and earns it: migration 0002 installs
a trigger rejecting UPDATE and DELETE on the events table, so a leaked runtime
credential can append but cannot rewrite or erase the trail. That materially
narrows the residual risk ADR-0001 section 5 called out. tamper_evidence stays
False because nothing here would prove a database owner had dropped the
trigger - hash-chaining or external anchoring would be needed and is not
implemented.

Idempotency is one statement (INSERT ... ON CONFLICT DO NOTHING RETURNING),
verified to behave identically to the SQLite backend under 12 concurrent
submissions of the same event. Migrations are ordered, recorded and
idempotent. Replay reconciles rather than duplicating - the piece deferred out
of WP-0004-T05 - and is tested to leave exactly one custody record.

Backend selection is by AUDIT_CORE_DATABASE_URL; the SQLite fallback logs a
warning so a deployment that lost its URL is visible rather than quietly
running on the wrong store.

Also fixed: ingestion had no __main__ guard, so python -m audit_core.ingestion
silently did nothing. Found during end-to-end smoke.

Counting semantics documented: occurrences counts transmissions, not stored
events, so a retry of a secret-shaped field increments it again. That is the
sender behaviour being optimized away.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 17:09:46 +02:00

42 lines
1.7 KiB
Makefile

SHELL := /usr/bin/env bash
.DEFAULT_GOAL := help
AUDIT_CORE_MOCK_DIR ?= /tmp/audit-core
test: ## Run unit tests
python3 -m pytest -q
mock-audit-smoke: ## Write one non-secret smoke event to the mock audit backend
AUDIT_CORE_MOCK_DIR="$(AUDIT_CORE_MOCK_DIR)" python3 -m audit_core emit \
--source audit-core \
--action audit_core.mock_backend.smoke \
--resource "$(AUDIT_CORE_MOCK_DIR)" \
--outcome success \
--detail backend=mock-file
mock-audit-cleanup: ## Remove mock audit files older than the retention window
AUDIT_CORE_MOCK_DIR="$(AUDIT_CORE_MOCK_DIR)" python3 -m audit_core cleanup
help: ## Show this help
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} \
/^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-24s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
.PHONY: test test-pg pg-test-up pg-test-down mock-audit-smoke mock-audit-cleanup help
PG_TEST_CONTAINER ?= ac-pg-test
PG_TEST_PORT ?= 55445
PG_TEST_URL ?= postgresql://postgres:test@127.0.0.1:$(PG_TEST_PORT)/audit_core
pg-test-up: ## Start a throwaway PostgreSQL for backend conformance tests
-docker rm -f $(PG_TEST_CONTAINER) 2>/dev/null
docker run -d --name $(PG_TEST_CONTAINER) -e POSTGRES_PASSWORD=test \
-e POSTGRES_DB=audit_core -p 127.0.0.1:$(PG_TEST_PORT):5432 postgres:16-alpine
@for i in $$(seq 1 40); do \
docker exec $(PG_TEST_CONTAINER) pg_isready -U postgres >/dev/null 2>&1 && exit 0; \
sleep 1; done; echo "postgres did not become ready" >&2; exit 1
pg-test-down: ## Remove the throwaway PostgreSQL
-docker rm -f $(PG_TEST_CONTAINER)
test-pg: ## Run the suite including PostgreSQL conformance (needs pg-test-up)
AUDIT_CORE_TEST_DATABASE_URL="$(PG_TEST_URL)" python3 -m pytest -q