OpenThymos RFC

OpenThymos RFC

Title

Declarative Policy Language v1

Status

Accepted — MVP implemented (Option A: minimal JSON predicate DSL). Shipped in thymos-policy::json_policy:

Also shipped:

Deferred (see Unresolved Questions): CEL/Rego (Options B/C) only if the closed DSL proves too limited.

Summary

Policies in OpenThymos are currently compiled Rust types implementing the thymos_policy::Policy trait (e.g. WritAuthorityPolicy, TenantIsolationPolicy, ThresholdApprovalPolicy). They are expressive and fast, but they cannot be authored, reviewed, versioned, signed, or changed without rebuilding and redeploying the runtime binary. This RFC explores a declarative, signed, versioned policy language that can be loaded at runtime while preserving the determinism guarantees that replay depends on.

This RFC builds on the provenance hook already in place: PolicyEngine::policy_set_hash() is recorded in every commit (CommitBody::policy_set_hash) and can be pinned by ReplayConfig::require_policy_set_hash. A declarative language would make that hash the digest of an explicit, signable artifact rather than of an in-binary rule list.

Motivation

Non-Goals

Options

Option A — Minimal JSON predicate DSL (custom). A small, closed grammar: boolean combinators over typed accessors into Intent/Writ/World, comparison operators, and a fixed set of decisions (permit / deny{reason} / require_approval{channel,reason}).

Option B — CEL (Common Expression Language), deterministic subset. Adopt CEL with a vetted profile (no timestamp(now), no extensions, integer/ decimal-as-fixed-point only).

Option C — Rego / OPA.

Option D — Keep Rust policies, add signed bundles. Don’t add a language; instead formalize a registry of named Rust policies and sign the selection + parameters (e.g. ThresholdApprovalPolicy params) as a bundle, hashed by the existing policy_set_hash.

Recommendation (for review)

Start with Option A (a minimal, closed JSON predicate DSL) for new logic, while keeping Option D’s signing/bundling model for both Rust and DSL policies:

  1. A policy bundle is a signed artifact: { version, rules[], issuer_pubkey, signature }, where signature is ed25519 over canonical_json(bundle without signature) — mirroring Writ and the new signed-commit scheme.
  2. PolicyEngine gains a loader that validates the bundle signature, compiles the DSL to an internal evaluator, and exposes the existing policy_set_hash() as the bundle digest.
  3. Each commit already records policy_set_hash; extend the record to also carry the bundle version and issuer for human-readable provenance.
  4. Evaluation MUST be pure and statically bounded; the loader rejects any rule referencing time, randomness, floats, or external data (fail-closed).

Defer CEL/Rego (Options B/C) unless the DSL proves too limited; revisit by follow-up RFC.

Determinism & Replay Constraints (binding on any option)

Security Considerations

Unresolved Questions

References