Complete end-to-end build plan for the embedded ReBAC authorization runtime.
Total estimate: ~40 weeks with 2-3 engineers Phases: 0 (Foundation) → 1 (Engine) → 2 (SDK) → 3 (Distributed)
Sprints 0–9 Complete — 327 tests pass (267 unit + 10 integration + 36 V2 multi-model + 4 stress + 2 soak + 8 test-utils), 0 failures — V1+V2 fully implemented
| Sprint | Focus | Status | Key Deliverables |
|---|---|---|---|
| 0 | Security Hardening | ✅ | 18 vulnerabilities fixed |
| 1 | Engine Features | ✅ | ABAC, OTel, hot-reload, recover, lint, FullyConsistent, logger callback |
| 2 | Storage Backends | ✅ | MySQL, PG/RocksDB event compaction, required trait methods |
| 3 | NAPI/TS SDK | ✅ | 14 exports, JsWatchSubscription, JsTransaction |
| 4 | CLI & REPL | ✅ | 19 commands, REPL with tab completion + colors, --dry-run |
| 5 | Test Coverage | ✅ | 24 new tests across 12 categories |
| 6 | Polish & Cleanup | ✅ | Dead code removal, deps cleanup, LRU cache, rate limiter GC, health fields, CI hardening, supply-chain docs |
| 7 | Go & Python SDKs | ✅ | C FFI, Go CGo SDK, PyO3 bindings |
| 8 | V1 Closure | ✅ | 7 bug fixes, 4 gap closures, 10 integration tests, docs alignment |
Architecture: Purely embedded — zero servers, ports, HTTP/gRPC listeners. SQLite + RocksDB as embedded storage backends (PG/MySQL available programmatically, not in CLI).
Key decisions: std::thread::scope for parallelism (not tokio), synchronous MPSC for watch events (zero threads), AtomicBool for parallel_eval toggle, OnceLock<SdkMeterProvider> for OTel.
- Architecture Overview
- Dependency Graph
- Phase 0 — Foundation
- Phase 1 — Engine Core
- Phase 2 — SDK & Integration
6. Phase 3 — Distributed & Scale — Removed (embedded-only) - Key Architectural Decisions
- Risk Assessment
- Integration & E2E Test Index
┌─────────────────────────────────────┐
│ Application │
│ (Node/Go/Python/Rust) │
└──────────────┬───────────────────────┘
│
┌──────────────▼───────────────────────┐
│ SDK Layer │
│ (NAPI / CGo / PyO3 / Pub) │
└──────────────┬───────────────────────┘
│
┌──────────────▼───────────────────────┐
│ Aegis Runtime │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Graph Engine │ │
│ │ ├─ Recursive Traversal │ │
│ │ ├─ Cycle Detection │ │
│ │ ├─ Parallel Sibling Eval │ │
│ │ │ (std::thread::scope) │ │
│ │ └─ Policy Resolution │ │
│ └───────────┬─────────────────────┘ │
│ │ │
│ ┌───────────▼─────────────────────┐ │
│ │ Transaction Manager │ │
│ │ + Revision Control │ │
│ └───────────┬─────────────────────┘ │
│ │ │
│ ┌───────────▼─────────────────────┐ │
│ │ Cache Layer │ │
│ │ (Decision + Traversal) │ │
│ └─────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Connection Manager │ │
│ │ ├─ Read Pool │ │
│ │ └─ Write Conn (serialized) │ │
│ └───────────┬─────────────────────┘ │
└──────────────┼────────────────────────┘
│
┌──────────────────────────┼──────────────────────────┐
│ ┌───────────────▼───────────────┐ │
│ │ Storage Adapter Layer │ │
│ │ │ │
┌────▼────┐ ┌───────▼──────┐ ┌───────▼──────┐ │
│ SQLite │ │ RocksDB │ │ PG/MySQL │ │
│ (WAL) │ │ (LSM-tree) │ │ (programmatic │ │
└─────────┘ └──────────────┘ │ only, not │ │
│ in CLI) │ │
└───────────────┘ │
│ │
┌────▼────────────────────────────────────────────────┐ │
│ ~~CRDT Sync Layer~~ — Removed (embedded-only) │ │
└─────────────────────────────────────────────────────┘ │
│ │
┌────▼────────────────────────────────────────────────┐ │
│ Observability (OTel + Logger) │ │
│ ├─ Spans for check/write/delete/explain │ │
│ ├─ Metrics (latency, count, cache, graph size) │ │
│ └─ Structured log callback │ │
└─────────────────────────────────────────────────────┘ │
│
┌────────────────────────────────────────────────┐ │
│ CLI + REPL │ │
│ ├─ aegis check / write / delete / explain │ │
│ ├─ aegis backup / export / import │ │
│ ├─ aegis schema lint │ │
│ ├─ aegis health │ │
│ └─ aegis repl (interactive shell) │ │
└────────────────────────────────────────────────┘ │
Phase 0 (Foundation)
└── All phases depend on this
Phase 1.1 (SQLite + Connection Manager)
└── 1.2 (Graph Engine)
├── 1.4 (Explain/Tracing)
└── 1.6 (Cache)
└── 1.7 (Write Operations)
1.3 (Transactions + Revisions)
└── 1.5 (Schema Migration)
└── 1.7 (Write Operations)
1.2 + 1.3 + 1.7
└── Phase 2 (SDKs)
├── 2.1 (TypeScript SDK)
├── 2.2 (CLI)
├── 2.3 (REPL)
└── 2.8 (Go/Rust/Python SDKs)
Phase 1 + 2 complete
├── Event Log, PG/MySQL, RocksDB, Watch Streams
├── OTel, GDPR
└── Embedded engine stable
Goal: Project skeleton, tooling, CI, and core data structures.
| Step | Component | Description | Status |
|---|---|---|---|
| 0.1 | Rust crate scaffold | cargo init with workspace: aegis-core + aegis-test-utils |
✅ |
| 0.2 | Data model types | SubjectId, ResourceId, Relation, RelationshipTuple, Revision, RevisionToken, Schema, TupleKey, TupleAction, ConsistencyMode, pagination types |
✅ |
| 0.3 | Error hierarchy | AegisError enum (20+ variants across Storage, Schema, Validation, Consistency, RateLimit, Internal) + AegisResult<T> alias |
✅ |
| 0.4 | Schema parser + linter | YAML parser, orphan detection, circular inheritance check, undefined reference detection, compatibility checker | ✅ |
| 0.5 | Storage adapter trait | StorageBackend trait (15 methods: write, read, delete, list, query, transact, audit, integrity, close) + StorageTransaction + TupleFilter |
✅ |
| 0.6 | Build system | Cargo.toml workspace, rust-toolchain.toml, rustfmt + clippy config, .gitignore |
✅ |
| 0.7 | Test harness | TestAegis in-memory engine with write/check/delete/list/query/pagination; load_fixture_yaml(); 4 built-in fixtures |
✅ |
| 0.8 | CI pipeline | GitHub Actions: format check, clippy, test (debug + release), coverage, security audit | ✅ |
Files created: 22 source files across 2 crates
Tests: 217 total (211 unit + 6 integration) — all passing
Goal: Full embedded authorization engine with SQLite, graph traversal, policies, and transactions.
| Step | Component | Description | Dependencies |
|---|---|---|---|
| 1.1.1 | SQLite adapter | Implement StorageBackend via rusqlite with WAL mode PRAGMA |
Phase 0 (trait defined) |
| 1.1.2 | Connection manager | Read pool (r2d2) + single dedicated write connection; busy_timeout=5000; health checks |
1.1.1 |
| 1.1.3 | Revision counter | Atomic revision in _aegis_meta table; next_revision() as SQL function |
1.1.1 |
| 1.1.4 | Index implementation | B-tree indexes on (subject, relation, object), (object, relation), (subject) |
1.1.1 |
| 1.1.5 | Schema version table | _aegis_schema(version, applied_at, checksum); read on init |
1.1.1 |
Tests: INT-001 through INT-005, PER-001 through PER-003
| Step | Component | Description | Dependencies |
|---|---|---|---|
| 1.2.1 | Recursive traversal | BFS/DFS engine with adjacency lookups via object/subject indexes | 1.1.4 |
| 1.2.2 | Cycle detection | HashSet<(SubjectId, Relation)> visited-node tracking; any revisit → branch returns deny |
1.2.1 |
| 1.2.3 | Parallel evaluation | tokio::task::spawn for sibling branches; CancellationToken for short-circuit on first allow |
1.2.1 |
| 1.2.4 | Policy resolution | Resolve permission → [relations] from schema; walk inheritance chain for each relation |
Phase 0 (schema types) |
| 1.2.5 | check() implementation |
lookup → resolve → traverse → decide; return CheckResult { allowed, revision } |
1.2.1–1.2.4 |
Tests: INT-001, INT-002, INT-011, INT-012, E2E-015
| Step | Component | Description | Dependencies |
|---|---|---|---|
| 1.3.1 | begin_transaction() |
SQLite BEGIN IMMEDIATE → StorageTransaction implementation |
1.1.2 |
| 1.3.2 | Savepoints | SAVEPOINT sp1 / RELEASE sp1 / ROLLBACK TO sp1 within transaction |
1.3.1 |
| 1.3.3 | Revision-based snapshots | Read against specific revision (stored in meta table); SELECT ... WHERE revision <= target |
1.1.3 |
| 1.3.4 | Consistency modes | minimize_latency (latest), at_revision (token-based), fully_consistent (flush + read) |
1.3.3 |
| 1.3.5 | Revision token encoding | RevisionToken { revision: u64, node_id: Uuid, timestamp } |
Phase 0 (types) |
Tests: INT-010 through INT-014, INT-020 through INT-024
| Step | Component | Description | Dependencies |
|---|---|---|---|
| 1.4.1 | Trace path recording | Record visited nodes during traversal as Vec<(SubjectId, Relation)> |
1.2.1 |
| 1.4.2 | explain() API |
{ allowed, path: Vec<String>, resolvedVia: String, durationMs, revision, cacheHit } |
1.4.1 |
| 1.4.3 | resolvedVia formatting |
Format derivation chain: "editor → member → owner" (right-facing arrows) |
1.4.1 |
Tests: INT-007
| Step | Component | Description | Dependencies |
|---|---|---|---|
| 1.5.1 | Migration runner | Read _aegis_schema version; apply up() scripts in order; log each application |
1.1.5 |
| 1.5.2 | Migration scripts | V1→V2, V2→V3 as additive patterns (new types, new relations) | Phase 0 (schema types) |
| 1.5.3 | Rollback support | down() for each migration; --to-version N CLI flag |
1.5.1 |
| 1.5.4 | Compatibility check | check_schema_compatibility(existing, new) → SchemaCompatibilityReport |
Phase 0 (validator) |
| 1.5.5 | Auto-migrate on init | autoMigrate: true → call migration runner during initialize() |
1.5.1 |
Tests: INT-030 through INT-037
| Step | Component | Description | Dependencies |
|---|---|---|---|
| 1.6.1 | Decision cache | HashMap<(SubjectId, Relation, ResourceId, Revision), bool>; TTL-based expiry; LRU eviction |
1.2.5 |
| 1.6.2 | Traversal cache | Cache HashMap<(SubjectId, Relation), Vec<ResourceId>> for intermediate path segments |
1.2.1 |
| 1.6.3 | Cache invalidation | On write → bump revision → compare entry.revision < current_revision → evict stale entries |
1.1.3 |
| 1.6.4 | Cache config | maxEntries (LRU cap), decisionTtlMs (TTL), traversalCacheSize |
— |
Tests: INT-040 through INT-044
| Step | Component | Description | Dependencies |
|---|---|---|---|
| 1.7.1 | Single write() |
Validate → begin tx → upsert tuple → bump revision → commit → return token | 1.1.2, 1.3.1 |
| 1.7.2 | writeBatch() |
Validate all → begin tx → upsert all → single revision bump → commit | 1.3.1 |
| 1.7.3 | delete() |
Remove tuple by key → bump revision | 1.1.2, 1.3.1 |
| 1.7.4 | deleteSubject() |
DELETE FROM tuples WHERE subject = ? → bump revision |
1.1.2 |
| 1.7.5 | deleteObject() |
DELETE FROM tuples WHERE object = ? → bump revision |
1.1.2 |
| 1.7.6 | list() |
Filter by subject/object/relation with optional relation filter | 1.1.4 |
| 1.7.7 | Paginated query() |
Cursor-based: SELECT ... WHERE id > cursor ORDER BY id LIMIT N |
1.1.4 |
| 1.7.8 | Idempotent write | INSERT OR REPLACE for upsert; DELETE on non-existent = no-op |
1.1.1 |
Tests: INT-060 through INT-074
Goal: Language bindings (TypeScript first), CLI tools, developer experience.
| Step | Component | Description | Dependencies |
|---|---|---|---|
| 2.1.1 | NAPI-RS binding | napi-rs async functions exposing all engine APIs |
Phase 1 complete |
| 2.1.2 | TypeScript types | Full .d.ts matching the spec: AegisConfig, WriteResult, CheckResult, etc. |
2.1.1 |
| 2.1.3 | SDK wrapper class | new Aegis(config) → initialize() → all async methods |
2.1.2 |
| 2.1.4 | Async support | NAPI async work tasks; Promise-based API | 2.1.1 |
| 2.1.5 | Error mapping | AegisError enum variants → TypeScript AegisError class hierarchy |
2.1.1 |
Tests: E2E-001, SDK-001, SDK-002
| Step | Component | Description | Dependencies |
|---|---|---|---|
| 2.2.1 | CLI scaffold | clap-based binary with subcommands |
Phase 1 complete |
| 2.2.2 | aegis check |
--subject --relation --object → JSON/human output |
1.2.5 |
| 2.2.3 | aegis backup |
create (dump to file) + restore (load from file) |
1.7.1 |
| 2.2.4 | aegis export / import |
JSON format export/import of full graph | 1.7.6 |
| 2.2.5 | aegis schema lint |
Load YAML schema file → run linter → diagnostics output | Phase 0 (schema linter) |
| 2.2.6 | aegis health |
Connection status, schema version, revision, cache stats | 1.1.2 |
Tests: E2E-009, E2E-010, E2E-024, E2E-025
| Step | Component | Description | Dependencies |
|---|---|---|---|
| 2.3.1 | REPL shell | rustyline-based interactive shell with history |
Phase 1 complete |
| 2.3.2 | REPL commands | write, check, delete, list, explain, watch, schema, health |
1.2–1.7 |
| 2.3.3 | Tab completion | Auto-complete for commands + known subjects/relations/objects | 2.3.1 |
| 2.3.4 | Output formatting | Colored output; JSON mode (--json flag) |
2.3.1 |
| Step | Component | Description | Dependencies |
|---|---|---|---|
| 2.4.1 | createTestAegis() |
In-memory SQLite instance + fixture loader | 1.1.1 |
| 2.4.2 | Fixture parser | YAML → schema + tuple list; loadFixture() method |
Phase 0 (fixtures) |
| 2.4.3 | Snapshot assertion | expect(check()).toMatchSnapshot() via insta or custom |
1.2.5 |
| 2.4.4 | Test factories | generateLargeFixture(n, m) → Vec<RelationshipTuple> |
Phase 0 (test-utils) |
| Step | Component | Description | Dependencies |
|---|---|---|---|
| 2.5.1 | health() API |
Storage ping, schema version, revision, cache stats, connection pool stats | 1.1.2 |
| 2.5.2 | Storage integrity | PRAGMA quick_check (SQLite) on startup and periodic health checks |
1.1.1 |
| 2.5.3 | Graceful shutdown | close(): flush cache → checkpoint WAL → close connections |
1.1.2 |
| 2.5.4 | Fail-closed default | Storage error in hot path → return deny; configurable to fail-open |
1.2.5 |
Tests: INT-017, INT-018, ERR-001 through ERR-012
| Step | Component | Description | Dependencies |
|---|---|---|---|
| 2.6.1 | Dry-run check | check({...}, {dryRun: true}) — evaluate without side effects or cache update |
1.2.5 |
| 2.6.2 | Dry-run write | write({...}, {dryRun: true}) — validate schema + constraints, no persist |
1.7.1 |
| 2.6.3 | Audit log read | audit({object, from, to}) → Vec<AuditEntry> |
1.1.1 |
| 2.6.4 | Event log (implicit) | Append-only _aegis_events table populated on every mutation |
1.7.1 |
Tests: INT-050 through INT-053, INT-090 through INT-093
| Step | Component | Description | Dependencies |
|---|---|---|---|
| 2.7.1 | Hook callbacks | onWrite(tuple), onDelete(tuple), onCheck(event) registered in config |
Phase 1 complete |
| 2.7.2 | Schema file watcher | notify crate for FSEvent on schema file |
Phase 0 (schema) |
| 2.7.3 | Hot-reload logic | Detect change → validate compatibility → atomic swap → invalidate cache | 1.5.4 |
| Step | Component | Description | Dependencies |
|---|---|---|---|
| 2.8.1 | Go SDK | CGo bindings; context.Context support; idiomatic Go Aegis struct |
Phase 1 complete |
| 2.8.2 | Rust SDK | Published aegis crate on crates.io; zero-overhead pub use API |
Phase 1 complete |
| 2.8.3 | Python SDK | PyO3 bindings; asyncio-compatible async API |
Phase 1 complete |
Tests: E2E-002, E2E-003, E2E-004, SDK-003 through SDK-009
❌ Not applicable. Aegis is and will remain an embedded-only authorization runtime. Distributed features (CRDT sync, edge replicas, distributed cache, multi-region consistency, gRPC servers, WAL-based sync) are explicitly out of scope. They would require external infrastructure (Redis, HTTP/gRPC servers, network coordination, CDC pipelines) that violates the embedded-first philosophy.
The existing event log, PostgreSQL/MySQL backends, RocksDB, watch/subscription streams, OpenTelemetry, and GDPR compliance have been moved into earlier phases or are part of the core embedded engine. |
Tests: E2E-024, E2E-052, E2E-053
| Step | Component | Description | Dependencies |
|---|---|---|---|
| 3.8.1 | Cross-node tokens | Token encodes (revision, nodeId); validate token against local node |
1.3.5, 3.2.1 |
| 3.8.2 | Rate limiter | Token-bucket per tenant via governor crate |
Phase 1 complete |
| 3.8.3 | Max traversal depth | Configurable depth limit; deny + trace when exceeded |
1.2.1 |
| 3.8.4 | Input validation hardening | Max length, character set, injection prevention (already at type level) | Phase 0 (types) |
Tests: ERR-007, SEC-001 through SEC-005
| Decision | Choice | Rationale |
|---|---|---|
| Storage engine | SQLite WAL as primary; PG/RocksDB/IndexedDB as adapters | WAL allows concurrent reads + single writer; matches embedded-first philosophy |
| Concurrency | Single serialized writer + read pool via r2d2 |
No write-write conflicts; WAL enables non-blocking reads; simple locking model |
| Consistency | Revision-based snapshot isolation | Avoids TrueTime dependency; works in embedded context; 3 explicit modes |
| Transaction | SQLite ACID transactions (BEGIN IMMEDIATE) |
Free rollback, savepoints, atomic batch writes; no distributed tx complexity |
| Multi-node sync | ❌ Not applicable — embedded-only | Each instance manages its own state; shared PG/MySQL for cross-instance data |
| FFI | NAPI-RS (Node), CGo (Go), PyO3 (Python) | Best-in-class FFI for each language ecosystem; wide community support |
| Observability | Optional OTel (no-op when absent) | Zero runtime cost when not configured; industry standard for metrics/tracing |
| Cache | Decision + Traversal, LRU + TTL, revision-invalidated | Simple, effective; no distributed cache coherence needed in embedded mode |
| Error strategy | Fail-closed by default | Authorization safety: deny on uncertainty; configurable to fail-open for non-critical paths |
| Schema evolution | Additive-only by default; compatibility check before apply | Safe zero-downtime migrations; breaking changes require explicit two-phase process |
| ID format | type:identifier (e.g. user:123, repo:fluxbus) |
Simple, self-describing, no structured IDs needed; validated via regex |
| Tenant isolation | Namespace prefix scoping | No per-tenant infrastructure; natural graph partitioning via tenant:id prefix |
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| WAL checkpoint starvation under long reads | Medium | Medium | Watchdog monitor; abort stale read transactions after configurable timeout; busy_timeout |
| NAPI binding memory safety | Low | High | Heavy use of Rust's #[napi] safe patterns; Valgrind / ASan in CI; extensive integration tests |
| Go CGo overhead | Medium | Low | Minimize CGo call count; batch operations where possible; benchmark before optimization |
| Performance regression in deep traversals | Medium | Medium | criterion benchmark suite in CI; performance regression gate (--bench -- --threshold 5%) |
| Schema migration on live data | Low | High | Additive-only migrations by default; compatibility check before apply; rollback tested in staging |
| Concurrent access from multiple processes | Medium | Medium | SQLite file locking via WAL; document that multi-process requires PostgreSQL/RocksDB |
| Event log unbounded growth | Low | Medium | Configurable compaction + retention policy; archive to object storage |
| WASM size for IndexedDB backend | Medium | Low | Tree-shake unused backends; wasm-opt for binary size optimization |
Full test specifications are in aegis-test-plan.md.
| Category | Count | ID Range | Key Tests |
|---|---|---|---|
| Basic Write + Check | 5 | INT-001–005 | Direct check, deny, empty graph, idempotent write, metadata |
| Transaction Semantics | 5 | INT-010–014 | Atomic writes, rollback, savepoints, empty tx, tx with reads |
| Revision & Consistency | 5 | INT-020–024 | Revision increments, read-your-writes, token staleness, consistent mode, latency mode |
| Schema & Migration | 8 | INT-030–037 | Invalid schema, circular types, auto-migration, version tracking, rollback, compatibility |
| Cache Behavior | 5 | INT-040–044 | Cache hit, invalidation, TTL, max size, cache miss |
| Dry-Run Mode | 4 | INT-050–053 | Dry-run check, write, validation failure, cache behavior |
| Deletion | 4 | INT-060–063 | Delete existing, non-existent, one of many, bulk subject |
| Query & List | 5 | INT-070–074 | List by object/subject/relation, pagination, cursor |
| Watch / Subscription | 5 | INT-080–084 | Subscribe, sinceRevision, unsubscribe, multiple, wildcard |
| Audit Log | 4 | INT-090–093 | All mutations, time range, object filter, entry structure |
| Category | Count | ID Range | Key Tests |
|---|---|---|---|
| Full SDK Lifecycle | 4 | E2E-001–004 | TypeScript, Go, Rust, Python |
| Multi-Language Interop | 3 | E2E-010–012 | Write in Go read in Node, write in Node read in Python, cross-language token |
| Persistence & Recovery | 6 | E2E-020–025 | SQLite restart, PG restart, backup/restore, export/import |
| Event Log Recovery | 3 | E2E-030–032 | Full recovery, PIT recovery, compaction |
| Middleware Integration | 4 | E2E-040–043 | Express allowed/forbidden/missing-auth, Hono |
| Deployment Modes | ❌ Distributed modes removed |
| Category | Count | ID Range |
|---|---|---|
| Error Handling | 12 | ERR-001–012 |
| Concurrency & Stress | 10 | STR-001–010 |
| Persistence & Recovery | 7 | PER-001–007 |
| Multi-Tenancy Isolation | 6 | TEN-001–006 |
| Security & Boundary | 5 | SEC-001–005 |
| SDK Cross-Language | 9 | SDK-001–009 |
| Performance Benchmarks | 11 | BENCH-001–011 |
Total: 217 implemented (211 unit + 6 integration), 0 failures — [tracked in .opencode/plans/implementation-plan.md]
| Scenario | Recommended Backend | Config |
|---|---|---|
| Local development | SQLite | { storage: "sqlite", path: "./aegis.db" } |
| Unit / integration tests | In-memory SQLite | createTestAegis() |
| Single-server production | SQLite (WAL, PV mount) or PostgreSQL | See connection config examples |
| Multi-server production | PostgreSQL (shared) | { storage: "postgres", connectionString: "..." } |
| High-write throughput | RocksDB | { storage: "rocksdb", path: "./aegis-data" } |
| Browser / edge runtime | IndexedDB (WASM) | { storage: "indexeddb" } |
| ❌ |
| Aegis Version | Protocol | Storage Schema | Migration Path |
|---|---|---|---|
| V1 | rev-based | v1 (tuples + meta + schema) | — |
| V2 | rev-based | v2 (adds roles + valid_until + deny + ABAC conditions) | migrate(v1 → v2) |
| ❌ |
Document version 1.0 — Last updated: May 2026