P0.2: Governance Audit Trail Persistence

Wire PostgreSQL persistence into policy-gateway to persist OPA decisions for compliance/audit requirements per last-mile-plan.md.

Background

Current State: Policy-gateway uses in-memory GovernanceStore (max 1000 entries, lost on restart).

Target State: Persistent storage via PostgreSQL using existing PostgresAuditLogEntryRepository.

What Already Exists:


Plan (Specific, No Placeholders)

policy-gateway Service

[MODIFY] settings.py

Add PostgreSQL connection settings with explicit env names:


[NEW] database.py

Async database connection manager:


[MODIFY] main.py

Update lifespan to manage database lifecycle:


[MODIFY] routes.py

Update to use PostgreSQL-backed audit persistence:


[NEW] audit_repository_adapter.py

Service-local adapter to support filtered audit log queries:


Database Migration

[NEW] Alembic configuration (services/policy-gateway)

Add baseline Alembic files so migrations are runnable:

[NEW] 001_create_audit_log_entries.py

Alembic migration for audit_log_entries table:


Runtime and Infrastructure

[MODIFY] entrypoint.sh

Remove database migration logic from the service entrypoint:

[NEW] Deployment migration step

Run migrations as a separate step prior to deploying new replicas:

[MODIFY] pyproject.toml

Add required runtime dependencies:


Verification Plan

Automated Tests

  1. Existing Repository Integration Tests
    1
    2
    3
    
    just test-adapters governance-runtime
    # Or directly:
    pytest libs/governance-runtime/adapters/tests/integration/test_audit_log_entry_repository_integration.py -v
    
  2. Policy Gateway Integration Test (new)
    1
    
    pytest services/policy-gateway/tests/test_audit_persistence.py -v
    

Manual Verification

  1. Start dev environment: just dev-up && just skeleton-up
  2. Start policy-gateway: cd services/policy-gateway && python -m uvicorn main:app
  3. Make a policy evaluation request:
    1
    2
    3
    
    curl -X POST http://localhost:8080/policy/evaluate \
      -H "Content-Type: application/json" \
      -d '{"prompt": "test prompt", "user_id": "test-user"}'
    
  4. Query audit log via API:
    1
    
    curl http://localhost:8080/governance/audit?limit=10
    
  5. Verify entry appears in Workbench at http://localhost:5173/governance → Audit Log tab
  6. Restart policy-gateway and verify audit entries persist

Risks

[!WARNING] Breaking Change (Configurable): In-memory behavior is preserved only when POLICY_GATEWAY_AUDIT_STORE_BACKEND=memory and POLICY_GATEWAY_DATABASE_URL is unset. The code path uses services/policy-gateway/src/store/memory_store.py and is selected by services/policy-gateway/src/config/settings.py in services/policy-gateway/src/api/routes.py.
Postgres Mode: Requires POLICY_GATEWAY_AUDIT_STORE_BACKEND=postgres and POLICY_GATEWAY_DATABASE_URL=postgresql+asyncpg://policy_user:policy_pass@postgres:5432/sea_governance; migrations must be run via the separate migration step before deploying new replicas.


Completion Criteria