JanssenProject / JanssenProject/jans

feat(jans-cedarling): Cedarling Core TRACE Support Design

Open
#15,006 0 comments 0 reactions 2 assignees Claimed by @olehbozhok View on GitHub
comp-jans-cedarling enhancement kind-feature
Dominant language
Java
Stars
648
Forks
174
Avg merge
1d 18h
Merged PRs (30d)
110

Description

## 1. Summary

This design adds a minimal TRACE-support surface to Cedarling Core. The core remains a fast, deterministic, embeddable policy decision point. It does not manage `trace_execution_id` lifecycles, collect tool events, contact external services, renew attestation, queue evidence, or construct complete TRACE Trust Records.

The core produces a stable `DecisionEvidence` object for each authorization and provides reusable validation and hashing primitives needed by the sidecar. Stateful TRACE production belongs to the Cedarling sidecar; correlation, final record construction, storage, and transparency anchoring belong to Jans Lock Server.

## 2. Design principles

- Preserve Cedarling's embedded, offline, and WASM-compatible architecture.
- Keep authorization latency independent of evidence export.
- Produce deterministic evidence from inputs already used in the decision.
- Do not introduce network, filesystem, background-task, or database dependencies.
- Do not make TRACE a prerequisite for normal Cedarling authorization.
- Expose the same evidence structure through Rust, Python, Java/Kotlin, Go, and WASM bindings where practical.
- Version the evidence contract independently from TRACE producer profiles.

## 3. Responsibilities

Cedarling Core will:

- Generate a unique `decision_id` for each authorization.
- Return the allow/deny decision and structured diagnostics.
- Identify the exact loaded policy store and its digest.
- Identify evaluated principal, action, and resource entities.
- Return safe token identifiers such as issuer, type, subject, and `jti`.
- Record the authorization timestamp and engine version.
- Produce a deterministic authorization-request commitment.
- Validate server-supplied attestation context against the policy schema.
- Optionally evaluate an Attestation Token through the existing token-processing pipeline.

## 4. Non-responsibilities

Cedarling Core will not:

- Generate or manage `trace_execution_id`.
- Define when an agent execution begins or ends.
- Collect MCP, A2A, model, or tool-response events.
- Obtain hardware evidence or contact ATS.
- Maintain an Attestation Token cache.
- Store ordered execution transcripts.
- Aggregate data classifications across calls.
- Sign TRACE records.
- Send evidence to Lock Server.
- Resolve SLSA, RIM, SCITT, or external artifact URLs.

## 5. Component boundary

```mermaid
flowchart TD
CALLER["Binding or sidecar"] -->|authorization input| CORE["Cedarling Core"]
CORE -->|decision + DecisionEvidence| CALLER
CALLER -->|execution lifecycle and events| TRACE["Sidecar TRACE module"]
TRACE -->|evidence envelope| LOCK["Lock Server"]
```

The caller supplies `trace_execution_id` and `call_id` as opaque correlation attributes when available. Core returns them unchanged but does not interpret, generate, or retain them.

## 6. DecisionEvidence contract

```rust
pub struct DecisionEvidence {
pub evidence_version: String,
pub decision_id: String,
pub decision: Decision,
pub timestamp: i64,
pub engine: EngineDescriptor,
pub policy: PolicyEvidence,
pub request: RequestEvidence,
pub tokens: Vec,
pub diagnostics: EvidenceDiagnostics,
pub correlation: CorrelationEvidence,
}

pub struct CorrelationEvidence {
pub trace_execution_id: Option,
pub call_id: Option,
pub transaction_id: Option,
}
```

This is a Jans evidence contract, not a TRACE Trust Record. The sidecar combines many `DecisionEvidence` objects with runtime and execution events.

### 6.1 PolicyEvidence

```rust
pub struct PolicyEvidence {
pub policy_store_id: String,
pub bundle_hash: String,
pub version: Option,
pub source_uri: Option,
pub enforcement_mode: EnforcementMode,
pub matched_policy_ids: Vec,
}
```

`bundle_hash` is calculated from the exact policy artifact loaded into the engine. It must not be reconstructed from parsed policies. A new engine instance with a new bundle produces a new digest.

### 6.2 RequestEvidence

```rust
pub struct RequestEvidence {
pub principal_ids: Vec,
pub action_id: EntityUid,
pub resource_id: EntityUid,
pub canonical_request_hash: String,
}
```

The hash commits to the normalized authorization inputs without exposing raw token values or sensitive context. The canonical preimage and excluded fields must be specified and versioned.

### 6.3 TokenEvidence

```rust
pub struct TokenEvidence {
pub issuer: String,
pub token_type: String,
pub subject: Option,
pub jti: Option,
pub cnf_jkt: Option,
pub audience_hash: Option,
pub validation_status: TokenValidationStatus,
}
```

Raw JWTs and claims not explicitly approved for evidence output are excluded.

### 6.4 Diagnostics

Diagnostics include matched permit and forbid policy IDs, validation failures, and deterministic reason codes. They must not contain tokens, secrets, or uncontrolled claim values.

## 7. Authorization API changes

Existing authorization methods retain their current behavior. New or extended methods return evidence:

```rust
pub fn authorize_with_evidence(
&self,
request: AuthorizeRequest,
correlation: Option,
) -> Result;
```

```rust
pub struct AuthorizeResultWithEvidence {
pub decision: bool,
pub diagnostics: Diagnostics,
pub evidence: DecisionEvidence,
}
```

Bindings may expose this through an opt-in flag so existing response shapes remain compatible.

## 8. Identifier semantics

| Identifier | Owner | Core behavior |
| --- | --- | --- |
| `trace_execution_id` | Application/gateway or sidecar fallback | Accept and echo; never generate or retain. |
| `decision_id` | Cedarling Core | Generate for each evaluation. |
| `call_id` | Application or gateway | Accept and echo. |
| Auth Server `sid` | Jans Auth Server | Treat only as optional token metadata. |
| Token `jti` | Token issuer | Extract from validated tokens. |
| `transaction_id` | Business/OAuth transaction owner | Accept and echo. |

`trace_execution_id` identifies a governed agent execution. It must never be inferred from an Auth Server login session or token identifier.

## 9. Policy artifact digest

Policy loading returns an immutable descriptor:

```rust
pub struct LoadedPolicyStoreDescriptor {
pub policy_store_id: String,
pub bundle_hash: String,
pub version: Option,
pub source_uri: Option,
pub loaded_at: i64,
}
```

Requirements:

- SHA-256 minimum; SHA-384 may be configured.
- Hash exact CJAR or canonical policy-bundle bytes before parsing.
- Associate the descriptor immutably with the engine instance.
- Never let caller context override the digest or enforcement mode.
- Expose a stable descriptor through bindings.

## 10. Attestation input

Core may receive normalized, server-controlled attestation context from the sidecar or validate an Attestation Token using the token-processing pipeline.

The preferred MVP boundary is:

1. Sidecar validates the ATS token and its relationship to JWT-SVID.
2. Sidecar creates a typed attestation context.
3. Core validates it against the policy schema.
4. Core evaluates Cedar policy.
5. `DecisionEvidence` records only the attestation token `jti`, issuer, subject, `cnf.jkt`, and validation status.

Caller-supplied request context must not be able to impersonate the reserved attestation namespace.

## 11. Canonical request commitment

Core provides a deterministic commitment to the authorization inputs:

```text
canonical_request_hash = SHA-256(
JCS(versioned_normalized_authorization_request)
)
```

Normalization must specify:

- Entity ordering.
- Set ordering.
- Token references rather than token bytes.
- Treatment of absent and null values.
- Numeric bounds.
- Excluded volatile fields.
- Schema/version identifier.

This commitment links the Cedar decision to the sidecar's execution-event record.

## 12. Serialization

`DecisionEvidence` supports deterministic JSON serialization for bindings and telemetry. Serialization must:

- Use stable field names.
- Reject unknown enum values during deserialization.
- Preserve JavaScript-safe integer ranges.
- Avoid map-order dependence.
- Carry `evidence_version`.
- Offer RFC 8785 canonical bytes as a separate explicit operation.

Core does not sign the serialized object.

## 13. Performance

- Evidence generation is opt-in.
- Target additional median latency: less than 10 microseconds excluding token validation already required for authorization.
- Hashing must operate on normalized references, not entire token bodies.
- No allocation of full diagnostic detail unless requested.
- No synchronous I/O.
- Benchmarks cover evidence disabled, minimal evidence, and full diagnostics.

## 14. Security requirements

- Never emit raw tokens or secrets.
- Generate unpredictable `decision_id` values or use a collision-resistant monotonic scheme scoped to the process.
- Do not trust caller-provided policy or attestation fields.
- Make reserved context namespaces unforgeable by external request input.
- Bind evidence to the exact evaluated request and engine instance.
- Return explicit validation state; absence is not success.
- Avoid ad hoc JSON canonicalization.
- Keep evidence output consistent across language bindings.

## 15. Observability

Core exposes evidence-generation counters through its existing telemetry hooks:

- Evidence enabled/disabled.
- Evidence generation failures.
- Canonicalization failures.
- Policy artifact digest.
- `decision_id`, `trace_execution_id`, and `call_id` correlation.

Core emits telemetry events through callbacks; it does not export them over a network.

## 16. Testing

- Stable evidence serialization across Rust versions and bindings.
- Golden vectors for request normalization and hashing.
- Policy digest changes when any artifact byte changes.
- `trace_execution_id` is echoed without being interpreted.
- Raw token values never appear in evidence.
- Reserved attestation context cannot be overridden.
- Multi-issuer token evidence remains deterministic.
- Evidence-enabled and legacy decisions are identical.
- Performance regression benchmarks.

## 17. Acceptance criteria

- Existing APIs and bindings remain backward compatible.
- `authorize_with_evidence` returns a stable `DecisionEvidence` object.
- The exact active policy artifact is identified by digest.
- Decisions are bound to deterministic request commitments.
- Safe token identifiers support Lock correlation.
- Core performs no network, filesystem, queue, execution-lifecycle, or signing work.
- Sidecar can construct its TRACE evidence envelope without parsing Cedarling logs.

## 18. Key design decision

Cedarling Core produces deterministic authorization evidence, not TRACE records. This keeps the PDP portable and fast while giving the sidecar a stable, structured source for workload-local TRACE evidence.

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.