// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import "./IVaultfireIdentity.sol"; /// @title ComplianceRegistry — EU AI Act on-chain compliance attestation for AI agents /// @author Vaultfire Protocol /// @notice Stores and verifies compliance attestations for AI agents per the EU AI Act. /// Maps to EU AI Act requirements: identity, audit trail, risk classification, /// human oversight, and post-market monitoring. Full enforcement August 2026. /// @dev Each agent can have multiple compliance attestations from authorized auditors. contract ComplianceRegistry { // ═══════════════════════════════════════════════════ // ENUMS & STRUCTS // ═══════════════════════════════════════════════════ enum RiskLevel { Minimal, Limited, High, Unacceptable } enum ComplianceStatus { Pending, Compliant, NonCompliant, Expired, Revoked } struct ComplianceAttestation { address agent; // Agent being attested address auditor; // Authorized auditor who issued attestation RiskLevel riskLevel; // EU AI Act risk classification ComplianceStatus status; // Current compliance status uint256 issuedAt; // Timestamp when attestation was issued uint256 expiresAt; // Timestamp when attestation expires uint256 revokedAt; // Timestamp when revoked (0 if not revoked) string attestationURI; // IPFS/URI to full compliance documentation string framework; // e.g., "EU_AI_ACT_2026", "NIST_AI_RMF" bool humanOversightVerified; // Whether human oversight mechanism is confirmed bool technicalDocVerified; // Whether technical documentation exists bool postMarketMonitoring; // Whether post-market monitoring is active bool incidentReporting; // Whether incident reporting is configured } struct AuditorInfo { bool authorized; uint256 authorizedAt; uint256 attestationsIssued; string auditorURI; // IPFS/URI to auditor credentials } // ═══════════════════════════════════════════════════ // STATE // ═══════════════════════════════════════════════════ IVaultfireIdentity public immutable identityRegistry; address public owner; uint256 public totalAttestations; uint256 public totalAuditors; uint256 public totalCompliantAgents; uint256 public constant MIN_ATTESTATION_DURATION = 30 days; uint256 public constant MAX_ATTESTATION_DURATION = 730 days; // 2 years mapping(uint256 => ComplianceAttestation) public attestations; mapping(address => uint256[]) public agentAttestations; mapping(address => AuditorInfo) public auditors; mapping(address => bool) public isCompliant; // Quick lookup: does agent have valid attestation? // ═══════════════════════════════════════════════════ // EVENTS // ═══════════════════════════════════════════════════ event AttestationIssued(uint256 indexed attestationId, address indexed agent, address indexed auditor, RiskLevel riskLevel, string framework); event AttestationRevoked(uint256 indexed attestationId, address indexed agent, address indexed auditor, string reason); event AttestationExpired(uint256 indexed attestationId, address indexed agent); event AuditorAuthorized(address indexed auditor, string auditorURI); event AuditorRevoked(address indexed auditor); event ComplianceStatusChanged(address indexed agent, bool isCompliant); // ═══════════════════════════════════════════════════ // MODIFIERS // ═══════════════════════════════════════════════════ modifier onlyOwner() { require(msg.sender == owner, "ComplianceRegistry: caller is not owner"); _; } modifier onlyAuthorizedAuditor() { require(auditors[msg.sender].authorized, "ComplianceRegistry: caller is not authorized auditor"); _; } modifier onlyRegisteredAgent(address agent) { (string memory uri, , , ) = identityRegistry.getAgent(agent); require(bytes(uri).length > 0, "ComplianceRegistry: agent not registered"); _; } // ═══════════════════════════════════════════════════ // CONSTRUCTOR // ═══════════════════════════════════════════════════ constructor(address _identityRegistry) { require(_identityRegistry != address(0), "ComplianceRegistry: zero identity address"); identityRegistry = IVaultfireIdentity(_identityRegistry); owner = msg.sender; } // ═══════════════════════════════════════════════════ // ATTESTATION MANAGEMENT // ═══════════════════════════════════════════════════ /// @notice Issue a compliance attestation for an agent /// @param agent The agent being attested /// @param riskLevel EU AI Act risk classification /// @param duration Duration of attestation validity (seconds) /// @param attestationURI URI to full compliance documentation /// @param framework Compliance framework (e.g., "EU_AI_ACT_2026") /// @param humanOversight Whether human oversight mechanism is confirmed /// @param technicalDoc Whether technical documentation exists /// @param postMarket Whether post-market monitoring is active /// @param incident Whether incident reporting is configured function issueAttestation( address agent, RiskLevel riskLevel, uint256 duration, string calldata attestationURI, string calldata framework, bool humanOversight, bool technicalDoc, bool postMarket, bool incident ) external onlyAuthorizedAuditor onlyRegisteredAgent(agent) { require(riskLevel != RiskLevel.Unacceptable, "ComplianceRegistry: unacceptable risk agents cannot be attested"); require(duration >= MIN_ATTESTATION_DURATION && duration <= MAX_ATTESTATION_DURATION, "ComplianceRegistry: invalid duration"); require(bytes(attestationURI).length > 0, "ComplianceRegistry: empty attestation URI"); require(bytes(framework).length > 0, "ComplianceRegistry: empty framework"); // High-risk agents must have all compliance checks verified if (riskLevel == RiskLevel.High) { require(humanOversight, "ComplianceRegistry: high-risk requires human oversight"); require(technicalDoc, "ComplianceRegistry: high-risk requires technical documentation"); require(postMarket, "ComplianceRegistry: high-risk requires post-market monitoring"); require(incident, "ComplianceRegistry: high-risk requires incident reporting"); } uint256 attestationId = totalAttestations; totalAttestations++; attestations[attestationId] = ComplianceAttestation({ agent: agent, auditor: msg.sender, riskLevel: riskLevel, status: ComplianceStatus.Compliant, issuedAt: block.timestamp, expiresAt: block.timestamp + duration, revokedAt: 0, attestationURI: attestationURI, framework: framework, humanOversightVerified: humanOversight, technicalDocVerified: technicalDoc, postMarketMonitoring: postMarket, incidentReporting: incident }); agentAttestations[agent].push(attestationId); auditors[msg.sender].attestationsIssued++; // Update compliance status if (!isCompliant[agent]) { isCompliant[agent] = true; totalCompliantAgents++; emit ComplianceStatusChanged(agent, true); } emit AttestationIssued(attestationId, agent, msg.sender, riskLevel, framework); } /// @notice Revoke an attestation /// @param attestationId The attestation to revoke /// @param reason Reason for revocation function revokeAttestation(uint256 attestationId, string calldata reason) external onlyAuthorizedAuditor { ComplianceAttestation storage att = attestations[attestationId]; require(att.status == ComplianceStatus.Compliant, "ComplianceRegistry: not compliant"); require(att.auditor == msg.sender || msg.sender == owner, "ComplianceRegistry: not attestation auditor"); att.status = ComplianceStatus.Revoked; att.revokedAt = block.timestamp; // Check if agent still has any valid attestation _recalculateCompliance(att.agent); emit AttestationRevoked(attestationId, att.agent, msg.sender, reason); } /// @notice Mark an attestation as expired /// @param attestationId The attestation to expire function expireAttestation(uint256 attestationId) external { ComplianceAttestation storage att = attestations[attestationId]; require(att.status == ComplianceStatus.Compliant, "ComplianceRegistry: not compliant"); require(block.timestamp >= att.expiresAt, "ComplianceRegistry: not expired yet"); att.status = ComplianceStatus.Expired; _recalculateCompliance(att.agent); emit AttestationExpired(attestationId, att.agent); } // ═══════════════════════════════════════════════════ // INTERNAL // ═══════════════════════════════════════════════════ /// @notice Recalculate if agent is still compliant after revocation/expiry function _recalculateCompliance(address agent) internal { uint256[] storage ids = agentAttestations[agent]; bool hasValid = false; for (uint256 i = 0; i < ids.length; i++) { ComplianceAttestation storage att = attestations[ids[i]]; if (att.status == ComplianceStatus.Compliant && block.timestamp < att.expiresAt) { hasValid = true; break; } } if (isCompliant[agent] && !hasValid) { isCompliant[agent] = false; totalCompliantAgents--; emit ComplianceStatusChanged(agent, false); } } // ═══════════════════════════════════════════════════ // VIEW FUNCTIONS // ═══════════════════════════════════════════════════ /// @notice Check if an agent is currently compliant function checkCompliance(address agent) external view returns (bool compliant, uint256 activeAttestations) { uint256[] storage ids = agentAttestations[agent]; uint256 active = 0; bool hasValid = false; for (uint256 i = 0; i < ids.length; i++) { ComplianceAttestation storage att = attestations[ids[i]]; if (att.status == ComplianceStatus.Compliant && block.timestamp < att.expiresAt) { hasValid = true; active++; } } return (hasValid, active); } /// @notice Get all attestation IDs for an agent function getAgentAttestations(address agent) external view returns (uint256[] memory) { return agentAttestations[agent]; } /// @notice Get compliance registry stats function getComplianceStats() external view returns ( uint256 _totalAttestations, uint256 _totalAuditors, uint256 _totalCompliantAgents ) { return (totalAttestations, totalAuditors, totalCompliantAgents); } // ═══════════════════════════════════════════════════ // AUDITOR MANAGEMENT // ═══════════════════════════════════════════════════ /// @notice Authorize an auditor to issue compliance attestations function authorizeAuditor(address auditor, string calldata auditorURI) external onlyOwner { require(auditor != address(0), "ComplianceRegistry: zero auditor"); require(!auditors[auditor].authorized, "ComplianceRegistry: already authorized"); auditors[auditor] = AuditorInfo({ authorized: true, authorizedAt: block.timestamp, attestationsIssued: 0, auditorURI: auditorURI }); totalAuditors++; emit AuditorAuthorized(auditor, auditorURI); } /// @notice Revoke an auditor's authorization function revokeAuditor(address auditor) external onlyOwner { require(auditors[auditor].authorized, "ComplianceRegistry: not authorized"); auditors[auditor].authorized = false; totalAuditors--; emit AuditorRevoked(auditor); } /// @notice Transfer ownership function transferOwnership(address newOwner) external onlyOwner { require(newOwner != address(0), "ComplianceRegistry: zero owner"); owner = newOwner; } }