This document provides guidelines for AI agents collaborating on the sentinel project.
Sentinel is a Rust library implementing NGAC-inspired (Next Generation Access Control) policy enforcement. It provides a centralized Policy Enforcement Point (PEP) backed by an attribute-matching policy graph, enabling applications to make authorization decisions through a single, auditable system.
Key characteristics:
- Domain-agnostic: Applications define their own resource types, operations, and attribute vocabularies
- Attribute-matching: Resources are NOT nodes in the graph; Object Attribute (OA) nodes carry metadata about which resource attributes they match, keeping the graph small regardless of data volume
- Event-sourced: The policy graph is persisted via the
epochCQRS/event-sourcing framework — sentinel uses epoch for storage, it does not implement its own backends - Two enforcement modes: Point checks (
evaluate) for command authorization and scope resolution (scope) for list-query filter injection
sentinel/
├── sentinel_core/ # Pure graph model, traits, PEP evaluation, scope resolution
├── sentinel_derive/ # Proc macros for policy enforcement annotations
└── sentinel/ # Facade crate with feature-gated re-exports
Sentinel depends on epoch_core for event sourcing. The consuming application configures epoch's backends (PG for production, in-memory for tests) — sentinel has no backend-specific crates.
As an AI agent, you are a collaborator in this project. Your primary responsibilities include:
- Implementing Features: Writing Rust code to implement new functionality as defined in specifications.
- Writing Tests: Creating unit and integration tests following TDD principles.
- Bug Fixes: Identifying and fixing bugs in the existing codebase.
- Refactoring: Improving structure, performance, and readability without changing external behavior.
- Documentation: Maintaining rustdoc comments and architecture documentation.
- List Files: Start by listing project files, avoiding
target/. - Read
Cargo.toml: Inspect workspace and crate dependencies. - Build:
cargo buildto verify the development environment. - Test:
cargo testto run the existing test suite.
The agent must not write any code until the developer has explicitly approved the implementation plan.
- User Prompt: The developer initiates a task.
- Codebase Grounding: Explore the existing codebase, avoiding
target/. - Specification Generation: Create or update a spec in
specs/. Detail files to modify, code changes, new dependencies, and expected outcome. - User Review: Wait for developer approval of the spec.
- Implementation Plan: Generate a step-by-step plan following TDD (failing test → implementation → refactor).
- User Review: Wait for developer approval of the plan.
- Implementation: Follow the approved plan precisely.
- Verification: Run tests to verify changes and check for regressions.
- Summarize: Provide a concise summary of implemented changes.
- Formatting:
cargo fmt - Linting:
cargo clippy -- -D warnings— zero warnings - Error Handling: Use proper error types. No
unwrap()/expect()in library code (tests OK). - Documentation: All public APIs must have rustdoc comments.
- Dependencies: Keep minimal and justified.
This project follows Conventional Commits:
<type>(<scope>): <description>
Types: feat, fix, refactor, test, docs, perf, chore
Scopes: core, derive, graph, pep, scope
Examples:
feat(core): implement policy graph node types and assignments
feat(pep): add evaluate() for point access checks
feat(scope): implement scope resolution with attribute constraints
test(core): add graph traversal property tests
docs(core): document PEP trait and usage patterns
Follow a natural growth pattern where structure emerges from complexity:
- Start Simple: Single
.rsfile for new functionality - Grow: Add features as the module evolves
- Split: When complexity emerges (~500-1000 lines or clear conceptual divisions), create a directory
- Nest: Apply the same pattern recursively for complex components
- Elevate: Move shared code to the lowest common ancestor
- Avoid Premature Abstraction: Don't create directories or split files "just in case"
- Follow Domain Boundaries: Group by feature/responsibility, not technical layer
- Keep Related Code Close: Code that changes together lives together
- Test Structure Mirrors Source: Split tests along the same boundaries as source
The graph has 4 node types:
| Type | NGAC Name | Description |
|---|---|---|
| U | User | Individual subject (user, machine, system process) |
| UA | User Attribute | Role, group, or subject category |
| OA | Object Attribute | Resource scope with attribute metadata |
| PC | Policy Class | Top-level policy scope (org, platform) |
Two relationship types:
- Assignments: U→UA, UA→UA, OA→OA, OA→PC (hierarchy)
- Associations: (UA, OA, {access_rights}) — permission grants
Object Attribute nodes carry metadata about resource attributes they match:
ObjectAttribute {
id: Uuid,
name: "alpha_jobs",
resource_type: "job",
attribute_key: "organization_id",
attribute_values: vec![alpha_org_id],
}This keeps the graph small (hundreds of nodes) regardless of data volume. Specific-object access uses the same mechanism with attribute_key: "id".
Two operations:
evaluate(subject, operation, resource_attrs) → Decision: Point check for command authorizationscope(subject, operation, resource_type) → AccessScope: Produces attribute constraints for list-query filter injection
The policy graph is event-sourced using epoch_core. Policy mutations (create node, add assignment, create association) are commands processed by a policy aggregate that emits events. This provides a full audit trail of policy changes.
- Unit Tests: Each module should have its own tests
- Integration Tests: In the
tests/directory for cross-module interaction - Run:
cargo test
- Parallel & Isolated: Tests run in parallel; don't rely on shared mutable state
- Idempotent: Tests must pass on repeated runs
- Behavior-Focused: Test observable behavior and contracts, not implementation details
- TDD: Write failing test first, then implement, then refactor
- Brainstorm:
docs/2602180855_brainstorm_policy_enforcement_authorization.typ— original design brainstorm with full context on attribute-matching vs objects-in-graph, scope constraint design, and integration strategy - Epoch Guide: The epoch framework's
docs/guide.mdexplains event sourcing patterns sentinel builds on