Some checks failed
Documentation Lint & Validation / Markdown Linting (push) Has been cancelled
Documentation Lint & Validation / Validate mdBook Configuration (push) Has been cancelled
Documentation Lint & Validation / Content & Structure Validation (push) Has been cancelled
mdBook Build & Deploy / Build mdBook (push) Has been cancelled
Nickel Type Check / Nickel Type Checking (push) Has been cancelled
Rust CI / Security Audit (push) Has been cancelled
Rust CI / Check + Test + Lint (nightly) (push) Has been cancelled
Rust CI / Check + Test + Lint (stable) (push) Has been cancelled
Documentation Lint & Validation / Lint & Validation Summary (push) Has been cancelled
mdBook Build & Deploy / Documentation Quality Check (push) Has been cancelled
mdBook Build & Deploy / Deploy to GitHub Pages (push) Has been cancelled
mdBook Build & Deploy / Notification (push) Has been cancelled
on+re:
- core.ncl: 5 new Practice nodes (notification-channels,
vapora-capabilities, agent-hot-reload-stable-identity,
merkle-audit-trail, notification-channels) + 5 new edges;
knowledge-graph-execution-history updated with HNSW+BM25+RRF
- state.ncl: production-readiness blocker/catalyst updated (hot-reload
complete, BudgetManager/LLMRouter still require restart);
ontoref-integration catalyst updated (vapora-ontology/reflection
crates, api-catalog.json, nickel contracts)
ADRs (NCL):
- adr-013: KG hybrid search — HNSW+BM25+RRF, rejected in-process scan
- adr-014: capability packages — AgentDefinition→vapora-shared,
DashMap shard-before-await constraint
- adr-015: Merkle audit trail — SHA-256 hash chain, rejected HMAC
- adr-016: agent hot-reload — stable_id=role, learning_profiles survive
drain, BudgetManager excluded from reload scope
landing page:
- 2 new feature boxes: VCS-Agnostic Worktree (jj/git), Ontology Protocol
- KG box: 20→28 tests, HNSW+BM25+RRF description
- Agents box: 71→82 tests, hot-reload + stable_id
- tech stack: Rust 21→23 crates, added jj, Radicle, ontoref badges
- status badge: 620→691 tests
79 lines
5.1 KiB
XML
79 lines
5.1 KiB
XML
let d = import "adr-defaults.ncl" in
|
|
|
|
d.make_adr {
|
|
id = "adr-007",
|
|
title = "Cedar Policy Engine for Declarative Authorization",
|
|
status = 'Accepted,
|
|
date = "2024-11-01",
|
|
|
|
context = "Vapora needs per-stage execution control in the workflow engine and fine-grained access control at the API layer. The markdown ADR describes Cedar in vapora-backend, but as of 2026-03-27, Cedar is implemented in vapora-workflow-engine/src/auth.rs (CedarAuthorizer, loaded from .cedar policy files) — not in the vapora-backend API handlers directly. Cedar policy evaluation happens before workflow stage execution, controlling which principals may trigger which actions on which resources.",
|
|
|
|
decision = "Cedar (via cedar-policy crate) is the policy engine for authorization in vapora-workflow-engine. CedarAuthorizer loads .cedar policy files from a configurable directory at startup. All workflow stage execution requests are evaluated against the loaded policy set before the stage executes. Policy files are version-controlled in the project repo.",
|
|
|
|
rationale = [
|
|
{
|
|
claim = "Declarative policies are auditable and reviewable without reading Rust code",
|
|
detail = "A Cedar policy file expressing 'Architect role may trigger Deploy actions on any workflow' is readable by non-Rust engineers, auditable by security reviewers, and versionable in Git. An equivalent RBAC check buried in match statements is none of these.",
|
|
},
|
|
{
|
|
claim = "Cedar's formal verification model prevents policy logic errors",
|
|
detail = "Cedar policies are formally specified — the evaluator is proven to be sound and complete. Custom RBAC implementations in Rust carry no such guarantees and have historically introduced privilege escalation via logic errors in compound conditions.",
|
|
},
|
|
{
|
|
claim = "Policy changes do not require recompilation",
|
|
detail = "CedarAuthorizer loads .cedar files at startup from a configurable directory. Updating authorization rules is a file change + restart, not a code change + deploy. This enables security patches to access control without a full release.",
|
|
},
|
|
],
|
|
|
|
consequences = {
|
|
positive = [
|
|
"Authorization rules for workflow stage execution are version-controlled separately from Rust logic",
|
|
"Adding a new stage or role requires only a .cedar policy addition, not a Rust change",
|
|
"CedarAuthorizer fails closed — if no .cedar files are found, startup fails rather than allowing all requests",
|
|
"Policy evaluation is synchronous and sub-millisecond — no async overhead in the authorization hot path",
|
|
],
|
|
negative = [
|
|
"Cedar policy language has a learning curve for engineers unfamiliar with it",
|
|
"Entity/action/resource schema must be kept synchronized between Cedar policies and the Rust types they authorize",
|
|
"The .cedar policy directory path must be configured correctly — misconfiguration causes startup failure",
|
|
],
|
|
},
|
|
|
|
alternatives_considered = [
|
|
{
|
|
option = "Custom RBAC in Rust (match on roles + permissions)",
|
|
why_rejected = "Custom RBAC is not auditable, not formally verified, and grows in complexity as role/permission combinations increase. Authorization bugs in custom code have caused data leaks in several production incidents.",
|
|
},
|
|
{
|
|
option = "Casbin policy engine",
|
|
why_rejected = "Casbin's Rust implementation is less mature than cedar-policy. Cedar has formal verification backing and is used in production at AWS scale. The correctness guarantee is the primary selection criterion.",
|
|
},
|
|
],
|
|
|
|
constraints = [
|
|
{
|
|
id = "cedar-authorizer-in-workflow-engine",
|
|
claim = "vapora-workflow-engine must use CedarAuthorizer for stage execution authorization — no ad-hoc role checks in stage execution code",
|
|
scope = "vapora-workflow-engine/src/",
|
|
severity = 'Hard,
|
|
check = { tag = 'Grep, pattern = "CedarAuthorizer", paths = ["crates/vapora-workflow-engine/src/"], must_be_empty = false },
|
|
rationale = "Ad-hoc role checks inside stage execution logic bypass the audit trail and cannot be updated without recompilation.",
|
|
},
|
|
{
|
|
id = "cedar-policy-files-in-repo",
|
|
claim = "Cedar .cedar policy files must be version-controlled in the project repo",
|
|
scope = "vapora (repo root or crates/vapora-workflow-engine/)",
|
|
severity = 'Soft,
|
|
check = { tag = 'NuCmd, cmd = "let r = (do { glob '**/*.cedar' } | complete); if $r.exit_code != 0 { exit 1 }; let files = ($r.stdout | lines | where { |l| ($l | str trim | is-not-empty) }); if ($files | is-empty) { exit 1 } else { exit 0 }", expect_exit = 0 },
|
|
rationale = "Policy files outside version control cannot be audited, rolled back, or reviewed in PRs. A misconfigured out-of-band policy file is a security incident waiting to happen.",
|
|
},
|
|
],
|
|
|
|
related_adrs = ["adr-002", "adr-004"],
|
|
|
|
ontology_check = {
|
|
decision_string = "cedar-policy for workflow stage authorization in vapora-workflow-engine; CedarAuthorizer loads .cedar files at startup; no ad-hoc role checks in stage code",
|
|
invariants_at_risk = [],
|
|
verdict = 'Safe,
|
|
},
|
|
}
|