agent_os_kernel 3.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agent_control_plane/__init__.py +662 -0
- agent_control_plane/a2a_adapter.py +543 -0
- agent_control_plane/adapter.py +417 -0
- agent_control_plane/agent_hibernation.py +394 -0
- agent_control_plane/agent_kernel.py +470 -0
- agent_control_plane/compliance.py +720 -0
- agent_control_plane/constraint_graphs.py +478 -0
- agent_control_plane/control_plane.py +854 -0
- agent_control_plane/example_executors.py +195 -0
- agent_control_plane/execution_engine.py +231 -0
- agent_control_plane/flight_recorder.py +846 -0
- agent_control_plane/governance_layer.py +435 -0
- agent_control_plane/hf_utils.py +563 -0
- agent_control_plane/interfaces/__init__.py +55 -0
- agent_control_plane/interfaces/kernel_interface.py +361 -0
- agent_control_plane/interfaces/plugin_interface.py +497 -0
- agent_control_plane/interfaces/protocol_interfaces.py +387 -0
- agent_control_plane/kernel_space.py +1009 -0
- agent_control_plane/langchain_adapter.py +424 -0
- agent_control_plane/lifecycle.py +3113 -0
- agent_control_plane/mcp_adapter.py +653 -0
- agent_control_plane/ml_safety.py +563 -0
- agent_control_plane/multimodal.py +727 -0
- agent_control_plane/mute_agent.py +422 -0
- agent_control_plane/observability.py +787 -0
- agent_control_plane/orchestrator.py +482 -0
- agent_control_plane/plugin_registry.py +750 -0
- agent_control_plane/policy_engine.py +954 -0
- agent_control_plane/process_isolation.py +777 -0
- agent_control_plane/shadow_mode.py +310 -0
- agent_control_plane/signals.py +493 -0
- agent_control_plane/supervisor_agents.py +430 -0
- agent_control_plane/time_travel_debugger.py +557 -0
- agent_control_plane/tool_registry.py +452 -0
- agent_control_plane/vfs.py +697 -0
- agent_kernel/__init__.py +69 -0
- agent_kernel/analyzer.py +435 -0
- agent_kernel/auditor.py +36 -0
- agent_kernel/completeness_auditor.py +237 -0
- agent_kernel/detector.py +203 -0
- agent_kernel/kernel.py +744 -0
- agent_kernel/memory_manager.py +85 -0
- agent_kernel/models.py +374 -0
- agent_kernel/nudge_mechanism.py +263 -0
- agent_kernel/outcome_analyzer.py +338 -0
- agent_kernel/patcher.py +582 -0
- agent_kernel/semantic_analyzer.py +316 -0
- agent_kernel/semantic_purge.py +349 -0
- agent_kernel/simulator.py +449 -0
- agent_kernel/teacher.py +85 -0
- agent_kernel/triage.py +152 -0
- agent_os/__init__.py +409 -0
- agent_os/_adversarial_impl.py +200 -0
- agent_os/_circuit_breaker_impl.py +232 -0
- agent_os/_mcp_metrics.py +193 -0
- agent_os/adversarial.py +20 -0
- agent_os/agents_compat.py +490 -0
- agent_os/audit_logger.py +135 -0
- agent_os/base_agent.py +651 -0
- agent_os/circuit_breaker.py +34 -0
- agent_os/cli/__init__.py +659 -0
- agent_os/cli/cmd_audit.py +128 -0
- agent_os/cli/cmd_init.py +152 -0
- agent_os/cli/cmd_policy.py +41 -0
- agent_os/cli/cmd_policy_gen.py +180 -0
- agent_os/cli/cmd_validate.py +258 -0
- agent_os/cli/mcp_scan.py +265 -0
- agent_os/cli/output.py +192 -0
- agent_os/cli/policy_checker.py +330 -0
- agent_os/compat.py +74 -0
- agent_os/constraint_graph.py +234 -0
- agent_os/content_governance.py +140 -0
- agent_os/context_budget.py +305 -0
- agent_os/credential_redactor.py +224 -0
- agent_os/diff_policy.py +89 -0
- agent_os/egress_policy.py +159 -0
- agent_os/escalation.py +276 -0
- agent_os/event_bus.py +124 -0
- agent_os/exceptions.py +180 -0
- agent_os/execution_context_policy.py +141 -0
- agent_os/github_enterprise.py +96 -0
- agent_os/health.py +20 -0
- agent_os/integrations/__init__.py +279 -0
- agent_os/integrations/a2a_adapter.py +279 -0
- agent_os/integrations/agent_lightning/__init__.py +30 -0
- agent_os/integrations/anthropic_adapter.py +420 -0
- agent_os/integrations/autogen_adapter.py +620 -0
- agent_os/integrations/base.py +1137 -0
- agent_os/integrations/compat.py +229 -0
- agent_os/integrations/config.py +98 -0
- agent_os/integrations/conversation_guardian.py +957 -0
- agent_os/integrations/crewai_adapter.py +467 -0
- agent_os/integrations/drift_detector.py +425 -0
- agent_os/integrations/dry_run.py +124 -0
- agent_os/integrations/escalation.py +582 -0
- agent_os/integrations/gemini_adapter.py +364 -0
- agent_os/integrations/google_adk_adapter.py +633 -0
- agent_os/integrations/guardrails_adapter.py +394 -0
- agent_os/integrations/health.py +197 -0
- agent_os/integrations/langchain_adapter.py +654 -0
- agent_os/integrations/llamafirewall.py +343 -0
- agent_os/integrations/llamaindex_adapter.py +188 -0
- agent_os/integrations/logging.py +191 -0
- agent_os/integrations/maf_adapter.py +631 -0
- agent_os/integrations/mistral_adapter.py +365 -0
- agent_os/integrations/openai_adapter.py +816 -0
- agent_os/integrations/openai_agents_sdk.py +406 -0
- agent_os/integrations/policy_compose.py +171 -0
- agent_os/integrations/profiling.py +144 -0
- agent_os/integrations/pydantic_ai_adapter.py +420 -0
- agent_os/integrations/rate_limiter.py +130 -0
- agent_os/integrations/rbac.py +143 -0
- agent_os/integrations/registry.py +113 -0
- agent_os/integrations/scope_guard.py +303 -0
- agent_os/integrations/semantic_kernel_adapter.py +769 -0
- agent_os/integrations/smolagents_adapter.py +629 -0
- agent_os/integrations/templates.py +178 -0
- agent_os/integrations/token_budget.py +134 -0
- agent_os/integrations/tool_aliases.py +190 -0
- agent_os/integrations/webhooks.py +177 -0
- agent_os/lite.py +208 -0
- agent_os/mcp_gateway.py +385 -0
- agent_os/mcp_message_signer.py +273 -0
- agent_os/mcp_protocols.py +161 -0
- agent_os/mcp_response_scanner.py +232 -0
- agent_os/mcp_security.py +924 -0
- agent_os/mcp_session_auth.py +231 -0
- agent_os/mcp_sliding_rate_limiter.py +184 -0
- agent_os/memory_guard.py +409 -0
- agent_os/metrics.py +134 -0
- agent_os/mute.py +428 -0
- agent_os/mute_agent.py +209 -0
- agent_os/policies/__init__.py +77 -0
- agent_os/policies/async_evaluator.py +275 -0
- agent_os/policies/backends.py +670 -0
- agent_os/policies/bridge.py +169 -0
- agent_os/policies/budget.py +85 -0
- agent_os/policies/cli.py +294 -0
- agent_os/policies/conflict_resolution.py +270 -0
- agent_os/policies/data_classification.py +252 -0
- agent_os/policies/evaluator.py +239 -0
- agent_os/policies/policy_schema.json +228 -0
- agent_os/policies/rate_limiting.py +145 -0
- agent_os/policies/schema.py +115 -0
- agent_os/policies/shared.py +331 -0
- agent_os/prompt_injection.py +694 -0
- agent_os/providers.py +182 -0
- agent_os/py.typed +0 -0
- agent_os/retry.py +81 -0
- agent_os/reversibility.py +251 -0
- agent_os/sandbox.py +432 -0
- agent_os/sandbox_provider.py +140 -0
- agent_os/secure_codegen.py +525 -0
- agent_os/security_skills.py +538 -0
- agent_os/semantic_policy.py +422 -0
- agent_os/server/__init__.py +15 -0
- agent_os/server/__main__.py +25 -0
- agent_os/server/app.py +277 -0
- agent_os/server/models.py +104 -0
- agent_os/shift_left_metrics.py +130 -0
- agent_os/stateless.py +742 -0
- agent_os/supervisor.py +148 -0
- agent_os/task_outcome.py +148 -0
- agent_os/transparency.py +181 -0
- agent_os/trust_root.py +128 -0
- agent_os_kernel-3.1.0.dist-info/METADATA +1269 -0
- agent_os_kernel-3.1.0.dist-info/RECORD +337 -0
- agent_os_kernel-3.1.0.dist-info/WHEEL +4 -0
- agent_os_kernel-3.1.0.dist-info/entry_points.txt +2 -0
- agent_os_kernel-3.1.0.dist-info/licenses/LICENSE +21 -0
- agent_os_observability/__init__.py +27 -0
- agent_os_observability/dashboards.py +898 -0
- agent_os_observability/metrics.py +398 -0
- agent_os_observability/server.py +223 -0
- agent_os_observability/tracer.py +232 -0
- agent_primitives/__init__.py +24 -0
- agent_primitives/failures.py +84 -0
- agent_primitives/py.typed +0 -0
- amb_core/__init__.py +177 -0
- amb_core/adapters/__init__.py +57 -0
- amb_core/adapters/aws_sqs_broker.py +376 -0
- amb_core/adapters/azure_servicebus_broker.py +340 -0
- amb_core/adapters/kafka_broker.py +260 -0
- amb_core/adapters/nats_broker.py +285 -0
- amb_core/adapters/rabbitmq_broker.py +235 -0
- amb_core/adapters/redis_broker.py +262 -0
- amb_core/broker.py +145 -0
- amb_core/bus.py +481 -0
- amb_core/cloudevents.py +509 -0
- amb_core/dlq.py +345 -0
- amb_core/hf_utils.py +536 -0
- amb_core/memory_broker.py +410 -0
- amb_core/models.py +141 -0
- amb_core/persistence.py +529 -0
- amb_core/schema.py +294 -0
- amb_core/tracing.py +358 -0
- atr/__init__.py +640 -0
- atr/access.py +348 -0
- atr/composition.py +645 -0
- atr/decorator.py +357 -0
- atr/executor.py +384 -0
- atr/health.py +557 -0
- atr/hf_utils.py +449 -0
- atr/injection.py +422 -0
- atr/metrics.py +440 -0
- atr/policies.py +403 -0
- atr/py.typed +2 -0
- atr/registry.py +452 -0
- atr/schema.py +480 -0
- atr/tools/safe/__init__.py +75 -0
- atr/tools/safe/calculator.py +467 -0
- atr/tools/safe/datetime_tool.py +443 -0
- atr/tools/safe/file_reader.py +402 -0
- atr/tools/safe/http_client.py +316 -0
- atr/tools/safe/json_parser.py +374 -0
- atr/tools/safe/text_tool.py +537 -0
- atr/tools/safe/toolkit.py +175 -0
- caas/__init__.py +162 -0
- caas/api/__init__.py +7 -0
- caas/api/server.py +1328 -0
- caas/caching.py +834 -0
- caas/cli.py +210 -0
- caas/conversation.py +223 -0
- caas/decay.py +72 -0
- caas/detection/__init__.py +9 -0
- caas/detection/detector.py +238 -0
- caas/enrichment.py +130 -0
- caas/gateway/__init__.py +27 -0
- caas/gateway/trust_gateway.py +474 -0
- caas/hf_utils.py +479 -0
- caas/ingestion/__init__.py +23 -0
- caas/ingestion/processors.py +253 -0
- caas/ingestion/structure_parser.py +188 -0
- caas/models.py +356 -0
- caas/pragmatic_truth.py +444 -0
- caas/routing/__init__.py +10 -0
- caas/routing/heuristic_router.py +58 -0
- caas/storage/__init__.py +9 -0
- caas/storage/store.py +389 -0
- caas/triad.py +213 -0
- caas/tuning/__init__.py +9 -0
- caas/tuning/tuner.py +329 -0
- caas/vfs/__init__.py +14 -0
- caas/vfs/filesystem.py +452 -0
- cmvk/__init__.py +218 -0
- cmvk/audit.py +402 -0
- cmvk/benchmarks.py +478 -0
- cmvk/constitutional.py +904 -0
- cmvk/hf_utils.py +301 -0
- cmvk/metrics.py +473 -0
- cmvk/profiles.py +300 -0
- cmvk/py.typed +0 -0
- cmvk/types.py +12 -0
- cmvk/verification.py +956 -0
- emk/__init__.py +89 -0
- emk/causal.py +352 -0
- emk/hf_utils.py +421 -0
- emk/indexer.py +83 -0
- emk/py.typed +0 -0
- emk/schema.py +204 -0
- emk/sleep_cycle.py +347 -0
- emk/store.py +281 -0
- iatp/__init__.py +166 -0
- iatp/attestation.py +461 -0
- iatp/cli.py +317 -0
- iatp/hf_utils.py +472 -0
- iatp/ipc_pipes.py +580 -0
- iatp/main.py +412 -0
- iatp/models/__init__.py +447 -0
- iatp/policy_engine.py +337 -0
- iatp/py.typed +2 -0
- iatp/recovery.py +321 -0
- iatp/security/__init__.py +270 -0
- iatp/sidecar/__init__.py +519 -0
- iatp/telemetry/__init__.py +164 -0
- iatp/tests/__init__.py +1 -0
- iatp/tests/test_attestation.py +370 -0
- iatp/tests/test_cli.py +131 -0
- iatp/tests/test_ed25519_attestation.py +211 -0
- iatp/tests/test_models.py +130 -0
- iatp/tests/test_policy_engine.py +347 -0
- iatp/tests/test_recovery.py +281 -0
- iatp/tests/test_security.py +222 -0
- iatp/tests/test_sidecar.py +167 -0
- iatp/tests/test_telemetry.py +175 -0
- mcp_kernel_server/__init__.py +28 -0
- mcp_kernel_server/cli.py +274 -0
- mcp_kernel_server/resources.py +217 -0
- mcp_kernel_server/server.py +564 -0
- mcp_kernel_server/tools.py +1174 -0
- mute_agent/__init__.py +68 -0
- mute_agent/core/__init__.py +1 -0
- mute_agent/core/execution_agent.py +166 -0
- mute_agent/core/handshake_protocol.py +201 -0
- mute_agent/core/reasoning_agent.py +238 -0
- mute_agent/knowledge_graph/__init__.py +1 -0
- mute_agent/knowledge_graph/graph_elements.py +65 -0
- mute_agent/knowledge_graph/multidimensional_graph.py +170 -0
- mute_agent/knowledge_graph/subgraph.py +224 -0
- mute_agent/listener/__init__.py +43 -0
- mute_agent/listener/adapters/__init__.py +31 -0
- mute_agent/listener/adapters/base_adapter.py +189 -0
- mute_agent/listener/adapters/caas_adapter.py +344 -0
- mute_agent/listener/adapters/control_plane_adapter.py +436 -0
- mute_agent/listener/adapters/iatp_adapter.py +332 -0
- mute_agent/listener/adapters/scak_adapter.py +251 -0
- mute_agent/listener/listener.py +610 -0
- mute_agent/listener/state_observer.py +436 -0
- mute_agent/listener/threshold_config.py +313 -0
- mute_agent/super_system/__init__.py +1 -0
- mute_agent/super_system/router.py +204 -0
- mute_agent/visualization/__init__.py +10 -0
- mute_agent/visualization/graph_debugger.py +502 -0
- nexus/README.md +60 -0
- nexus/__init__.py +51 -0
- nexus/arbiter.py +359 -0
- nexus/client.py +466 -0
- nexus/dmz.py +444 -0
- nexus/escrow.py +430 -0
- nexus/exceptions.py +286 -0
- nexus/pyproject.toml +36 -0
- nexus/registry.py +393 -0
- nexus/reputation.py +425 -0
- nexus/schemas/__init__.py +51 -0
- nexus/schemas/compliance.py +276 -0
- nexus/schemas/escrow.py +251 -0
- nexus/schemas/manifest.py +225 -0
- nexus/schemas/receipt.py +208 -0
- nexus/tests/__init__.py +0 -0
- nexus/tests/conftest.py +146 -0
- nexus/tests/test_arbiter.py +192 -0
- nexus/tests/test_dmz.py +194 -0
- nexus/tests/test_escrow.py +276 -0
- nexus/tests/test_exceptions.py +225 -0
- nexus/tests/test_registry.py +232 -0
- nexus/tests/test_reputation.py +328 -0
- nexus/tests/test_schemas.py +295 -0
nexus/dmz.py
ADDED
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""
|
|
4
|
+
DMZ Protocol
|
|
5
|
+
|
|
6
|
+
Extension of IATP for secure data handoff between agents.
|
|
7
|
+
Implements the REQUEST_ESCROW verb and data handling policies.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from datetime import datetime, timedelta, timezone
|
|
11
|
+
from typing import Optional, Literal
|
|
12
|
+
from pydantic import BaseModel, Field
|
|
13
|
+
import hashlib
|
|
14
|
+
import uuid
|
|
15
|
+
from dataclasses import dataclass, field
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class DataHandlingPolicy(BaseModel):
|
|
19
|
+
"""
|
|
20
|
+
Policy that receiving agent must sign before getting decryption key.
|
|
21
|
+
|
|
22
|
+
Ensures data handling compliance between agents.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
policy_id: str = Field(
|
|
26
|
+
default_factory=lambda: f"policy_{uuid.uuid4().hex[:12]}"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# Retention
|
|
30
|
+
max_retention_seconds: int = Field(
|
|
31
|
+
default=3600,
|
|
32
|
+
ge=0,
|
|
33
|
+
le=2592000, # 30 days max
|
|
34
|
+
description="Maximum time to retain data (0 = process and delete)"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Permissions
|
|
38
|
+
allow_persistence: bool = Field(
|
|
39
|
+
default=False,
|
|
40
|
+
description="Whether data can be persisted to storage"
|
|
41
|
+
)
|
|
42
|
+
allow_training: bool = Field(
|
|
43
|
+
default=False,
|
|
44
|
+
description="Whether data can be used for model training"
|
|
45
|
+
)
|
|
46
|
+
allow_forwarding: bool = Field(
|
|
47
|
+
default=False,
|
|
48
|
+
description="Whether data can be forwarded to other agents"
|
|
49
|
+
)
|
|
50
|
+
allow_logging: bool = Field(
|
|
51
|
+
default=True,
|
|
52
|
+
description="Whether metadata can be logged (not content)"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Audit
|
|
56
|
+
audit_required: bool = Field(
|
|
57
|
+
default=True,
|
|
58
|
+
description="Whether all data access must be audited"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Encryption
|
|
62
|
+
require_encryption_at_rest: bool = Field(
|
|
63
|
+
default=True,
|
|
64
|
+
description="Whether data must be encrypted when stored"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def compute_hash(self) -> str:
|
|
68
|
+
"""Compute deterministic policy hash."""
|
|
69
|
+
data = self.model_dump(exclude={"policy_id"})
|
|
70
|
+
import json
|
|
71
|
+
canonical = json.dumps(data, sort_keys=True)
|
|
72
|
+
return hashlib.sha256(canonical.encode()).hexdigest()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class DMZRequest(BaseModel):
|
|
76
|
+
"""Request to share data through the secure DMZ."""
|
|
77
|
+
|
|
78
|
+
verb: Literal["REQUEST_ESCROW"] = "REQUEST_ESCROW"
|
|
79
|
+
request_id: str = Field(
|
|
80
|
+
default_factory=lambda: f"dmz_{uuid.uuid4().hex[:16]}"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Parties
|
|
84
|
+
sender_did: str
|
|
85
|
+
receiver_did: str
|
|
86
|
+
|
|
87
|
+
# Data (only hash, not actual content)
|
|
88
|
+
data_hash: str = Field(
|
|
89
|
+
...,
|
|
90
|
+
description="SHA-256 hash of the actual data"
|
|
91
|
+
)
|
|
92
|
+
data_size_bytes: int = Field(
|
|
93
|
+
...,
|
|
94
|
+
ge=0,
|
|
95
|
+
description="Size of the data in bytes"
|
|
96
|
+
)
|
|
97
|
+
data_classification: Literal["public", "internal", "confidential", "pii"] = Field(
|
|
98
|
+
default="internal",
|
|
99
|
+
description="Classification level of the data"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Policy
|
|
103
|
+
required_policy: DataHandlingPolicy
|
|
104
|
+
|
|
105
|
+
# Encryption
|
|
106
|
+
encrypted_data_location: Optional[str] = Field(
|
|
107
|
+
default=None,
|
|
108
|
+
description="URL/URI where encrypted data is stored"
|
|
109
|
+
)
|
|
110
|
+
encryption_algorithm: str = Field(
|
|
111
|
+
default="AES-256-GCM",
|
|
112
|
+
description="Encryption algorithm used"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# Timestamps
|
|
116
|
+
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
117
|
+
expires_at: Optional[datetime] = Field(
|
|
118
|
+
default=None,
|
|
119
|
+
description="When this request expires"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@dataclass
|
|
124
|
+
class SignedPolicy:
|
|
125
|
+
"""A policy that has been signed by the receiving agent."""
|
|
126
|
+
|
|
127
|
+
policy: DataHandlingPolicy
|
|
128
|
+
policy_hash: str
|
|
129
|
+
|
|
130
|
+
# Signer
|
|
131
|
+
signer_did: str
|
|
132
|
+
signed_at: datetime = field(default_factory=datetime.utcnow)
|
|
133
|
+
|
|
134
|
+
# Signature
|
|
135
|
+
signature: str = ""
|
|
136
|
+
|
|
137
|
+
# Verification
|
|
138
|
+
verified: bool = False
|
|
139
|
+
verified_at: Optional[datetime] = None
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@dataclass
|
|
143
|
+
class DMZTransfer:
|
|
144
|
+
"""Record of a DMZ data transfer."""
|
|
145
|
+
|
|
146
|
+
transfer_id: str
|
|
147
|
+
request: DMZRequest
|
|
148
|
+
|
|
149
|
+
# Policy signing
|
|
150
|
+
policy_signed: bool = False
|
|
151
|
+
signed_policy: Optional[SignedPolicy] = None
|
|
152
|
+
|
|
153
|
+
# Key release
|
|
154
|
+
key_released: bool = False
|
|
155
|
+
key_released_at: Optional[datetime] = None
|
|
156
|
+
|
|
157
|
+
# Data access
|
|
158
|
+
data_accessed: bool = False
|
|
159
|
+
data_accessed_at: Optional[datetime] = None
|
|
160
|
+
|
|
161
|
+
# Completion
|
|
162
|
+
completed: bool = False
|
|
163
|
+
completed_at: Optional[datetime] = None
|
|
164
|
+
|
|
165
|
+
# Audit
|
|
166
|
+
audit_trail: list[dict] = field(default_factory=list)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class DMZProtocol:
|
|
170
|
+
"""
|
|
171
|
+
Implements the DMZ (Demilitarized Zone) protocol for secure data sharing.
|
|
172
|
+
|
|
173
|
+
Flow:
|
|
174
|
+
1. Agent A sends REQUEST_ESCROW with data hash to Nexus
|
|
175
|
+
2. Nexus holds the encrypted data reference
|
|
176
|
+
3. Agent B signs the DataHandlingPolicy
|
|
177
|
+
4. Nexus releases decryption key to Agent B
|
|
178
|
+
5. All operations are logged for compliance
|
|
179
|
+
"""
|
|
180
|
+
|
|
181
|
+
def __init__(self):
|
|
182
|
+
# Storage
|
|
183
|
+
self._transfers: dict[str, DMZTransfer] = {}
|
|
184
|
+
self._signed_policies: dict[str, SignedPolicy] = {}
|
|
185
|
+
self._encryption_keys: dict[str, bytes] = {} # transfer_id -> key
|
|
186
|
+
|
|
187
|
+
async def initiate_transfer(
|
|
188
|
+
self,
|
|
189
|
+
sender_did: str,
|
|
190
|
+
receiver_did: str,
|
|
191
|
+
data: bytes,
|
|
192
|
+
classification: Literal["public", "internal", "confidential", "pii"],
|
|
193
|
+
policy: DataHandlingPolicy,
|
|
194
|
+
expiry_hours: int = 24,
|
|
195
|
+
) -> DMZRequest:
|
|
196
|
+
"""
|
|
197
|
+
Initiate a secure data transfer through DMZ.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
sender_did: DID of sending agent
|
|
201
|
+
receiver_did: DID of receiving agent
|
|
202
|
+
data: The actual data to transfer (will be encrypted)
|
|
203
|
+
classification: Data classification level
|
|
204
|
+
policy: Required data handling policy
|
|
205
|
+
expiry_hours: Hours until request expires
|
|
206
|
+
"""
|
|
207
|
+
# Compute data hash
|
|
208
|
+
data_hash = hashlib.sha256(data).hexdigest()
|
|
209
|
+
|
|
210
|
+
# Generate encryption key
|
|
211
|
+
import secrets
|
|
212
|
+
encryption_key = secrets.token_bytes(32) # AES-256
|
|
213
|
+
|
|
214
|
+
# Encrypt data (placeholder - would use proper encryption)
|
|
215
|
+
encrypted_data = self._encrypt_data(data, encryption_key)
|
|
216
|
+
|
|
217
|
+
# Create request
|
|
218
|
+
request = DMZRequest(
|
|
219
|
+
sender_did=sender_did,
|
|
220
|
+
receiver_did=receiver_did,
|
|
221
|
+
data_hash=data_hash,
|
|
222
|
+
data_size_bytes=len(data),
|
|
223
|
+
data_classification=classification,
|
|
224
|
+
required_policy=policy,
|
|
225
|
+
expires_at=datetime.now(timezone.utc) + timedelta(hours=expiry_hours),
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Create transfer record
|
|
229
|
+
transfer = DMZTransfer(
|
|
230
|
+
transfer_id=request.request_id,
|
|
231
|
+
request=request,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# Store
|
|
235
|
+
self._transfers[request.request_id] = transfer
|
|
236
|
+
self._encryption_keys[request.request_id] = encryption_key
|
|
237
|
+
|
|
238
|
+
# Add audit entry
|
|
239
|
+
transfer.audit_trail.append({
|
|
240
|
+
"event": "transfer_initiated",
|
|
241
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
242
|
+
"sender": sender_did,
|
|
243
|
+
"receiver": receiver_did,
|
|
244
|
+
"data_hash": data_hash,
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
return request
|
|
248
|
+
|
|
249
|
+
async def sign_policy(
|
|
250
|
+
self,
|
|
251
|
+
transfer_id: str,
|
|
252
|
+
signer_did: str,
|
|
253
|
+
signature: str,
|
|
254
|
+
) -> SignedPolicy:
|
|
255
|
+
"""
|
|
256
|
+
Sign a data handling policy to receive access.
|
|
257
|
+
|
|
258
|
+
The receiving agent must sign the policy before getting
|
|
259
|
+
the decryption key.
|
|
260
|
+
"""
|
|
261
|
+
if transfer_id not in self._transfers:
|
|
262
|
+
raise ValueError(f"Transfer not found: {transfer_id}")
|
|
263
|
+
|
|
264
|
+
transfer = self._transfers[transfer_id]
|
|
265
|
+
request = transfer.request
|
|
266
|
+
|
|
267
|
+
# Verify signer is the intended receiver
|
|
268
|
+
if signer_did != request.receiver_did:
|
|
269
|
+
raise ValueError("Only intended receiver can sign policy")
|
|
270
|
+
|
|
271
|
+
# Verify transfer hasn't expired
|
|
272
|
+
if request.expires_at and datetime.now(timezone.utc) > request.expires_at:
|
|
273
|
+
raise ValueError("Transfer has expired")
|
|
274
|
+
|
|
275
|
+
# Create signed policy
|
|
276
|
+
signed = SignedPolicy(
|
|
277
|
+
policy=request.required_policy,
|
|
278
|
+
policy_hash=request.required_policy.compute_hash(),
|
|
279
|
+
signer_did=signer_did,
|
|
280
|
+
signature=signature,
|
|
281
|
+
verified=True, # Would verify signature in production
|
|
282
|
+
verified_at=datetime.now(timezone.utc),
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
# Update transfer
|
|
286
|
+
transfer.policy_signed = True
|
|
287
|
+
transfer.signed_policy = signed
|
|
288
|
+
|
|
289
|
+
# Add audit entry
|
|
290
|
+
transfer.audit_trail.append({
|
|
291
|
+
"event": "policy_signed",
|
|
292
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
293
|
+
"signer": signer_did,
|
|
294
|
+
"policy_hash": signed.policy_hash,
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
# Store signed policy
|
|
298
|
+
self._signed_policies[f"{transfer_id}:{signer_did}"] = signed
|
|
299
|
+
|
|
300
|
+
return signed
|
|
301
|
+
|
|
302
|
+
async def release_key(
|
|
303
|
+
self,
|
|
304
|
+
transfer_id: str,
|
|
305
|
+
requester_did: str,
|
|
306
|
+
) -> bytes:
|
|
307
|
+
"""
|
|
308
|
+
Release decryption key after policy is signed.
|
|
309
|
+
|
|
310
|
+
Only releases key if:
|
|
311
|
+
1. Requester is the intended receiver
|
|
312
|
+
2. Policy has been signed
|
|
313
|
+
3. Transfer hasn't expired
|
|
314
|
+
"""
|
|
315
|
+
if transfer_id not in self._transfers:
|
|
316
|
+
raise ValueError(f"Transfer not found: {transfer_id}")
|
|
317
|
+
|
|
318
|
+
transfer = self._transfers[transfer_id]
|
|
319
|
+
request = transfer.request
|
|
320
|
+
|
|
321
|
+
# Verify requester
|
|
322
|
+
if requester_did != request.receiver_did:
|
|
323
|
+
raise ValueError("Only intended receiver can get key")
|
|
324
|
+
|
|
325
|
+
# Verify policy signed
|
|
326
|
+
if not transfer.policy_signed:
|
|
327
|
+
raise ValueError("Policy must be signed before key release")
|
|
328
|
+
|
|
329
|
+
# Verify not expired
|
|
330
|
+
if request.expires_at and datetime.now(timezone.utc) > request.expires_at:
|
|
331
|
+
raise ValueError("Transfer has expired")
|
|
332
|
+
|
|
333
|
+
# Get key
|
|
334
|
+
if transfer_id not in self._encryption_keys:
|
|
335
|
+
raise ValueError("Encryption key not found")
|
|
336
|
+
|
|
337
|
+
key = self._encryption_keys[transfer_id]
|
|
338
|
+
|
|
339
|
+
# Update transfer
|
|
340
|
+
transfer.key_released = True
|
|
341
|
+
transfer.key_released_at = datetime.now(timezone.utc)
|
|
342
|
+
|
|
343
|
+
# Add audit entry
|
|
344
|
+
transfer.audit_trail.append({
|
|
345
|
+
"event": "key_released",
|
|
346
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
347
|
+
"recipient": requester_did,
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
return key
|
|
351
|
+
|
|
352
|
+
async def record_data_access(
|
|
353
|
+
self,
|
|
354
|
+
transfer_id: str,
|
|
355
|
+
accessor_did: str,
|
|
356
|
+
) -> None:
|
|
357
|
+
"""Record that data was accessed (for audit)."""
|
|
358
|
+
if transfer_id not in self._transfers:
|
|
359
|
+
raise ValueError(f"Transfer not found: {transfer_id}")
|
|
360
|
+
|
|
361
|
+
transfer = self._transfers[transfer_id]
|
|
362
|
+
transfer.data_accessed = True
|
|
363
|
+
transfer.data_accessed_at = datetime.now(timezone.utc)
|
|
364
|
+
|
|
365
|
+
transfer.audit_trail.append({
|
|
366
|
+
"event": "data_accessed",
|
|
367
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
368
|
+
"accessor": accessor_did,
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
async def complete_transfer(
|
|
372
|
+
self,
|
|
373
|
+
transfer_id: str,
|
|
374
|
+
) -> DMZTransfer:
|
|
375
|
+
"""Mark transfer as complete."""
|
|
376
|
+
if transfer_id not in self._transfers:
|
|
377
|
+
raise ValueError(f"Transfer not found: {transfer_id}")
|
|
378
|
+
|
|
379
|
+
transfer = self._transfers[transfer_id]
|
|
380
|
+
transfer.completed = True
|
|
381
|
+
transfer.completed_at = datetime.now(timezone.utc)
|
|
382
|
+
|
|
383
|
+
transfer.audit_trail.append({
|
|
384
|
+
"event": "transfer_completed",
|
|
385
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
# Clean up key (data should be deleted per policy)
|
|
389
|
+
if transfer_id in self._encryption_keys:
|
|
390
|
+
del self._encryption_keys[transfer_id]
|
|
391
|
+
|
|
392
|
+
return transfer
|
|
393
|
+
|
|
394
|
+
def get_transfer(self, transfer_id: str) -> DMZTransfer:
|
|
395
|
+
"""Get transfer by ID."""
|
|
396
|
+
if transfer_id not in self._transfers:
|
|
397
|
+
raise ValueError(f"Transfer not found: {transfer_id}")
|
|
398
|
+
return self._transfers[transfer_id]
|
|
399
|
+
|
|
400
|
+
def get_audit_trail(self, transfer_id: str) -> list[dict]:
|
|
401
|
+
"""Get audit trail for a transfer."""
|
|
402
|
+
transfer = self.get_transfer(transfer_id)
|
|
403
|
+
return transfer.audit_trail
|
|
404
|
+
|
|
405
|
+
def has_signed_policy(self, transfer_id: str, agent_did: str) -> bool:
|
|
406
|
+
"""Check if agent has signed policy for a transfer."""
|
|
407
|
+
key = f"{transfer_id}:{agent_did}"
|
|
408
|
+
return key in self._signed_policies
|
|
409
|
+
|
|
410
|
+
def _encrypt_data(self, data: bytes, key: bytes) -> bytes:
|
|
411
|
+
"""Encrypt data with AES-256-GCM.
|
|
412
|
+
|
|
413
|
+
Requires the ``cryptography`` package (``pip install cryptography``).
|
|
414
|
+
Falls back to a clearly-marked no-op if not installed.
|
|
415
|
+
"""
|
|
416
|
+
try:
|
|
417
|
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
418
|
+
except ImportError:
|
|
419
|
+
raise ImportError(
|
|
420
|
+
"DMZ encryption requires the 'cryptography' package. "
|
|
421
|
+
"Install with: pip install cryptography"
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
# Derive a 256-bit key via SHA-256 to ensure correct length
|
|
425
|
+
derived = hashlib.sha256(key).digest()
|
|
426
|
+
nonce = hashlib.sha256(data[:16] + key).digest()[:12] # 96-bit nonce
|
|
427
|
+
aesgcm = AESGCM(derived)
|
|
428
|
+
return nonce + aesgcm.encrypt(nonce, data, None)
|
|
429
|
+
|
|
430
|
+
def _decrypt_data(self, encrypted: bytes, key: bytes) -> bytes:
|
|
431
|
+
"""Decrypt data encrypted with AES-256-GCM."""
|
|
432
|
+
try:
|
|
433
|
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
434
|
+
except ImportError:
|
|
435
|
+
raise ImportError(
|
|
436
|
+
"DMZ decryption requires the 'cryptography' package. "
|
|
437
|
+
"Install with: pip install cryptography"
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
derived = hashlib.sha256(key).digest()
|
|
441
|
+
nonce = encrypted[:12]
|
|
442
|
+
ciphertext = encrypted[12:]
|
|
443
|
+
aesgcm = AESGCM(derived)
|
|
444
|
+
return aesgcm.decrypt(nonce, ciphertext, None)
|