Thoryn

Policy rules · Policy rules

Revocation — active and fresh

ALLOW only if the credential is active AND its `valid_until` is in the future. Defends against credentials that aren't revoked yet but are stale.

Tested against:policyEngine: 1.0.0

policy-engine recipe — shared category architecture: how this pattern composes with Hub, Broker, and the rest of the catalog

Use case

Broker already runs the status-list revocation check (10-step trust-chain validation). This rule complements that with a freshness gate — the credential's own valid_until must be in the future. Some credentials carry expiry but no revocation channel; this catches those.

Rule

{
  "all": [
    { "fact": "credential_status", "operator": "equal", "value": "active" },
    { "fact": "valid_until_epoch", "operator": "greaterThan", "fact_or_value": "now_epoch" }
  ]
}

The fact_or_value operator references another fact rather than a literal — the host service supplies now_epoch so the rule stays deterministic.

Facts shape

data class FreshnessFacts(
  val credential_status: String,        // "active" | "revoked" | "suspended"
  val valid_until_epoch: Long,          // from the credential
  val now_epoch: Long,                  // from the host: Instant.now().epochSecond
)

Trace — DENY (expired)

{
  "decision": "DENY",
  "reason": "credential expired",
  "trace": [
    { "all": [
        { "fact": "credential_status", "operator": "equal", "value": "active", "actual": "active", "result": "pass" },
        { "fact": "valid_until_epoch", "operator": "greaterThan", "compare_to_fact": "now_epoch", "actual": 1715000000, "compare": 1735000000, "result": "fail" }
      ],
      "result": "fail"
    }
  ]
}

When to use

  • Credentials with intrinsic expiry (exp claim, valid_until)
  • Sector-specific freshness windows — a healthcare licence checked monthly even if the status list says "active"

When not to use

  • Credentials without an intrinsic expiry — relies on Broker's status-list check alone

See also