@parmanasystems/audit-db
v1.98.56
Published
Deterministic audit persistence infrastructure for immutable governance lineage, replay-safe execution history, provenance continuity, and independently verifiable governance evidence storage.
Maintainers
Readme
@parmanasystems/audit-db
Append-only PostgreSQL audit persistence for the Parmana governance runtime. Every governed execution, verification result, security event, and API access is recorded fire-and-forget — writes never block server responses. The schema is managed by runMigrations(), which is called automatically during server startup. The AuditDb class exposes typed read methods for the dashboard and /audit/* API routes.
Public API
class AuditDb {
constructor(connectionString: string)
/** Check database connectivity. Throws on failure. */
async ping(): Promise<void>
/** Run schema migrations. Called automatically by the server on startup. */
async migrate(): Promise<void>
/** Alias for migrate(). */
async runMigrations(): Promise<void>
/** Close the connection pool. */
async close(): Promise<void>
async disconnect(): Promise<void>
// ── Write methods (fire-and-forget — do not await) ──────────────────────
/** Record a governance execution attestation. Idempotent on execution_id. */
recordDecision(attestation: ExecutionAttestation): void
/** Record the result of an attestation verification. */
recordVerification(executionId: string, result: OperationalVerificationResult): void
/** Record a security event (auth failure, replay attempt, etc.). */
recordSecurityEvent(event: SecurityEventInput): void
/** Record an API access log entry. */
recordApiAccess(access: ApiAccessInput): void
// ── Read methods ─────────────────────────────────────────────────────────
/** Paginated decision timeline with optional filters. */
async getDecisionTimeline(
limit?: number, // default: 100
filter?: DecisionFilter
): Promise<DecisionTimelineRow[]>
/** Aggregate statistics (counts of decisions, verifications, security events, API calls). */
async getStats(): Promise<AuditStats>
/** Look up a single decision record by execution ID. Returns null if not found. */
async getDecisionById(executionId: string): Promise<AuditDecision | null>
}
/** Run schema migrations using a pre-existing PoolClient. */
async function runMigrations(client: PoolClient): Promise<void>
// ── Input types ─────────────────────────────────────────────────────────────
interface SecurityEventInput {
event_type: string;
severity: SecurityEventSeverity;
ip_address?: string;
path?: string;
method?: string;
user_agent?: string;
details?: Record<string, unknown>;
}
type SecurityEventSeverity = "low" | "medium" | "high" | "critical"
interface ApiAccessInput {
method: string;
path: string;
status_code: number;
response_time_ms?: number;
ip_address?: string;
user_agent?: string;
executionId?: string;
}
interface DecisionFilter {
policyId?: string;
decision?: string;
from_date?: string;
to_date?: string;
}
// ── Row types ────────────────────────────────────────────────────────────────
interface AuditStats {
total_decisions: string;
decisions_today: string;
total_verifications: string;
valid_verifications: string;
invalid_verifications: string;
total_security_events: string;
total_api_calls: string;
}
interface DecisionTimelineRow { /* columns from view_decision_timeline */ }Environment variables
| Variable | Description |
|---|---|
| AUDIT_DATABASE_URL | PostgreSQL DSN. The server reads this directly — if unset, AuditDb is not instantiated and all /audit/* routes are absent. |
In the docker-compose setup, this is constructed automatically:
postgresql://Parmana:${POSTGRES_PASSWORD}@postgres:5432/Parmana_auditPackage wiring
@parmanasystems/audit-db imports ExecutionAttestation from @parmanasystems/execution and OperationalVerificationResult from @parmanasystems/verifier. It has no other internal @parmanasystems dependencies. The server creates a single AuditDb instance on startup and passes it to route handlers, which call recordDecision, recordVerification, and recordSecurityEvent after each request.
