ADORSYS-GIS / ADORSYS-GIS/lightbridge-authz

[Story]: Phase 2b — OPA-Wasm policy engine behind the same decision contract

Open
#194 1 comment 0 reactions 0 assignees View on GitHub
governance user-story
Dominant language
Rust
Stars
0
Forks
1
Avg merge
6h 42m
Merged PRs (30d)
246

Description

### Story Statement

As a **policy author holding the restricted `policy-admin` role**,
I want **arbitrary Rego, compiled to OPA-Wasm and embedded in this service, evaluated behind the same decision contract as the rule-data engine**,
so that **I can express cross-entity and aggregate policies the rule-data evaluator genuinely cannot (e.g. "no other project member refilled this period", "approve up to 20% of last period's consumption") without a second, divergent decision path.**

### Real Intent

ADR-0007 chose rule-data first *specifically because* it lets the policy lifecycle (versioning, staging, activation, rollback, simulation, decision logs) get built and proven on the cheap engine before Wasm has to plug into it. This story is that plug-in, not a rewrite: if it requires touching the lifecycle built in #190, the contract in #190 was not stable enough and that is itself a finding worth reporting.

Outline only — filed now so the PR breakdown in the epic's delivery plan has an issue to reference; not scheduled ahead of #189/#190.

### Background and Context

No engine exists yet for either authoring level; #190 ships the rule-data one first. Platform history matters here: OPA was removed from the *gateway* on 2026-06-04 after a missing Secret in an ext_authz step 404'd the whole gateway. This is a different blast radius — an embedded evaluator in a control-plane API, off the inference path, with a last-known-good fallback — and ADR-0007 is explicit that the name should not do the arguing.

### Source of truth (links)

- Epic: #188
- Depends on: #190 (the decision contract must exist and be stable)
- ADR-0007 (docs/adr/0007-refill-decisions-rule-data-then-opa-wasm.md) — the contract, the fail-closed defaults, the follow-ups this story must pick up
- Runbook: docs/runbooks/roll-back-a-budget-policy.md (already written against `activateBudgetPolicy`/`simulateBudgetPolicy`, which this engine must serve identically to the rule-data one)

### Acceptance Criteria

#### Functional
- [ ] Given a `policy-admin`, when they author a Rego bundle, then it builds to Wasm, is signed, and is verified before load.
- [ ] Given a signed bundle, when it is activated, then the swap is atomic and the previously active revision keeps serving until the new one is fully loaded and validated.
- [ ] Given the active revision, when `/health` is queried, then it reports the revision **actually serving**, never the one an operator merely attempted to load.
- [ ] Given a Wasm evaluation, when it produces a decision, then the shape is identical to the rule-data engine's `Decision` — no consumer of the decision can tell which engine produced it.

#### Negative / Edge Cases
- [ ] Given a bundle that fails to compile, load, or verify, when activation is attempted, then the previous known-good revision stays in force and the failure is loud (not silent, not partial).
- [ ] Given an evaluation that exceeds its timeout, when the timeout fires, then the result is `deny`, never automatic approval.
- [ ] Given a Rego built-in the production policy needs, when the bundle is built, then it is verified to actually compile to Wasm before this ships — not every built-in does.

#### Non-Functional
- [ ] `budget:policy-write` (ship executable code into the decision path) and `budget:policy-activate` (make it live) are separate permissions — never the same identity holds both by default.
- [ ] `cargo clippy --all-targets --all-features -- -D warnings` clean.

### Out of Scope

- The rule-data evaluator and policy lifecycle (versioning, simulation, `/health` revision reporting) — #190 builds these; this story lands onto them, and any inability to do so without modifying #190 is itself a finding.
- Automatic augmentation — Phase 5, separate story.

### Dependencies and Blockers

- Depends on #190 (the decision contract and policy lifecycle must exist first).
- Not date-scheduled; the epic's only date-scheduled phase is 6a (ai-helm#877), unrelated to this story.

### Assumptions

1. Not every Rego built-in the production policy wants compiles to Wasm — verify during this phase, not assumed from the Rego source alone.
2. `now` is passed in as evaluation input rather than the bundle calling `time.now_ns()` (ADR-0007's own stated "right instinct").

### Implementation Notes

Guidance, not gospel — see ADR-0007's Neutral/follow-ups section in full: bundle build/sign/verify pipeline, atomic hot-swap, last-known-good fallback, evaluation timeout, active revision on `/health`, and the `policy-write`/`policy-activate` permission split.

### Test Expectations

- A bundle-load failure test proving the previous revision keeps serving and `/health` reflects it.
- An evaluation-timeout test proving `deny`, not approval.
- A test that the Wasm engine's decision output is structurally identical to the rule-data engine's for an equivalent scenario (the contract's real test).

### Verification evidence

*To be completed on delivery.*

### Human accountable owner

@stephane-segning — filed at outline depth on the epic owner's behalf per the #188 backlog comment (2026-08-01); needs their confirmation before entering sprint planning.

### AI Usage Declaration

Drafting this story

### Human verification completed

- [ ] I checked the story against the source of truth
- [ ] I confirmed the acceptance criteria
- [ ] I am the accountable owner and accept responsibility for this story.

> Filed by an AI planning session as part of decomposing epic #188 (see the delivery plan discussion on that issue). None of the boxes above are ticked because the session that filed this is not the accountable owner — @stephane-segning, please review and tick what you've verified.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.