halyn 1.0.0__tar.gz → 2.0.0__tar.gz
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.
- halyn-2.0.0/LICENSE +21 -0
- halyn-2.0.0/PKG-INFO +80 -0
- halyn-2.0.0/README.md +60 -0
- halyn-2.0.0/halyn/__init__.py +15 -0
- halyn-2.0.0/halyn/core.py +238 -0
- halyn-2.0.0/halyn.egg-info/PKG-INFO +80 -0
- halyn-2.0.0/halyn.egg-info/SOURCES.txt +11 -0
- halyn-2.0.0/halyn.egg-info/requires.txt +5 -0
- halyn-2.0.0/pyproject.toml +32 -0
- halyn-2.0.0/tests/test_halyn.py +163 -0
- halyn-1.0.0/LICENSE +0 -4
- halyn-1.0.0/PKG-INFO +0 -108
- halyn-1.0.0/README.md +0 -73
- halyn-1.0.0/pyproject.toml +0 -49
- halyn-1.0.0/src/halyn/__init__.py +0 -48
- halyn-1.0.0/src/halyn/__main__.py +0 -4
- halyn-1.0.0/src/halyn/audit.py +0 -278
- halyn-1.0.0/src/halyn/auth.py +0 -88
- halyn-1.0.0/src/halyn/autonomy.py +0 -262
- halyn-1.0.0/src/halyn/cli.py +0 -208
- halyn-1.0.0/src/halyn/config.py +0 -135
- halyn-1.0.0/src/halyn/consent.py +0 -243
- halyn-1.0.0/src/halyn/control_plane.py +0 -449
- halyn-1.0.0/src/halyn/dashboard.py +0 -206
- halyn-1.0.0/src/halyn/discovery.py +0 -323
- halyn-1.0.0/src/halyn/drivers/__init__.py +0 -0
- halyn-1.0.0/src/halyn/drivers/browser.py +0 -70
- halyn-1.0.0/src/halyn/drivers/dds.py +0 -156
- halyn-1.0.0/src/halyn/drivers/docker.py +0 -72
- halyn-1.0.0/src/halyn/drivers/http_auto.py +0 -259
- halyn-1.0.0/src/halyn/drivers/mqtt.py +0 -103
- halyn-1.0.0/src/halyn/drivers/opcua.py +0 -87
- halyn-1.0.0/src/halyn/drivers/ros2.py +0 -134
- halyn-1.0.0/src/halyn/drivers/serial.py +0 -226
- halyn-1.0.0/src/halyn/drivers/socket_raw.py +0 -153
- halyn-1.0.0/src/halyn/drivers/ssh.py +0 -131
- halyn-1.0.0/src/halyn/drivers/unitree.py +0 -113
- halyn-1.0.0/src/halyn/drivers/websocket.py +0 -175
- halyn-1.0.0/src/halyn/engine.py +0 -222
- halyn-1.0.0/src/halyn/integrations/__init__.py +0 -0
- halyn-1.0.0/src/halyn/integrations/telegram.py +0 -193
- halyn-1.0.0/src/halyn/intent.py +0 -240
- halyn-1.0.0/src/halyn/llm.py +0 -178
- halyn-1.0.0/src/halyn/mcp.py +0 -239
- halyn-1.0.0/src/halyn/mcp_serve.py +0 -393
- halyn-1.0.0/src/halyn/memory/__init__.py +0 -0
- halyn-1.0.0/src/halyn/memory/store.py +0 -200
- halyn-1.0.0/src/halyn/nrp_bridge.py +0 -213
- halyn-1.0.0/src/halyn/py.typed +0 -0
- halyn-1.0.0/src/halyn/sanitizer.py +0 -120
- halyn-1.0.0/src/halyn/server.py +0 -292
- halyn-1.0.0/src/halyn/shield.py +0 -113
- halyn-1.0.0/src/halyn/types.py +0 -116
- halyn-1.0.0/src/halyn/watchdog.py +0 -252
- halyn-1.0.0/src/halyn.egg-info/PKG-INFO +0 -108
- halyn-1.0.0/src/halyn.egg-info/SOURCES.txt +0 -50
- halyn-1.0.0/src/halyn.egg-info/entry_points.txt +0 -3
- halyn-1.0.0/src/halyn.egg-info/requires.txt +0 -20
- halyn-1.0.0/tests/test_halyn.py +0 -246
- {halyn-1.0.0/src → halyn-2.0.0}/halyn.egg-info/dependency_links.txt +0 -0
- {halyn-1.0.0/src → halyn-2.0.0}/halyn.egg-info/top_level.txt +0 -0
- {halyn-1.0.0 → halyn-2.0.0}/setup.cfg +0 -0
halyn-2.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Elmadani SALKA
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
halyn-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: halyn
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Halyn — AAP in action. AI agents that can prove what they did.
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://halyn.dev
|
|
7
|
+
Project-URL: Repository, https://github.com/ElmadaniS/halyn
|
|
8
|
+
Keywords: halyn,aap,agent,accountability,ai,safety
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: aap-protocol>=0.1.0
|
|
16
|
+
Requires-Dist: nrprotocol>=0.1.0
|
|
17
|
+
Provides-Extra: test
|
|
18
|
+
Requires-Dist: pytest>=8.0; extra == "test"
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
<div align="center">
|
|
22
|
+
|
|
23
|
+
# Halyn
|
|
24
|
+
|
|
25
|
+
**AAP in action. AI agents that can prove what they did.**
|
|
26
|
+
|
|
27
|
+
[](https://pypi.org/project/halyn/)
|
|
28
|
+
[](LICENSE)
|
|
29
|
+
[](tests/)
|
|
30
|
+
|
|
31
|
+
The reference implementation of [AAP — Agent Accountability Protocol](https://aap-protocol.dev)
|
|
32
|
+
|
|
33
|
+
[Website](https://halyn.dev) · [AAP Spec](https://aap-protocol.dev) · [NRP](https://nrprotocol.dev)
|
|
34
|
+
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## What is Halyn?
|
|
40
|
+
|
|
41
|
+
Halyn is the complete demonstration of [AAP](https://aap-protocol.dev).
|
|
42
|
+
|
|
43
|
+
Every agent action goes through 7 steps — none skippable:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
Identity → Authorization → Scope → Shield → Execute → Provenance → Audit
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from halyn import HalynControlPlane, ExecutionContext
|
|
51
|
+
from aap import AAPKeyPair, AAPIdentity, AAPAuthorization, AuthorizationLevel
|
|
52
|
+
|
|
53
|
+
# Setup
|
|
54
|
+
supervisor = AAPKeyPair.generate()
|
|
55
|
+
cp = HalynControlPlane(supervisor_keypair=supervisor, supervisor_did="did:key:z6Mk...")
|
|
56
|
+
|
|
57
|
+
# Register agent + node
|
|
58
|
+
cp.register_identity(identity)
|
|
59
|
+
cp.register_node(node)
|
|
60
|
+
auth = cp.grant_authorization(agent_id=..., level=AuthorizationLevel.SUPERVISED, scope=["write:file"])
|
|
61
|
+
|
|
62
|
+
# Execute — full AAP pipeline, fully audited
|
|
63
|
+
ctx = ExecutionContext(identity=identity, authorization=auth, agent_keypair=agent_kp)
|
|
64
|
+
result = cp.execute(ctx, node_id="nrp://acme/server/files-1", action="write:file", params={...})
|
|
65
|
+
|
|
66
|
+
# Verify audit chain integrity
|
|
67
|
+
valid, count, broken = cp.verify_audit()
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## AAP Ecosystem
|
|
71
|
+
|
|
72
|
+
| Layer | Project | Role |
|
|
73
|
+
|-------|---------|------|
|
|
74
|
+
| Spec | [AAP](https://aap-protocol.dev) | Protocol definition |
|
|
75
|
+
| Physical | [NRP](https://nrprotocol.dev) | AAP for robots, IoT, machines |
|
|
76
|
+
| Full system | **Halyn** (this) | Complete demonstration |
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
MIT
|
halyn-2.0.0/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# Halyn
|
|
4
|
+
|
|
5
|
+
**AAP in action. AI agents that can prove what they did.**
|
|
6
|
+
|
|
7
|
+
[](https://pypi.org/project/halyn/)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](tests/)
|
|
10
|
+
|
|
11
|
+
The reference implementation of [AAP — Agent Accountability Protocol](https://aap-protocol.dev)
|
|
12
|
+
|
|
13
|
+
[Website](https://halyn.dev) · [AAP Spec](https://aap-protocol.dev) · [NRP](https://nrprotocol.dev)
|
|
14
|
+
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## What is Halyn?
|
|
20
|
+
|
|
21
|
+
Halyn is the complete demonstration of [AAP](https://aap-protocol.dev).
|
|
22
|
+
|
|
23
|
+
Every agent action goes through 7 steps — none skippable:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
Identity → Authorization → Scope → Shield → Execute → Provenance → Audit
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from halyn import HalynControlPlane, ExecutionContext
|
|
31
|
+
from aap import AAPKeyPair, AAPIdentity, AAPAuthorization, AuthorizationLevel
|
|
32
|
+
|
|
33
|
+
# Setup
|
|
34
|
+
supervisor = AAPKeyPair.generate()
|
|
35
|
+
cp = HalynControlPlane(supervisor_keypair=supervisor, supervisor_did="did:key:z6Mk...")
|
|
36
|
+
|
|
37
|
+
# Register agent + node
|
|
38
|
+
cp.register_identity(identity)
|
|
39
|
+
cp.register_node(node)
|
|
40
|
+
auth = cp.grant_authorization(agent_id=..., level=AuthorizationLevel.SUPERVISED, scope=["write:file"])
|
|
41
|
+
|
|
42
|
+
# Execute — full AAP pipeline, fully audited
|
|
43
|
+
ctx = ExecutionContext(identity=identity, authorization=auth, agent_keypair=agent_kp)
|
|
44
|
+
result = cp.execute(ctx, node_id="nrp://acme/server/files-1", action="write:file", params={...})
|
|
45
|
+
|
|
46
|
+
# Verify audit chain integrity
|
|
47
|
+
valid, count, broken = cp.verify_audit()
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## AAP Ecosystem
|
|
51
|
+
|
|
52
|
+
| Layer | Project | Role |
|
|
53
|
+
|-------|---------|------|
|
|
54
|
+
| Spec | [AAP](https://aap-protocol.dev) | Protocol definition |
|
|
55
|
+
| Physical | [NRP](https://nrprotocol.dev) | AAP for robots, IoT, machines |
|
|
56
|
+
| Full system | **Halyn** (this) | Complete demonstration |
|
|
57
|
+
|
|
58
|
+
## License
|
|
59
|
+
|
|
60
|
+
MIT
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Halyn — AAP in action. AI agents that can prove what they did.
|
|
3
|
+
|
|
4
|
+
The reference implementation of AAP — Agent Accountability Protocol.
|
|
5
|
+
https://halyn.dev | https://aap-protocol.dev
|
|
6
|
+
License: MIT
|
|
7
|
+
"""
|
|
8
|
+
import sys
|
|
9
|
+
sys.path.insert(0, "/mnt/data/aap/sdk/python")
|
|
10
|
+
sys.path.insert(0, "/mnt/data/nrp-new")
|
|
11
|
+
|
|
12
|
+
from .core import HalynControlPlane, ExecutionContext, ExecutionResult
|
|
13
|
+
|
|
14
|
+
__version__ = "1.0.0"
|
|
15
|
+
__all__ = ["HalynControlPlane", "ExecutionContext", "ExecutionResult"]
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Halyn ControlPlane — the full AAP implementation.
|
|
3
|
+
Orchestrates: Identity → Authorization → Execution → Provenance → Audit
|
|
4
|
+
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
import sys
|
|
7
|
+
sys.path.insert(0, "/mnt/data/aap/sdk/python")
|
|
8
|
+
sys.path.insert(0, "/mnt/data/nrp-new")
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from datetime import datetime, timezone
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from aap import (
|
|
15
|
+
AAPIdentity, AAPKeyPair, AAPAuthorization, AuthorizationLevel,
|
|
16
|
+
AAPProvenance, AAPAuditChain,
|
|
17
|
+
AAPPhysicalWorldViolation, AAPScopeError, AAPRevocationError,
|
|
18
|
+
)
|
|
19
|
+
from nrp import NRPNode, ShieldViolation
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class ExecutionContext:
|
|
24
|
+
"""Full context for a single agent action — identity + authorization."""
|
|
25
|
+
identity: AAPIdentity
|
|
26
|
+
authorization: AAPAuthorization
|
|
27
|
+
agent_keypair: AAPKeyPair
|
|
28
|
+
|
|
29
|
+
def check(self) -> None:
|
|
30
|
+
"""Validate context before any action."""
|
|
31
|
+
if self.identity.revoked:
|
|
32
|
+
raise AAPRevocationError(f"Identity {self.identity.id} is revoked")
|
|
33
|
+
self.authorization.check()
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def agent_id(self) -> str:
|
|
37
|
+
return self.identity.id
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def auth_level(self) -> AuthorizationLevel:
|
|
41
|
+
return self.authorization.level
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class ExecutionResult:
|
|
46
|
+
"""Result of a Halyn-mediated action."""
|
|
47
|
+
success: bool
|
|
48
|
+
agent_id: str
|
|
49
|
+
action: str
|
|
50
|
+
node_id: str | None
|
|
51
|
+
output: Any
|
|
52
|
+
provenance_id: str | None
|
|
53
|
+
audit_entry_id: str | None
|
|
54
|
+
blocked: bool = False
|
|
55
|
+
block_reason: str | None = None
|
|
56
|
+
detail: str | None = None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class HalynControlPlane:
|
|
60
|
+
"""
|
|
61
|
+
AAP reference implementation — full system.
|
|
62
|
+
|
|
63
|
+
Flow for every action:
|
|
64
|
+
1. IDENTITY — verify agent exists and is not revoked
|
|
65
|
+
2. AUTHORIZATION — verify agent has permission at correct level
|
|
66
|
+
3. SCOPE — verify action is within authorized scope
|
|
67
|
+
4. SHIELD — verify NRP shield rules (physical nodes only)
|
|
68
|
+
5. EXECUTE — run the action
|
|
69
|
+
6. PROVENANCE — record what was produced
|
|
70
|
+
7. AUDIT — append to tamper-evident chain
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
supervisor_keypair: AAPKeyPair,
|
|
76
|
+
supervisor_did: str,
|
|
77
|
+
audit_path: str | None = None,
|
|
78
|
+
) -> None:
|
|
79
|
+
self._supervisor_keypair = supervisor_keypair
|
|
80
|
+
self._supervisor_did = supervisor_did
|
|
81
|
+
self._identities: dict[str, AAPIdentity] = {}
|
|
82
|
+
self._nodes: dict[str, NRPNode] = {}
|
|
83
|
+
self._audit = AAPAuditChain(
|
|
84
|
+
storage_path=__import__("pathlib").Path(audit_path) if audit_path else None
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# ── Registration ──────────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
def register_identity(self, identity: AAPIdentity) -> None:
|
|
90
|
+
"""Register a known agent identity."""
|
|
91
|
+
self._identities[identity.id] = identity
|
|
92
|
+
|
|
93
|
+
def register_node(self, node: NRPNode) -> None:
|
|
94
|
+
"""Register a physical or digital node."""
|
|
95
|
+
self._nodes[node.id] = node
|
|
96
|
+
|
|
97
|
+
def grant_authorization(
|
|
98
|
+
self,
|
|
99
|
+
agent_id: str,
|
|
100
|
+
level: AuthorizationLevel,
|
|
101
|
+
scope: list[str],
|
|
102
|
+
physical: bool = False,
|
|
103
|
+
expires_in_seconds: int | None = None,
|
|
104
|
+
) -> AAPAuthorization:
|
|
105
|
+
"""
|
|
106
|
+
Grant authorization to a registered agent.
|
|
107
|
+
Enforces Physical World Rule automatically.
|
|
108
|
+
"""
|
|
109
|
+
expires_at = None
|
|
110
|
+
if expires_in_seconds is not None:
|
|
111
|
+
from datetime import timedelta
|
|
112
|
+
expires_at = datetime.now(timezone.utc) + timedelta(seconds=expires_in_seconds)
|
|
113
|
+
|
|
114
|
+
return AAPAuthorization.create(
|
|
115
|
+
agent_id=agent_id,
|
|
116
|
+
level=level,
|
|
117
|
+
scope=scope,
|
|
118
|
+
supervisor_keypair=self._supervisor_keypair,
|
|
119
|
+
supervisor_did=self._supervisor_did,
|
|
120
|
+
physical=physical,
|
|
121
|
+
expires_at=expires_at,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# ── Execution ──────────────────────────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
def execute(
|
|
127
|
+
self,
|
|
128
|
+
ctx: ExecutionContext,
|
|
129
|
+
node_id: str,
|
|
130
|
+
action: str,
|
|
131
|
+
params: dict[str, Any],
|
|
132
|
+
input_description: str = "",
|
|
133
|
+
) -> ExecutionResult:
|
|
134
|
+
"""
|
|
135
|
+
Execute an action through the full AAP pipeline.
|
|
136
|
+
Every step is recorded. Nothing is skipped.
|
|
137
|
+
"""
|
|
138
|
+
# 1. Identity + Authorization check
|
|
139
|
+
try:
|
|
140
|
+
ctx.check()
|
|
141
|
+
except (AAPRevocationError, Exception) as e:
|
|
142
|
+
return self._blocked(ctx.agent_id, action, node_id, str(e))
|
|
143
|
+
|
|
144
|
+
# 2. Scope check
|
|
145
|
+
if not ctx.identity.allows_action(action):
|
|
146
|
+
return self._blocked(
|
|
147
|
+
ctx.agent_id, action, node_id,
|
|
148
|
+
f"Action '{action}' not in agent scope: {ctx.identity.scope}"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# 3. Get node
|
|
152
|
+
node = self._nodes.get(node_id)
|
|
153
|
+
if node is None:
|
|
154
|
+
return self._blocked(ctx.agent_id, action, node_id, f"Node {node_id!r} not registered")
|
|
155
|
+
|
|
156
|
+
# 4. Shield check + Execute
|
|
157
|
+
try:
|
|
158
|
+
node_result = node.execute(action, params)
|
|
159
|
+
except ShieldViolation as e:
|
|
160
|
+
# Shield blocked — still record to audit
|
|
161
|
+
prov = AAPProvenance.create(
|
|
162
|
+
agent_id=ctx.agent_id,
|
|
163
|
+
action=action,
|
|
164
|
+
input_data=str(params).encode(),
|
|
165
|
+
output_data=b"blocked",
|
|
166
|
+
authorization_id=ctx.authorization.session_id,
|
|
167
|
+
agent_keypair=ctx.agent_keypair,
|
|
168
|
+
target=node_id,
|
|
169
|
+
)
|
|
170
|
+
entry = self._audit.append(
|
|
171
|
+
agent_id=ctx.agent_id,
|
|
172
|
+
action=action,
|
|
173
|
+
result="blocked",
|
|
174
|
+
provenance_id=prov.artifact_id,
|
|
175
|
+
agent_keypair=ctx.agent_keypair,
|
|
176
|
+
authorization_level=int(ctx.auth_level),
|
|
177
|
+
physical=node.is_physical,
|
|
178
|
+
result_detail=str(e),
|
|
179
|
+
)
|
|
180
|
+
return ExecutionResult(
|
|
181
|
+
success=False, agent_id=ctx.agent_id, action=action,
|
|
182
|
+
node_id=node_id, output=None,
|
|
183
|
+
provenance_id=prov.artifact_id,
|
|
184
|
+
audit_entry_id=entry.entry_id,
|
|
185
|
+
blocked=True, block_reason=str(e),
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
# 5. Provenance
|
|
189
|
+
prov = AAPProvenance.create(
|
|
190
|
+
agent_id=ctx.agent_id,
|
|
191
|
+
action=action,
|
|
192
|
+
input_data=str(params).encode(),
|
|
193
|
+
output_data=str(node_result.output).encode(),
|
|
194
|
+
authorization_id=ctx.authorization.session_id,
|
|
195
|
+
agent_keypair=ctx.agent_keypair,
|
|
196
|
+
target=node_id,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# 6. Audit
|
|
200
|
+
result_str = "success" if node_result.success else "failure"
|
|
201
|
+
entry = self._audit.append(
|
|
202
|
+
agent_id=ctx.agent_id,
|
|
203
|
+
action=action,
|
|
204
|
+
result=result_str,
|
|
205
|
+
provenance_id=prov.artifact_id,
|
|
206
|
+
agent_keypair=ctx.agent_keypair,
|
|
207
|
+
authorization_level=int(ctx.auth_level),
|
|
208
|
+
physical=node.is_physical,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
return ExecutionResult(
|
|
212
|
+
success=node_result.success,
|
|
213
|
+
agent_id=ctx.agent_id,
|
|
214
|
+
action=action,
|
|
215
|
+
node_id=node_id,
|
|
216
|
+
output=node_result.output,
|
|
217
|
+
provenance_id=prov.artifact_id,
|
|
218
|
+
audit_entry_id=entry.entry_id,
|
|
219
|
+
detail=node_result.detail,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
def verify_audit(self) -> tuple[bool, int, str | None]:
|
|
223
|
+
"""Verify the audit chain integrity."""
|
|
224
|
+
return self._audit.verify()
|
|
225
|
+
|
|
226
|
+
def audit_entries(self) -> list[dict]:
|
|
227
|
+
"""Export full audit log."""
|
|
228
|
+
return self._audit.to_list()
|
|
229
|
+
|
|
230
|
+
# ── Internal ──────────────────────────────────────────────────────────────
|
|
231
|
+
|
|
232
|
+
def _blocked(self, agent_id, action, node_id, reason) -> ExecutionResult:
|
|
233
|
+
return ExecutionResult(
|
|
234
|
+
success=False, agent_id=agent_id, action=action,
|
|
235
|
+
node_id=node_id, output=None,
|
|
236
|
+
provenance_id=None, audit_entry_id=None,
|
|
237
|
+
blocked=True, block_reason=reason,
|
|
238
|
+
)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: halyn
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Halyn — AAP in action. AI agents that can prove what they did.
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://halyn.dev
|
|
7
|
+
Project-URL: Repository, https://github.com/ElmadaniS/halyn
|
|
8
|
+
Keywords: halyn,aap,agent,accountability,ai,safety
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: aap-protocol>=0.1.0
|
|
16
|
+
Requires-Dist: nrprotocol>=0.1.0
|
|
17
|
+
Provides-Extra: test
|
|
18
|
+
Requires-Dist: pytest>=8.0; extra == "test"
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
<div align="center">
|
|
22
|
+
|
|
23
|
+
# Halyn
|
|
24
|
+
|
|
25
|
+
**AAP in action. AI agents that can prove what they did.**
|
|
26
|
+
|
|
27
|
+
[](https://pypi.org/project/halyn/)
|
|
28
|
+
[](LICENSE)
|
|
29
|
+
[](tests/)
|
|
30
|
+
|
|
31
|
+
The reference implementation of [AAP — Agent Accountability Protocol](https://aap-protocol.dev)
|
|
32
|
+
|
|
33
|
+
[Website](https://halyn.dev) · [AAP Spec](https://aap-protocol.dev) · [NRP](https://nrprotocol.dev)
|
|
34
|
+
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## What is Halyn?
|
|
40
|
+
|
|
41
|
+
Halyn is the complete demonstration of [AAP](https://aap-protocol.dev).
|
|
42
|
+
|
|
43
|
+
Every agent action goes through 7 steps — none skippable:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
Identity → Authorization → Scope → Shield → Execute → Provenance → Audit
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from halyn import HalynControlPlane, ExecutionContext
|
|
51
|
+
from aap import AAPKeyPair, AAPIdentity, AAPAuthorization, AuthorizationLevel
|
|
52
|
+
|
|
53
|
+
# Setup
|
|
54
|
+
supervisor = AAPKeyPair.generate()
|
|
55
|
+
cp = HalynControlPlane(supervisor_keypair=supervisor, supervisor_did="did:key:z6Mk...")
|
|
56
|
+
|
|
57
|
+
# Register agent + node
|
|
58
|
+
cp.register_identity(identity)
|
|
59
|
+
cp.register_node(node)
|
|
60
|
+
auth = cp.grant_authorization(agent_id=..., level=AuthorizationLevel.SUPERVISED, scope=["write:file"])
|
|
61
|
+
|
|
62
|
+
# Execute — full AAP pipeline, fully audited
|
|
63
|
+
ctx = ExecutionContext(identity=identity, authorization=auth, agent_keypair=agent_kp)
|
|
64
|
+
result = cp.execute(ctx, node_id="nrp://acme/server/files-1", action="write:file", params={...})
|
|
65
|
+
|
|
66
|
+
# Verify audit chain integrity
|
|
67
|
+
valid, count, broken = cp.verify_audit()
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## AAP Ecosystem
|
|
71
|
+
|
|
72
|
+
| Layer | Project | Role |
|
|
73
|
+
|-------|---------|------|
|
|
74
|
+
| Spec | [AAP](https://aap-protocol.dev) | Protocol definition |
|
|
75
|
+
| Physical | [NRP](https://nrprotocol.dev) | AAP for robots, IoT, machines |
|
|
76
|
+
| Full system | **Halyn** (this) | Complete demonstration |
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
MIT
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "halyn"
|
|
7
|
+
version = "2.0.0"
|
|
8
|
+
description = "Halyn — AAP in action. AI agents that can prove what they did."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
dependencies = ["aap-protocol>=0.1.0", "nrprotocol>=0.1.0"]
|
|
13
|
+
keywords = ["halyn", "aap", "agent", "accountability", "ai", "safety"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
]
|
|
19
|
+
[project.urls]
|
|
20
|
+
Homepage = "https://halyn.dev"
|
|
21
|
+
Repository = "https://github.com/ElmadaniS/halyn"
|
|
22
|
+
|
|
23
|
+
[project.optional-dependencies]
|
|
24
|
+
test = ["pytest>=8.0"]
|
|
25
|
+
|
|
26
|
+
[tool.pytest.ini_options]
|
|
27
|
+
testpaths = ["tests"]
|
|
28
|
+
addopts = "-v --tb=short"
|
|
29
|
+
|
|
30
|
+
[tool.setuptools.packages.find]
|
|
31
|
+
where = ["."]
|
|
32
|
+
include = ["halyn*"]
|