diff --git a/src/gridguard_secure_attested_cross_domain_e/__init__.py b/src/gridguard_secure_attested_cross_domain_e/__init__.py index 9ae3032..ec1d250 100644 --- a/src/gridguard_secure_attested_cross_domain_e/__init__.py +++ b/src/gridguard_secure_attested_cross_domain_e/__init__.py @@ -13,7 +13,7 @@ from .marketplace import AdaptersMarketplace from .simulation import SimulationHarness from .privacy import SecureAggregator, PrivacyBudget from .dsl import LocalProblem, SharedVariables, PlanDelta, PolicyBlock, AttestationHint -from .bridge import to_canonical, from_canonical +from .bridge import to_canonical, from_canonical, EnergiBridge __all__ = [ "SecurityContractsRegistry", @@ -33,4 +33,5 @@ __all__ = [ "AttestationHint", "to_canonical", "from_canonical", + "EnergiBridge", ] diff --git a/src/gridguard_secure_attested_cross_domain_e/attestation.py b/src/gridguard_secure_attested_cross_domain_e/attestation.py index 9ae8740..b62cfe4 100644 --- a/src/gridguard_secure_attested_cross_domain_e/attestation.py +++ b/src/gridguard_secure_attested_cross_domain_e/attestation.py @@ -12,10 +12,16 @@ class AttestedAgent: self.agent_id = agent_id self.hardware = hardware self._credential: Optional[str] = None + # Lightweight DID-style identity; in a real system this would be bound to + # a hardware-backed identity and rotated periodically. + self._did: Optional[str] = None def attest(self) -> bool: # In a real system, remote attestation would happen here. self._credential = f"attest-{self.agent_id}-{self.hardware}-v1" + # Issue or refresh a minimal DID identity alongside attestation + if self._did is None: + self._did = f"did:gridguard:{self.agent_id}" return True @property @@ -31,3 +37,28 @@ class AttestedAgent: """ expected = f"attest-{self.agent_id}-{self.hardware}-v1" return credential == expected + + @property + def did(self) -> str: + """Return the agent's Decentralized Identifier (DID). + + If not yet issued, lazily generate a simple DID. This is a lightweight + stand-in for DID/identity binding in MVP contexts. + """ + if self._did is None: + self._did = f"did:gridguard:{self.agent_id}" + return self._did + + def rotate_identity(self) -> None: + """Rotate the agent's identity to simulate short-lived credentials. + + In a production system this would refresh attestation keys and rotate + credentials. Here we simply mutate the DID suffix to reflect rotation. + """ + suffix = getattr(self, "_did", None) + if suffix is None: + self._did = f"did:gridguard:{self.agent_id}" + else: + # Simple rotation by appending a timestamp-like suffix + import time + self._did = f"did:gridguard:{self.agent_id}:{int(time.time())}" diff --git a/src/gridguard_secure_attested_cross_domain_e/bridge.py b/src/gridguard_secure_attested_cross_domain_e/bridge.py index 2b0479f..1127e0b 100644 --- a/src/gridguard_secure_attested_cross_domain_e/bridge.py +++ b/src/gridguard_secure_attested_cross_domain_e/bridge.py @@ -37,3 +37,28 @@ class EnergiBridge: @staticmethod def from_canonical(canonical: Dict[str, Any]) -> Dict[str, Any]: return from_canonical(canonical) + + @staticmethod + def to_canonical_with_contract(local_problem: Dict[str, Any], version: str = "1.0", contract_id: str = "default") -> Dict[str, Any]: + """Extended canonical representation including simple contract metadata. + + This enhances the MVP bridge with a minimal Graph-of-Contracts hint to + help adapters interoperate in a CatOpt-like canonical form while keeping + the representation lightweight. + """ + base = EnergiBridge.to_canonical(local_problem) + # Attach a tiny contract metadata block to aid auditing and routing + base_metadata = { + "GraphOfContracts": [ + { + "contract_id": contract_id, + "version": version, + "bindings": ["LocalProblems"], + "notes": "mvp metadata" , + } + ], + "ContractsVersion": version, + } + # Merge metadata into the canonical payload under a consistent key + base.update(base_metadata) + return base