Repository navigation
v0.1.0
Pre-releaseFirst release. The engine was extracted from another project rather than written here, so this entry records what moved, what changed on the way, and what the public surface now is.
Added
-
The policy engine, ported from
contextforge-org/cpexwith history intact. Extracted withgit-filter-repoat source commitaed0f15, 192 files across 37 filtered commits, sogit logandgit blamereach back before this repository existed. The Rego decision point came in a second pass fromfa222c4.docs/port-provenance.mdrecords both anchors, which is what any later comparison between the two trees needs. -
praxis-policy, a host facade. One dependency instead of a dozen. It re-exports the runtime (PolicyEngine,AplOptions,register_apl) and owns registration of the bundled extensions, each behind its own feature.defaultis empty, so the bare dependency is the engine alone with nothing extra compiled in;builtinsturns on the whole set, or name a subset (jwt,oauth,elicitation-ciba,cedar,cel,opa,valkey). -
Three decision points, selectable per route. Cedar policy sets (
cedar:), inline CEL expressions (cel:), and embedded OPA/Rego via regorus (opa:). One binary serves all three; a route picks one with a step. -
Bundled extensions: multi-source JWT identity, RFC 8693 OAuth token delegation, out-of-band human approval over OIDC CIBA, and a Valkey-backed session store for taint that survives a restart.
-
Sensitive headers never reach a decision point.
Authorization,CookieandX-API-Keyare stripped from the projection a PDP receives, matched case-insensitively because headers arrive in whatever case the client sent. For a remote PDP that projection crosses the network, so this is the difference between consulting a policy service and handing it a bearer token. -
A documented path for plugins the engine does not bundle. Implement
PluginFactoryagainstpraxis_policy_core::preludeand register it withPolicyEngine::register_factoryunder thekind:your policy names. An unrecognisedkindfails at load, so a missing registration is a startup error naming the kind rather than a plugin that silently never runs. The prelude's doc example is compiled, notignored, so it cannot drift from what a plugin actually needs.
Changed
-
Renamed to the Praxis Policy Engine throughout, crates, types and docs. Deliberately unchanged: the policy document format, the
kind:strings an operator writes, and the violation codes a client sees. Those are the surface a deployment depends on, andcrates/ppe-core/tests/wire_compatibility.rspins them against a document authored before the rename. -
Edition 2024 and resolver 3, with the MSRV pinned to the same toolchain the formatter and coverage run on, so there is one Rust version to reason about.
-
Six core crates instead of eight.
praxis-policy-sdkbecamepraxis_policy_core::prelude: every name in it was already re-exported from core, so the separate crate offered a curated namespace and no dependency saving, which a module provides without a second crate to version.praxis-policy-builtinsfolded into the facade, because the feature list, the factory re-exports and the registration table all describe one set and can disagree when split across two crates. -
The PII scanner and audit logger are no longer published or bundled. They live under
reference/plugins/as worked examples a host registers itself. The scanner is regex matching with no Luhn check, and the logger writes to stderr; neither is something a policy engine should ship as supported, and both are what a deployment will want to replace. This is breaking for anyone who hadfeatures = ["pii"]or["audit"], or who namedPiiScannerFactory/AuditLoggerFactory: register the factory instead.
Fixed
-
A float in a Cedar attribute source denied every request through that step. Cedar's value model has no floating-point type, so
attributes: { score: 1.5 }failed entity construction with a message that named neither the attribute nor the reason. It now reports which key holds the float and what to supply instead. The same walker covers the operator-authored resource block. -
A quoted argument containing a lone quote aborted the policy parser instead of being read as a literal.
-
Branch outcomes are keyed rather than positional, so a concurrent effect's result can no longer be attributed to the wrong branch.
Security
-
nbfis now enforced on inbound JWTs.validate_nbfis off by default in jsonwebtoken, unlikevalidate_exp, and nothing turned it on. The module documentedauth.token_not_yet_validas a stable code and mappedImmatureSignatureto it, but that error could never be produced, so a token whose own issuer said it was not valid until later was accepted the moment it was minted. Enforced under the same leeway that already coversexp, so ordinary clock skew is still tolerated. A deployment whose IdP deliberately mints a futurenbfwill start seeing that code. -
An issuer accepting no signature algorithms now rejects every token from it, rather than treating the empty list as "any algorithm is acceptable" and handing algorithm choice to whoever minted the token.