Skip to content

Conversation

@beonde
Copy link
Member

@beonde beonde commented Jan 18, 2026

Summary

Aligns SDK with RFC-002 §5 Trust Levels and adds missing BadgeClaims fields.

Changes

TrustLevel Enum

  • Add LEVEL_0 (Self-Signed / SS) per RFC-002 §5
  • Add LEVEL_4 (Extended Validated / EV) per RFC-002 §5
  • Update comments to use RFC-002 canonical names

BadgeClaims Dataclass

  • Add ial field for Identity Assurance Level (default "0")
  • Add raw_claims for advanced access to full JWT payload
  • Add has_key_binding property for IAL-1 detection per RFC-002 §7.2.1
  • Add confirmation_key property for cnf claim access
  • Fix audience string to list conversion

FastAPI Middleware

  • Implement exclude_paths parameter (was documented but missing)

Tests

  • Add tests for all 5 trust levels (0-4)
  • Add tests for IAL parsing and raw_claims preservation
  • Add tests for exclude_paths feature

Testing

pytest tests/unit/test_badge.py -v  # 28 tests pass
pytest tests/unit/test_fastapi_integration.py -v  # 5 tests pass

Related

  • RFC-002 §5: Trust Levels
  • RFC-002 §7.2.1: IAL-0 vs IAL-1

- Add LEVEL_0 (SS) and LEVEL_4 (EV) to TrustLevel enum per RFC-002 §5
- Update TrustLevel comments to use RFC-002 canonical names
- Add 'ial' field to BadgeClaims for Identity Assurance Level
- Add 'raw_claims' for advanced access to full JWT payload
- Add 'has_key_binding' property for IAL-1 detection
- Add 'confirmation_key' property for cnf claim access
- Fix audience string to list conversion
- Implement 'exclude_paths' parameter in FastAPI middleware
- Add tests for all new features
Copilot AI review requested due to automatic review settings January 18, 2026 15:06
@github-actions
Copy link

✅ Documentation validation passed!

Unified docs will be deployed from capiscio-docs repo.

@github-actions
Copy link

✅ All checks passed! Ready for review.

@codecov
Copy link

codecov bot commented Jan 18, 2026

Codecov Report

❌ Patch coverage is 93.33333% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
capiscio_sdk/badge.py 92.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aligns the Python SDK with RFC-002 §5 Trust Levels by adding missing trust levels (LEVEL_0 and LEVEL_4), implementing Identity Assurance Level (IAL) support, and adding the previously documented but missing exclude_paths parameter to FastAPI middleware.

Changes:

  • Adds LEVEL_0 (Self-Signed) and LEVEL_4 (Extended Validated) to TrustLevel enum with RFC-002 canonical names
  • Adds ial field, raw_claims preservation, has_key_binding property, and confirmation_key property to BadgeClaims dataclass
  • Implements exclude_paths parameter in FastAPI middleware for skipping verification on public endpoints
  • Adds comprehensive test coverage for all 5 trust levels (0-4), IAL parsing, and exclude_paths functionality

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
capiscio_sdk/badge.py Adds LEVEL_0 and LEVEL_4 to TrustLevel enum; adds ial, raw_claims fields and has_key_binding/confirmation_key properties to BadgeClaims; improves error messages and documentation
capiscio_sdk/integrations/fastapi.py Implements exclude_paths parameter to allow skipping verification for specific paths like /health
tests/unit/test_badge.py Adds tests for all 5 trust levels, IAL claim parsing, raw_claims preservation, and audience string-to-list conversion
tests/unit/test_fastapi_integration.py Adds test coverage for exclude_paths middleware feature

audience=data.get("aud", []),
audience=aud,
ial=data.get("ial", "0"), # RFC-002 §7.2.1
raw_claims=data, # Preserve for advanced access (cnf, key, etc.)
Copy link

Copilot AI Jan 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Storing the original data dictionary directly as raw_claims creates a reference to the input dictionary, which could lead to unintended mutations. If the caller modifies the original dictionary after creating BadgeClaims, the raw_claims will also be modified. Consider creating a shallow copy of the data dictionary instead.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link

✅ Integration tests passed! Server validation, BadgeKeeper, and gRPC tests all working.

@github-actions
Copy link

✅ Documentation validation passed!

Unified docs will be deployed from capiscio-docs repo.

@github-actions
Copy link

✅ All checks passed! Ready for review.

@github-actions
Copy link

✅ Integration tests passed! Server validation, BadgeKeeper, and gRPC tests all working.

Copilot AI review requested due to automatic review settings January 19, 2026 01:31
@github-actions
Copy link

✅ Documentation validation passed!

Unified docs will be deployed from capiscio-docs repo.

@github-actions
Copy link

✅ All checks passed! Ready for review.

@github-actions
Copy link

✅ Integration tests passed! Server validation, BadgeKeeper, and gRPC tests all working.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

that cryptographically binds the badge to the agent's private key.
"""
if self.raw_claims is None:
return self.ial == "1"
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The has_key_binding property has a logical issue. When raw_claims is None, it returns True if ial == "1", but this is misleading. If raw_claims is None, we cannot verify the presence of the cnf claim, so we shouldn't assume it exists based solely on the IAL value. The property should either:

  1. Return False when raw_claims is None (safer default)
  2. Raise an exception indicating that verification cannot be performed
    The current implementation could lead to false positives where a badge claims IAL-1 but doesn't actually have the cnf claim.
Suggested change
return self.ial == "1"
# Without raw_claims we cannot verify the presence of the cnf claim,
# so conservatively report no key binding instead of inferring from IAL.
return False

Copilot uses AI. Check for mistakes.
audience=data.get("aud", []),
audience=aud,
ial=data.get("ial", "0"), # RFC-002 §7.2.1
raw_claims=data, # Preserve for advanced access (cnf, key, etc.)
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The raw_claims field stores a reference to the input dictionary, which could lead to unintended mutations. If the caller modifies the dictionary passed to from_dict after creating the BadgeClaims object, those changes will be reflected in raw_claims. Consider storing a shallow copy instead: raw_claims=dict(data) or raw_claims=data.copy() to prevent external modifications.

Suggested change
raw_claims=data, # Preserve for advanced access (cnf, key, etc.)
raw_claims=dict(data), # Preserve for advanced access (cnf, key, etc.) with a snapshot copy

Copilot uses AI. Check for mistakes.
Comment on lines +180 to +212
def test_ial_claim_parsing(self):
"""Test IAL claim is correctly parsed from dict (RFC-002 §7.2.1)."""
# IAL-0 badge (account-attested, no key proof)
data_ial0 = {
"jti": "badge-ial0",
"iss": "https://registry.capisc.io",
"sub": "did:web:registry.capisc.io:agents:agent1",
"iat": 1704067200,
"exp": 1735689600,
"trust_level": "1",
"domain": "example.com",
"ial": "0",
}
claims_ial0 = BadgeClaims.from_dict(data_ial0)
assert claims_ial0.ial == "0"
assert not claims_ial0.has_key_binding

# IAL-1 badge (proof of possession, with cnf claim)
data_ial1 = {
"jti": "badge-ial1",
"iss": "https://registry.capisc.io",
"sub": "did:web:registry.capisc.io:agents:agent2",
"iat": 1704067200,
"exp": 1735689600,
"trust_level": "1",
"domain": "example.com",
"ial": "1",
"cnf": {"jkt": "sha256-thumbprint-of-key"},
}
claims_ial1 = BadgeClaims.from_dict(data_ial1)
assert claims_ial1.ial == "1"
assert claims_ial1.has_key_binding
assert claims_ial1.confirmation_key == {"jkt": "sha256-thumbprint-of-key"}
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding test coverage for the has_key_binding property when raw_claims is None. Currently, all tests use from_dict which always sets raw_claims. A test creating BadgeClaims directly (with raw_claims=None) and verifying has_key_binding behavior would catch the edge case identified in another comment.

Copilot uses AI. Check for mistakes.

### Added
- **MCP Service Client**: RFC-006/RFC-007 operations via MCP protocol
- **MCP gRPC Client**: Server identity operations
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CHANGELOG is missing an entry for the exclude_paths feature added to FastAPI middleware. This was mentioned in the PR description as one of the key changes ("Implement exclude_paths parameter") and should be documented in the "Added" section of the changelog.

Suggested change
- **MCP gRPC Client**: Server identity operations
- **MCP gRPC Client**: Server identity operations
- **FastAPI SimpleGuard middleware**: Added `exclude_paths` parameter to skip badge verification for selected routes (e.g., health checks, metrics)

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +18
- **MCP Service Client**: RFC-006/RFC-007 operations via MCP protocol
- **MCP gRPC Client**: Server identity operations
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CHANGELOG mentions "MCP Service Client" and "MCP gRPC Client" additions in the "Added" section, but these features don't appear to be part of this PR based on the file changes. The PR description indicates this is specifically about "RFC-002 alignment for TrustLevel and BadgeClaims" plus the exclude_paths feature for FastAPI middleware. Either these MCP-related items should be removed from this version's changelog, or they're being added in a different PR and this needs clarification.

Suggested change
- **MCP Service Client**: RFC-006/RFC-007 operations via MCP protocol
- **MCP gRPC Client**: Server identity operations
- **FastAPI SimpleGuard middleware**: Added `exclude_paths` support to bypass badge verification for selected routes (for example `/health`).

Copilot uses AI. Check for mistakes.
@beonde beonde merged commit c3913a7 into main Jan 19, 2026
19 checks passed
@beonde beonde deleted the fix/rfc002-alignment branch January 19, 2026 01:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants