Implement capture policy engine and sensitive-context exclusions #69
No reviewers
Labels
No labels
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
nsaspy/local-recall!69
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "agent/issue-17-capture-policy"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #17.
Problem and scope
Implements the authoritative Local Recall capture policy security boundary. Policy now makes separate, typed decisions for screenshot capture, metadata capture, OCR, indexing, summarization, and remote-provider eligibility using normalized Local Recall metadata and lifecycle generation state.
The policy engine remains side-effect free. Enforcement is provided through typed adapters/guards that prevent denied or stale work from invoking capture, persistence, indexing, summarization, or provider callbacks.
Architecture
local_recall.domain.policy: project-owned policy operations, phases, sanitized decisions/authorizations/status, reason codes, and temporary-sensitive scopes.local_recall.policy.PolicyEngine: deterministic evaluator over validated configuration plus runtime privacy/lock/generation state.CapturePolicyAdapter: bridges the engine to the existingCapturePolicycapture port rather than creating a parallel capture model.PolicyGuard: revalidates policy revision/generation and capture generation immediately before side effects.PolicyEnforcementBoundary: phase-specific callback boundary for pre-capture, post-capture/pre-persistence, and downstream operations.Policy phases
Operations
Independent operations are:
screenshotmetadataocrindexingsummarizationremote-providerRemote-provider policy is eligibility only; it cannot perform network/provider calls.
Deterministic precedence
Security precedence is explicit:
Configured deny always beats configured allow regardless of numeric priority. Within matching rules of the same effect, higher priority wins and equal priority is resolved by stable rule ID, so configuration ordering cannot change the result.
Default-deny sensitive contexts
Narrow built-in safety rules cover password-manager classes, common lock-screen classes, and common authentication prompts/title indicators. Configured sensitive applications/workspaces support additional environments such as dedicated pentesting workspaces without treating every terminal as sensitive.
privacy-strictcontinues to require default deny.Manual privacy mode
Manual privacy mode is engine state, not a UI label. Toggling it increments policy generation and invalidates prior authorization. Capture-sensitive operations and remote eligibility fail closed while active. Lock-state transitions use the same invalidation model.
Temporary sensitive context
Typed control methods support temporary sensitivity for:
window.idworkspaceMarkers have explicit TTLs (1 second through 24 hours), are bound to capture generation, take effect immediately, can be explicitly cleared, expire deterministically, and cannot silently survive into a later capture generation.
Pattern security
Title patterns are validated and compiled before hot-path evaluation. Policy config bounds rules to 256, title patterns to 256 characters, and evaluated metadata strings to 4096 characters. Malformed expressions, unsupported complex inline constructs, alternation, backreferences, nested repetition, excessive unbounded repetition, and oversized patterns are rejected. Security tests cover catastrophic-pattern candidates, malformed/oversized patterns, Unicode, and oversized metadata.
No dependency was added for regex handling.
Domain, time, and metadata-source matching
url.domainmetadata and never reconstructs full URLs.example.comcannot matchnotexample.com; subdomain semantics require an explicitinclude_subdomainsflag and a label boundary.xorg-generic,qtile, andactivitywatch; adapter-specific raw payloads are not consumed.Policy revision and stale work
Each loaded policy has an immutable revision plus an in-memory generation. Privacy transitions, lock transitions, temporary-sensitive changes/expiry, and policy replacement invalidate older authorization. A later policy reload therefore cannot silently make queued work authorized under an older revision current again.
The existing validated configuration manager remains the outer atomic configuration boundary and fails malformed reloads to the repository's established safe configuration semantics.
Sanitized audit/status behavior
Policy decisions contain only control information: operation, phase, allow/deny, certainty, stable reason code, optional rule ID, revision, and generation. They cannot carry screenshots, OCR text, titles, domains, or matched substrings.
Status exposes revision/generation, enabled-rule count, privacy/lock state, health, and sanitized error code only. Effective-config inspection masks policy application/title/workspace/domain/source matcher values. Existing audit models reject raw policy-match attributes outside their closed safe field set.
Synthetic security fixtures seed recognizable credential/title/domain/command-like values and assert they do not appear in expected sanitized outputs. The repository's detect-secrets baseline remains unchanged; two intentionally synthetic test literals use the scanner-supported inline allowlist pragma only on those fixture lines.
Configuration/schema impact
The existing immutable Pydantic configuration is extended with typed policy selectors, closed enums, stable IDs, timezone/time-window validation, bounds, and
extra='forbid'. Changes are additive with safe defaults, so schema version 1 remains valid and no persistent migration is required.TDD evidence
RED
Commit
f1c6cbda6d56de8dd3d19fa60251ed175705663d, Actions run31621658847, job94197728739.Formatting, Ruff, and ShellCheck passed. Strict Pyright then failed specifically because the #17 production policy module/types/config fields did not exist yet. Earlier formatting/lint-only failures were explicitly rejected as invalid red evidence and corrected before recording this red state.
GREEN
Final exact PR head:
c4d850900a6980487a45d7e4eae59c3f8ebc6703.Actions run
31625941909is fully green on the exact head:Functional coverage on the final path includes:
Full canonical gate
Job
94212233905ran the repository's canonical command exactly:./scripts/checkand passed on exact head
c4d850900a6980487a45d7e4eae59c3f8ebc6703.The execution environment available to this agent could not materialize the private repository checkout locally, so local filesystem execution was not falsely claimed; the exact canonical command was added as a required PR CI job and executed successfully on the tested head.
Acceptance-criteria mapping
domain.policy+PolicyEnginedocs/policy.mdKnown limitations / boundaries