AkurAI Build
Menu

Public repository / 4fac5c7826068a0d615a09851633816b41204468

AkurAI-Notes / docs/adr/006-authz-scopes-redaction.md

Latest change 3f840dbe5150769b17edb21aa5e15a212d9df54d - OLI-115: ADR 006 authz scopes, secret gate, redaction by Ólafur Búi Ólafsson

# ADR 006: Authorization scopes, secret gate and redaction for akurai-index / akurai-memory

Status: Proposed (SecOps, 2026-09-30) · Epic OLI-112 · Task OLI-115

## Context
The index and memory store embed source and agent-written text. Embeddings are hard to scrub after the fact, and a recalled memory lands in agent context, so a secret that gets in is disclosed repeatedly (STRIDE: Information disclosure, Tampering). Clients are agents with differing trust.

## Decisions
1. **Ingestion gate (before embed, before store):** path exclusion -> gitleaks -> PII redaction. A chunk with a gitleaks hit is dropped whole (not redacted); the audit row records rule id, repo, path, blob sha, never the match. Path exclusions: `.env*`, `*.pem|key|p12|pfx|kdbx`, `id_*`, `*secret*`, `*credentials*`, lockfiles, sqlite/db, `target/`, `node_modules/`, `.git/`. PassVault: names only, never values.
2. **Redaction** (emails, JWTs, IPs; tokens by gitleaks) applies to memory text on `remember` and to extractor output too, since extractors see run transcripts. Redaction is lossy and one-way.
3. **Authz in SQL, not client:** Postgres RLS (FORCE) on `memory` and chunk tables, non-owner role `akurai_app`; service sets `SET LOCAL akurai.scopes/actor/projects` per transaction from the verified token. Default deny when GUCs are unset.
4. **Scopes (OAuth 2.1 via AkurAI ID, audience `akurai-mcp`, PKCE, short-lived tokens, no implicit flow):** `index:read`, `memory:read`, `memory:write`, `memory:forget`. `forget` is owner/board only. Agent tokens get read + write by default, never forget. Tool annotations (`readOnlyHint`/`destructiveHint`) must match the scope required; server rejects mismatch.
5. **Audit:** append-only `audit_log` (trigger blocks UPDATE/DELETE/TRUNCATE, no grants beyond INSERT/SELECT) recording actor, run id, action, scopes, target. Covers writes, forgets, gate drops, authz denials. Forget = tombstone + vector delete + audit row. DB superuser can still alter it, so the nightly dump ships off-host (Platform backup) and gives Auditability by retention.
6. Service secrets via akurai-secret only; loopback listeners; token introspection/JWKS cached with short TTL.

## Threats not fully covered (residual)
Prompt injection stored in a memory and recalled later (mitigation: recall output is cited and marked untrusted; review queue for low confidence). Secrets not matching gitleaks rules. Superuser tampering. Semantic leakage across scopes via dedupe (dedupe must run inside the caller's RLS view).

## Verification (this task)
Prototype `security/akurai-index/ingest_gate.py`; reference SQL `authz_audit.sql` (untested against live PG, pending Platform PG17 instance). Acceptance tests for QA: secret fixture never reaches embed input; RLS denies cross-scope read; UPDATE/DELETE on audit_log fails.