Explainability Schema

Last updated: 2025-12-13. Owner: Policy Guild + Docs Guild.

This document defines the explainability schema addressing gaps EX1-EX10 from the November 2025 product findings. It specifies the canonical format for vulnerability verdict explanations, DSSE signing policy, CAS storage rules, and export/replay formats.


1. Overview

Explainability provides auditable, machine-readable rationale for every vulnerability verdict. Each explanation includes:


2. Gap Resolutions

EX1: Schema/Canonicalization + Hashes

Explanation schema:

{
  "schema": "stellaops.explanation@v1",
  "explanation_id": "explain:sha256:{hex}",
  "finding_id": "P-7:S-42:pkg:maven/log4j@2.14.1:CVE-2021-44228",
  "verdict": {
    "status": "affected",
    "severity": {"normalized": "Critical", "score": 10.0},
    "confidence": 0.92
  },
  "decision_chain": [
    {
      "rule_id": "rule:reachability_gate",
      "rule_version": "1.0.0",
      "inputs": {
        "reachability.state": "CR",
        "reachability.confidence": 0.92
      },
      "output": {"allowed": true, "contribution": 0.4},
      "evidence_refs": ["cas://reachability/graphs/blake3:..."]
    },
    {
      "rule_id": "rule:severity_baseline",
      "rule_version": "1.0.0",
      "inputs": {
        "cvss_base": 10.0,
        "epss_percentile": 0.95
      },
      "output": {"severity": "Critical", "contribution": 0.6},
      "evidence_refs": ["cas://advisories/CVE-2021-44228.json"]
    }
  ],
  "aggregate_confidence": 0.88,
  "created_at": "2025-12-13T10:00:00Z",
  "policy_version": "sha256:...",
  "graph_revision_id": "rev:blake3:..."
}

Canonicalization rules:

  1. JSON keys sorted alphabetically at all levels
  2. Arrays in decision_chain ordered by rule execution sequence
  3. evidence_refs arrays sorted alphabetically
  4. No whitespace, UTF-8 encoding
  5. Hash computed over canonical JSON: sha256(canonical_json)

EX2: DSSE Predicate/Signing Policy

DSSE predicate type:

stella.ops/explanation@v1

Signing policy:

ElementRequiredSigner
Explanation bodyYesPolicy Engine key
Graph DSSE referenceYes (if reachability cited)Scanner key
VEX DSSE referenceYes (if VEX cited)Policy Engine key

DSSE envelope structure:

{
  "payloadType": "application/vnd.stellaops.explanation+json",
  "payload": "<base64(canonical_explanation_json)>",
  "signatures": [
    {
      "keyid": "policy-engine-signing-2025",
      "sig": "base64:..."
    }
  ]
}

Signing requirements:

EX3: CAS Storage Rules for Evidence

Storage layout:

cas://explanations/
  {sha256}/                      # Explanation body
  {sha256}.dsse                  # DSSE envelope
  by-finding/{finding_id}/       # Index by finding
  by-policy/{policy_digest}/     # Index by policy version
  by-graph/{graph_revision_id}/  # Index by graph revision

Storage rules:

  1. Explanations are immutable after signing
  2. New verdicts create new explanation documents (no updates)
  3. Previous explanations are retained per retention policy
  4. Cross-references validated at write time (graphs, VEX must exist)

Deduplication:

Required links:

{
  "links": {
    "policy_version": "sha256:7e1d...",
    "policy_uri": "cas://policy/versions/sha256:7e1d...",
    "graph_revision_id": "rev:blake3:a1b2...",
    "graph_uri": "cas://reachability/revisions/blake3:a1b2...",
    "sbom_digest": "sha256:def4...",
    "sbom_uri": "cas://scanner-artifacts/sbom.cdx.json",
    "vex_digest": "sha256:e5f6...",
    "vex_uri": "cas://excititor/vex/openvex.json"
  }
}

Validation:

EX5: Export/Replay Bundle Format

Export bundle manifest:

{
  "schema": "stellaops.explanation.bundle@v1",
  "bundle_id": "bundle:explain:2025-12-13",
  "created_at": "2025-12-13T10:00:00Z",
  "explanations": [
    {
      "explanation_id": "explain:sha256:...",
      "finding_id": "...",
      "explanation_uri": "explanations/sha256:....json",
      "dsse_uri": "explanations/sha256:....dsse"
    }
  ],
  "dependencies": {
    "graphs": [
      {"revision_id": "rev:blake3:...", "uri": "graphs/blake3:....json"}
    ],
    "policies": [
      {"digest": "sha256:...", "uri": "policies/sha256:....json"}
    ],
    "vex_statements": [
      {"digest": "sha256:...", "uri": "vex/sha256:....json"}
    ]
  },
  "verification": {
    "bundle_hash": "sha256:...",
    "signature": "base64:...",
    "signed_by": "policy-engine-signing-2025"
  }
}

Replay verification:

stella explain verify --bundle ./explanation-bundle.tgz

# Output:
Bundle: bundle:explain:2025-12-13
Explanations: 42
Dependencies: 5 graphs, 2 policies, 12 VEX

Verifying explanations...
  Canonical hashes: 42/42 MATCH
  DSSE signatures: 42/42 VALID
  Dependency links: 42/42 RESOLVED

Replay verification PASSED.

EX6: PII/Redaction Rules

Redaction categories:

CategoryRedactionExample
User identifiersHashuser:alice -> user:sha256:a1b2...
IP addressesMask192.168.1.100 -> 192.168.x.x
File pathsNormalize/home/alice/code/... -> {HOME}/code/...
Email addressesHashalice@example.com -> email:sha256:...
API keys/tokensOmitAuthorization: Bearer xxx -> [REDACTED]

Redaction metadata:

{
  "redaction": {
    "applied": true,
    "level": "standard",
    "fields_redacted": ["actor.email", "evidence.file_path"],
    "redaction_policy": "stellaops.redaction.standard@v1"
  }
}

Export modes:

EX7: Size Budgets

Limits:

ElementDefault LimitConfigurable
Explanation body256 KBYes
Decision chain entries100Yes
Evidence refs per rule20Yes
Total evidence refs200Yes
Path entries50No

Truncation behavior:

When limits are exceeded:

  1. Log warning with truncation details
  2. Add truncation metadata to explanation
  3. Store full evidence in separate CAS object
  4. Include full_evidence_uri reference
{
  "truncation": {
    "applied": true,
    "elements_truncated": ["decision_chain", "evidence_refs"],
    "full_evidence_uri": "cas://explanations/full/sha256:..."
  }
}

EX8: Versioning

Schema versioning:

Migration support:

stella explain migrate --from v1 --to v2 --input ./explanations/

# Output:
Migrating 1000 explanations from v1 to v2...
  Migrated: 998
  Skipped (already v2): 2

Migration complete.

Version compatibility matrix:

API VersionSchema v1Schema v2
1.0.xFullN/A
1.1.xFullFull
2.0.xRead-onlyFull

EX9: Golden Fixtures/Tests

Test fixture location:

tests/Explanation/
  fixtures/
    simple-affected.json
    simple-not-affected.json
    with-reachability-evidence.json
    multi-rule-chain.json
    truncated-evidence.json
    redacted-pii.json
  golden/
    simple-affected.golden.json
    simple-affected.golden.dsse

datasets/explanations/
  schema/
    explanation.schema.json
  samples/
    log4j-affected/
      explanation.json
      expected-hash.txt

Test categories:

  1. Canonicalization tests: Verify hash stability across JSON reordering
  2. DSSE signing tests: Verify signature creation and verification
  3. Redaction tests: Verify PII handling
  4. Truncation tests: Verify size budget enforcement
  5. Replay tests: Verify bundle export/import cycle
  6. Migration tests: Verify version upgrade paths

CI integration:

# .gitea/workflows/explanation-tests.yml
explanation-tests:
  runs-on: ubuntu-latest
  steps:
    - name: Run explanation tests
      run: dotnet test src/Policy/__Tests/StellaOps.Policy.Explanation.Tests
    - name: Verify golden fixtures
      run: scripts/verify-golden-fixtures.sh tests/Explanation/golden/

EX10: Determinism Guarantees

Determinism requirements:

  1. Same inputs produce identical explanation_id hash
  2. Decision chain ordering is stable (execution order)
  3. Evidence refs sorted alphabetically
  4. Timestamps use UTC ISO-8601 with millisecond precision
  5. Floating-point values rounded to 6 decimal places

Verification:

# Run twice with same inputs, verify identical hashes
stella explain generate --finding "..." --output a.json
stella explain generate --finding "..." --output b.json
diff a.json b.json  # Should be empty

# Or use built-in verify
stella explain verify-determinism --finding "..." --iterations 3

3. API Reference

3.1 Generate Explanation

POST /api/policy/findings/{findingId}/explain
Authorization: Bearer <token>
Content-Type: application/json

{
  "mode": "full",
  "include_evidence": true,
  "redaction_level": "standard"
}

3.2 Get Explanation

GET /api/explanations/{explanationId}
Authorization: Bearer <token>
Accept: application/json

3.3 Export Explanation Bundle

POST /api/explanations/export
Authorization: Bearer <token>
Content-Type: application/json

{
  "finding_ids": ["...", "..."],
  "include_dependencies": true,
  "redaction_level": "standard"
}

3.4 Verify Explanation

POST /api/explanations/{explanationId}/verify
Authorization: Bearer <token>

4. CLI Reference

# Generate explanation for a finding
stella explain generate --finding "P-7:S-42:pkg:maven/log4j@2.14.1:CVE-2021-44228"

# Export explanation bundle
stella explain export --findings ./finding-ids.txt --output ./bundle.tgz

# Verify explanation
stella explain verify --explanation ./explanation.json --dsse ./explanation.dsse

# Verify bundle
stella explain verify --bundle ./bundle.tgz

# Check determinism
stella explain verify-determinism --finding "..." --iterations 5


Last updated: 2025-12-13. See Sprint 0401 EXPLAIN-GAPS-401-064 for change history.