tbn-agent-network 0.1.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.
- tbn_agent_network-0.1.0/LICENSE +19 -0
- tbn_agent_network-0.1.0/MANIFEST.in +3 -0
- tbn_agent_network-0.1.0/PKG-INFO +131 -0
- tbn_agent_network-0.1.0/README.md +77 -0
- tbn_agent_network-0.1.0/auth.py +58 -0
- tbn_agent_network-0.1.0/database.py +309 -0
- tbn_agent_network-0.1.0/envelope.py +216 -0
- tbn_agent_network-0.1.0/eu_compliance.py +473 -0
- tbn_agent_network-0.1.0/governance.py +417 -0
- tbn_agent_network-0.1.0/multinode.py +302 -0
- tbn_agent_network-0.1.0/network.py +287 -0
- tbn_agent_network-0.1.0/requirements.txt +5 -0
- tbn_agent_network-0.1.0/sdk.py +231 -0
- tbn_agent_network-0.1.0/setup.cfg +4 -0
- tbn_agent_network-0.1.0/setup.py +68 -0
- tbn_agent_network-0.1.0/tbn_agent_network.egg-info/PKG-INFO +131 -0
- tbn_agent_network-0.1.0/tbn_agent_network.egg-info/SOURCES.txt +18 -0
- tbn_agent_network-0.1.0/tbn_agent_network.egg-info/dependency_links.txt +1 -0
- tbn_agent_network-0.1.0/tbn_agent_network.egg-info/requires.txt +10 -0
- tbn_agent_network-0.1.0/tbn_agent_network.egg-info/top_level.txt +8 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
GNU AFFERO GENERAL PUBLIC LICENSE
|
|
2
|
+
Version 3, 19 November 2007
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2026 Hardin Enterprises Ltd (trading as Hardin AI Solutions)
|
|
5
|
+
|
|
6
|
+
This program is free software: you can redistribute it and/or modify
|
|
7
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
8
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
(at your option) any later version.
|
|
10
|
+
|
|
11
|
+
This program is distributed in the hope that it will be useful,
|
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
GNU Affero General Public License for more details.
|
|
15
|
+
|
|
16
|
+
You should have received a copy of the GNU Affero General Public License
|
|
17
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
18
|
+
|
|
19
|
+
For commercial licensing, contact: burhan@hardinai.co.uk
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tbn-agent-network
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Trust-verified communication network for AI agents. The governed internet for AI.
|
|
5
|
+
Home-page: https://github.com/burhanyanbolu-design/project-hardin
|
|
6
|
+
Author: Burhan Yanbolu
|
|
7
|
+
Author-email: burhan@hardinai.co.uk
|
|
8
|
+
License: AGPL-3.0
|
|
9
|
+
Project-URL: Homepage, https://net.hardinai.co.uk
|
|
10
|
+
Project-URL: TBN Protocol, https://tbn.hardinai.co.uk
|
|
11
|
+
Project-URL: Documentation, https://github.com/burhanyanbolu-design/project-hardin
|
|
12
|
+
Project-URL: Bug Tracker, https://github.com/burhanyanbolu-design/project-hardin/issues
|
|
13
|
+
Keywords: ai,agents,trust,verification,governance,network,communication,cryptography,eu-ai-act,compliance,certification,identity,tbn
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: License :: OSI Approved :: GNU Affero General Public License v3
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Topic :: Security
|
|
26
|
+
Classifier: Topic :: Security :: Cryptography
|
|
27
|
+
Classifier: Topic :: System :: Networking
|
|
28
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
29
|
+
Requires-Python: >=3.9
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Requires-Dist: requests>=2.28.0
|
|
33
|
+
Requires-Dist: cryptography>=41.0.0
|
|
34
|
+
Requires-Dist: flask>=3.0.0
|
|
35
|
+
Requires-Dist: tbn-protocol>=0.1.0
|
|
36
|
+
Provides-Extra: server
|
|
37
|
+
Requires-Dist: gunicorn>=21.0.0; extra == "server"
|
|
38
|
+
Provides-Extra: multinode
|
|
39
|
+
Requires-Dist: redis>=5.0.0; extra == "multinode"
|
|
40
|
+
Dynamic: author
|
|
41
|
+
Dynamic: author-email
|
|
42
|
+
Dynamic: classifier
|
|
43
|
+
Dynamic: description
|
|
44
|
+
Dynamic: description-content-type
|
|
45
|
+
Dynamic: home-page
|
|
46
|
+
Dynamic: keywords
|
|
47
|
+
Dynamic: license
|
|
48
|
+
Dynamic: license-file
|
|
49
|
+
Dynamic: project-url
|
|
50
|
+
Dynamic: provides-extra
|
|
51
|
+
Dynamic: requires-dist
|
|
52
|
+
Dynamic: requires-python
|
|
53
|
+
Dynamic: summary
|
|
54
|
+
|
|
55
|
+
# TBN Agent Network — Trust-Verified Agent Communication
|
|
56
|
+
|
|
57
|
+
The missing trust layer for AI agent-to-agent communication.
|
|
58
|
+
|
|
59
|
+
## What is this?
|
|
60
|
+
|
|
61
|
+
TBN Agent Network is a communication protocol where AI agents can only exchange messages if both parties are TBN-verified. Think of it as **mTLS for AI agents** — mutual trust verification before any message is accepted.
|
|
62
|
+
|
|
63
|
+
Built on top of [TBN Protocol](https://github.com/burhanyanbolu-design/tbn-protocol) (the identity and certification layer for AI agents).
|
|
64
|
+
|
|
65
|
+
## How it works
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
Agent A TBN Agent Network Agent B
|
|
69
|
+
| | |
|
|
70
|
+
|-- "I want to send msg" ----->| |
|
|
71
|
+
| |-- Verify Agent A (TBN) ------>|
|
|
72
|
+
| |<-- A is VALID ----------------|
|
|
73
|
+
| |-- Verify Agent B (TBN) ------>|
|
|
74
|
+
| |<-- B is VALID ----------------|
|
|
75
|
+
| | |
|
|
76
|
+
| |-- Deliver signed envelope --->|
|
|
77
|
+
| | |
|
|
78
|
+
|<-- Delivery receipt ---------| |
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Every message carries a **TBN Verification Envelope**:
|
|
82
|
+
- Sender's TBN verification proof (cryptographically signed)
|
|
83
|
+
- Receiver's TBN verification proof
|
|
84
|
+
- Message payload (encrypted)
|
|
85
|
+
- Timestamp + nonce (replay protection)
|
|
86
|
+
- Network signature (proves the network verified both parties)
|
|
87
|
+
|
|
88
|
+
## Key Features
|
|
89
|
+
|
|
90
|
+
- **Mutual trust verification** — Both sender and receiver must be TBN-certified
|
|
91
|
+
- **Cryptographic envelopes** — Every message is signed and verifiable
|
|
92
|
+
- **Offline verification** — Recipients can verify envelopes without calling TBN again
|
|
93
|
+
- **Replay protection** — Nonce + timestamp prevents message replay attacks
|
|
94
|
+
- **Agent discovery** — Find certified agents by capability
|
|
95
|
+
- **Scalable** — Designed for thousands of concurrent agents
|
|
96
|
+
- **Uses live TBN** — Connects to tbn.hardinai.co.uk for real verification
|
|
97
|
+
|
|
98
|
+
## Architecture
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
┌─────────────────────────────────────────────────────┐
|
|
102
|
+
│ TBN Agent Network │
|
|
103
|
+
│ │
|
|
104
|
+
│ ┌──────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
105
|
+
│ │ Router │ │ Envelope │ │ Discovery │ │
|
|
106
|
+
│ │ Layer │ │ Signing │ │ Registry │ │
|
|
107
|
+
│ └────┬─────┘ └──────┬───────┘ └──────┬───────┘ │
|
|
108
|
+
│ │ │ │ │
|
|
109
|
+
│ ┌────┴───────────────┴──────────────────┴───────┐ │
|
|
110
|
+
│ │ TBN Verification Gate │ │
|
|
111
|
+
│ │ (calls tbn.hardinai.co.uk/api/verify) │ │
|
|
112
|
+
│ └────────────────────────────────────────────────┘ │
|
|
113
|
+
└─────────────────────────────────────────────────────┘
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Quick Start
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
pip install -r requirements.txt
|
|
120
|
+
python demo_network.py
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## License
|
|
124
|
+
|
|
125
|
+
AGPL-3.0 — Hardin Enterprises Ltd (trading as Hardin AI Solutions)
|
|
126
|
+
|
|
127
|
+
## Links
|
|
128
|
+
|
|
129
|
+
- TBN Protocol: https://tbn.hardinai.co.uk
|
|
130
|
+
- GitHub: https://github.com/burhanyanbolu-design/tbn-protocol
|
|
131
|
+
- PyPI: https://pypi.org/project/tbn-protocol/
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# TBN Agent Network — Trust-Verified Agent Communication
|
|
2
|
+
|
|
3
|
+
The missing trust layer for AI agent-to-agent communication.
|
|
4
|
+
|
|
5
|
+
## What is this?
|
|
6
|
+
|
|
7
|
+
TBN Agent Network is a communication protocol where AI agents can only exchange messages if both parties are TBN-verified. Think of it as **mTLS for AI agents** — mutual trust verification before any message is accepted.
|
|
8
|
+
|
|
9
|
+
Built on top of [TBN Protocol](https://github.com/burhanyanbolu-design/tbn-protocol) (the identity and certification layer for AI agents).
|
|
10
|
+
|
|
11
|
+
## How it works
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
Agent A TBN Agent Network Agent B
|
|
15
|
+
| | |
|
|
16
|
+
|-- "I want to send msg" ----->| |
|
|
17
|
+
| |-- Verify Agent A (TBN) ------>|
|
|
18
|
+
| |<-- A is VALID ----------------|
|
|
19
|
+
| |-- Verify Agent B (TBN) ------>|
|
|
20
|
+
| |<-- B is VALID ----------------|
|
|
21
|
+
| | |
|
|
22
|
+
| |-- Deliver signed envelope --->|
|
|
23
|
+
| | |
|
|
24
|
+
|<-- Delivery receipt ---------| |
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Every message carries a **TBN Verification Envelope**:
|
|
28
|
+
- Sender's TBN verification proof (cryptographically signed)
|
|
29
|
+
- Receiver's TBN verification proof
|
|
30
|
+
- Message payload (encrypted)
|
|
31
|
+
- Timestamp + nonce (replay protection)
|
|
32
|
+
- Network signature (proves the network verified both parties)
|
|
33
|
+
|
|
34
|
+
## Key Features
|
|
35
|
+
|
|
36
|
+
- **Mutual trust verification** — Both sender and receiver must be TBN-certified
|
|
37
|
+
- **Cryptographic envelopes** — Every message is signed and verifiable
|
|
38
|
+
- **Offline verification** — Recipients can verify envelopes without calling TBN again
|
|
39
|
+
- **Replay protection** — Nonce + timestamp prevents message replay attacks
|
|
40
|
+
- **Agent discovery** — Find certified agents by capability
|
|
41
|
+
- **Scalable** — Designed for thousands of concurrent agents
|
|
42
|
+
- **Uses live TBN** — Connects to tbn.hardinai.co.uk for real verification
|
|
43
|
+
|
|
44
|
+
## Architecture
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
┌─────────────────────────────────────────────────────┐
|
|
48
|
+
│ TBN Agent Network │
|
|
49
|
+
│ │
|
|
50
|
+
│ ┌──────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
51
|
+
│ │ Router │ │ Envelope │ │ Discovery │ │
|
|
52
|
+
│ │ Layer │ │ Signing │ │ Registry │ │
|
|
53
|
+
│ └────┬─────┘ └──────┬───────┘ └──────┬───────┘ │
|
|
54
|
+
│ │ │ │ │
|
|
55
|
+
│ ┌────┴───────────────┴──────────────────┴───────┐ │
|
|
56
|
+
│ │ TBN Verification Gate │ │
|
|
57
|
+
│ │ (calls tbn.hardinai.co.uk/api/verify) │ │
|
|
58
|
+
│ └────────────────────────────────────────────────┘ │
|
|
59
|
+
└─────────────────────────────────────────────────────┘
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install -r requirements.txt
|
|
66
|
+
python demo_network.py
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
AGPL-3.0 — Hardin Enterprises Ltd (trading as Hardin AI Solutions)
|
|
72
|
+
|
|
73
|
+
## Links
|
|
74
|
+
|
|
75
|
+
- TBN Protocol: https://tbn.hardinai.co.uk
|
|
76
|
+
- GitHub: https://github.com/burhanyanbolu-design/tbn-protocol
|
|
77
|
+
- PyPI: https://pypi.org/project/tbn-protocol/
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TBN Agent Network — Authentication & Authorization
|
|
3
|
+
|
|
4
|
+
API key authentication for network access.
|
|
5
|
+
Every request must include a valid key.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
Header: Authorization: Bearer tbn_net_xxxx
|
|
9
|
+
Or query param: ?api_key=tbn_net_xxxx
|
|
10
|
+
|
|
11
|
+
(c) 2026 Hardin Enterprises Ltd. AGPL-3.0
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from functools import wraps
|
|
15
|
+
from flask import request, jsonify
|
|
16
|
+
from database import validate_key, log_event
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def require_network_key(f):
|
|
20
|
+
"""Decorator: require a valid TBN Agent Network API key."""
|
|
21
|
+
@wraps(f)
|
|
22
|
+
def decorated(*args, **kwargs):
|
|
23
|
+
# Check Authorization header
|
|
24
|
+
auth_header = request.headers.get("Authorization", "")
|
|
25
|
+
if auth_header.startswith("Bearer "):
|
|
26
|
+
key = auth_header[7:]
|
|
27
|
+
else:
|
|
28
|
+
# Check query param
|
|
29
|
+
key = request.args.get("api_key") or (request.get_json() or {}).get("api_key")
|
|
30
|
+
|
|
31
|
+
if not key:
|
|
32
|
+
return jsonify({
|
|
33
|
+
"error": "API key required",
|
|
34
|
+
"detail": "Include 'Authorization: Bearer tbn_net_xxxx' header or 'api_key' parameter",
|
|
35
|
+
"docs": "https://tbn.hardinai.co.uk/docs"
|
|
36
|
+
}), 401
|
|
37
|
+
|
|
38
|
+
# Validate key
|
|
39
|
+
key_record = validate_key(key)
|
|
40
|
+
if not key_record:
|
|
41
|
+
log_event("AUTH_FAILED", detail=f"Invalid key attempt: {key[:12]}...")
|
|
42
|
+
return jsonify({
|
|
43
|
+
"error": "Invalid API key",
|
|
44
|
+
"detail": "Key not found or deactivated"
|
|
45
|
+
}), 403
|
|
46
|
+
|
|
47
|
+
# Attach key info to request
|
|
48
|
+
request.network_key = key_record
|
|
49
|
+
return f(*args, **kwargs)
|
|
50
|
+
|
|
51
|
+
return decorated
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def check_permission(key_record, permission):
|
|
55
|
+
"""Check if a key has a specific permission."""
|
|
56
|
+
import json
|
|
57
|
+
permissions = json.loads(key_record.get("permissions", "[]"))
|
|
58
|
+
return permission in permissions or "admin" in permissions
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TBN Agent Network — Database Persistence Layer
|
|
3
|
+
|
|
4
|
+
SQLite-backed storage for agents, messages, governance decisions,
|
|
5
|
+
and compliance records. Survives restarts.
|
|
6
|
+
|
|
7
|
+
(c) 2026 Hardin Enterprises Ltd. AGPL-3.0
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import sqlite3
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
from datetime import datetime, timezone
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
DB_PATH = os.environ.get("TBN_NET_DB", "data/tbn_network.db")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_db():
|
|
20
|
+
"""Get database connection."""
|
|
21
|
+
os.makedirs(os.path.dirname(DB_PATH) or ".", exist_ok=True)
|
|
22
|
+
conn = sqlite3.connect(DB_PATH)
|
|
23
|
+
conn.row_factory = sqlite3.Row
|
|
24
|
+
conn.execute("PRAGMA journal_mode=WAL")
|
|
25
|
+
conn.execute("PRAGMA foreign_keys=ON")
|
|
26
|
+
return conn
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def init_db():
|
|
30
|
+
"""Initialize database tables."""
|
|
31
|
+
conn = get_db()
|
|
32
|
+
conn.executescript("""
|
|
33
|
+
-- Registered agents
|
|
34
|
+
CREATE TABLE IF NOT EXISTS agents (
|
|
35
|
+
agent_id TEXT PRIMARY KEY,
|
|
36
|
+
name TEXT NOT NULL,
|
|
37
|
+
capabilities TEXT DEFAULT '[]',
|
|
38
|
+
owner_id TEXT,
|
|
39
|
+
owner_name TEXT,
|
|
40
|
+
owner_email TEXT,
|
|
41
|
+
status TEXT DEFAULT 'active',
|
|
42
|
+
connected_at TEXT NOT NULL,
|
|
43
|
+
last_seen TEXT,
|
|
44
|
+
metadata TEXT DEFAULT '{}'
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
-- Message envelopes (audit trail)
|
|
48
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
49
|
+
envelope_id TEXT PRIMARY KEY,
|
|
50
|
+
sender_id TEXT NOT NULL,
|
|
51
|
+
receiver_id TEXT NOT NULL,
|
|
52
|
+
content_hash TEXT NOT NULL,
|
|
53
|
+
content_type TEXT DEFAULT 'application/json',
|
|
54
|
+
sender_status TEXT,
|
|
55
|
+
receiver_status TEXT,
|
|
56
|
+
signature TEXT,
|
|
57
|
+
delivered INTEGER DEFAULT 0,
|
|
58
|
+
blocked_reason TEXT,
|
|
59
|
+
timestamp TEXT NOT NULL,
|
|
60
|
+
latency_ms REAL
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
-- Governance decisions
|
|
64
|
+
CREATE TABLE IF NOT EXISTS governance_decisions (
|
|
65
|
+
receipt_id TEXT PRIMARY KEY,
|
|
66
|
+
agent_id TEXT NOT NULL,
|
|
67
|
+
action_class TEXT NOT NULL,
|
|
68
|
+
decision TEXT NOT NULL,
|
|
69
|
+
reason TEXT,
|
|
70
|
+
message_id TEXT,
|
|
71
|
+
human_approver TEXT,
|
|
72
|
+
decision_hash TEXT NOT NULL,
|
|
73
|
+
timestamp TEXT NOT NULL
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
-- Agent authorities
|
|
77
|
+
CREATE TABLE IF NOT EXISTS authorities (
|
|
78
|
+
agent_id TEXT PRIMARY KEY,
|
|
79
|
+
owner_id TEXT NOT NULL,
|
|
80
|
+
owner_name TEXT NOT NULL,
|
|
81
|
+
owner_email TEXT NOT NULL,
|
|
82
|
+
permitted_actions TEXT DEFAULT '[]',
|
|
83
|
+
max_risk_level TEXT DEFAULT 'medium',
|
|
84
|
+
daily_action_limit INTEGER DEFAULT 1000,
|
|
85
|
+
requires_approval TEXT DEFAULT '[]',
|
|
86
|
+
created_at TEXT NOT NULL
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
-- EU compliance records
|
|
90
|
+
CREATE TABLE IF NOT EXISTS compliance_records (
|
|
91
|
+
agent_id TEXT PRIMARY KEY,
|
|
92
|
+
risk_category TEXT NOT NULL,
|
|
93
|
+
overall_status TEXT NOT NULL,
|
|
94
|
+
compliance_score REAL,
|
|
95
|
+
article_status TEXT DEFAULT '{}',
|
|
96
|
+
recommendations TEXT DEFAULT '[]',
|
|
97
|
+
assessed_at TEXT NOT NULL,
|
|
98
|
+
agent_info TEXT DEFAULT '{}'
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
-- Network events (real-time log)
|
|
102
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
103
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
104
|
+
event_type TEXT NOT NULL,
|
|
105
|
+
agent_id TEXT,
|
|
106
|
+
detail TEXT,
|
|
107
|
+
timestamp TEXT NOT NULL
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
-- API keys for network access
|
|
111
|
+
CREATE TABLE IF NOT EXISTS api_keys (
|
|
112
|
+
key_hash TEXT PRIMARY KEY,
|
|
113
|
+
key_prefix TEXT NOT NULL,
|
|
114
|
+
owner_name TEXT NOT NULL,
|
|
115
|
+
owner_email TEXT NOT NULL,
|
|
116
|
+
permissions TEXT DEFAULT '["connect","send","receive","discover"]',
|
|
117
|
+
rate_limit INTEGER DEFAULT 1000,
|
|
118
|
+
calls_today INTEGER DEFAULT 0,
|
|
119
|
+
last_reset TEXT,
|
|
120
|
+
created_at TEXT NOT NULL,
|
|
121
|
+
active INTEGER DEFAULT 1
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
-- Indexes for performance
|
|
125
|
+
CREATE INDEX IF NOT EXISTS idx_messages_sender ON messages(sender_id);
|
|
126
|
+
CREATE INDEX IF NOT EXISTS idx_messages_receiver ON messages(receiver_id);
|
|
127
|
+
CREATE INDEX IF NOT EXISTS idx_messages_timestamp ON messages(timestamp);
|
|
128
|
+
CREATE INDEX IF NOT EXISTS idx_governance_agent ON governance_decisions(agent_id);
|
|
129
|
+
CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
|
|
130
|
+
""")
|
|
131
|
+
conn.commit()
|
|
132
|
+
conn.close()
|
|
133
|
+
print("[DB] Database initialized: " + DB_PATH)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# ── Agent Operations ──────────────────────────────────────────────────
|
|
137
|
+
|
|
138
|
+
def save_agent(agent_id, name, capabilities, owner_id=None, owner_name=None, owner_email=None):
|
|
139
|
+
conn = get_db()
|
|
140
|
+
conn.execute("""
|
|
141
|
+
INSERT OR REPLACE INTO agents (agent_id, name, capabilities, owner_id, owner_name, owner_email, status, connected_at, last_seen)
|
|
142
|
+
VALUES (?, ?, ?, ?, ?, ?, 'active', ?, ?)
|
|
143
|
+
""", (agent_id, name, json.dumps(capabilities), owner_id, owner_name, owner_email,
|
|
144
|
+
datetime.now(timezone.utc).isoformat(), datetime.now(timezone.utc).isoformat()))
|
|
145
|
+
conn.commit()
|
|
146
|
+
conn.close()
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def get_agent(agent_id):
|
|
150
|
+
conn = get_db()
|
|
151
|
+
row = conn.execute("SELECT * FROM agents WHERE agent_id = ?", (agent_id,)).fetchone()
|
|
152
|
+
conn.close()
|
|
153
|
+
return dict(row) if row else None
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def list_agents(status="active"):
|
|
157
|
+
conn = get_db()
|
|
158
|
+
rows = conn.execute("SELECT * FROM agents WHERE status = ?", (status,)).fetchall()
|
|
159
|
+
conn.close()
|
|
160
|
+
return [dict(r) for r in rows]
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def revoke_agent(agent_id):
|
|
164
|
+
conn = get_db()
|
|
165
|
+
conn.execute("UPDATE agents SET status = 'revoked' WHERE agent_id = ?", (agent_id,))
|
|
166
|
+
conn.commit()
|
|
167
|
+
conn.close()
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def reactivate_agent(agent_id):
|
|
171
|
+
conn = get_db()
|
|
172
|
+
conn.execute("UPDATE agents SET status = 'active' WHERE agent_id = ?", (agent_id,))
|
|
173
|
+
conn.commit()
|
|
174
|
+
conn.close()
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
# ── Message Operations ────────────────────────────────────────────────
|
|
178
|
+
|
|
179
|
+
def save_message(envelope_id, sender_id, receiver_id, content_hash, content_type,
|
|
180
|
+
sender_status, receiver_status, signature, delivered, blocked_reason=None, latency_ms=None):
|
|
181
|
+
conn = get_db()
|
|
182
|
+
conn.execute("""
|
|
183
|
+
INSERT INTO messages (envelope_id, sender_id, receiver_id, content_hash, content_type,
|
|
184
|
+
sender_status, receiver_status, signature, delivered, blocked_reason, timestamp, latency_ms)
|
|
185
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
186
|
+
""", (envelope_id, sender_id, receiver_id, content_hash, content_type,
|
|
187
|
+
sender_status, receiver_status, signature, 1 if delivered else 0,
|
|
188
|
+
blocked_reason, datetime.now(timezone.utc).isoformat(), latency_ms))
|
|
189
|
+
conn.commit()
|
|
190
|
+
conn.close()
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def get_messages(agent_id=None, limit=100):
|
|
194
|
+
conn = get_db()
|
|
195
|
+
if agent_id:
|
|
196
|
+
rows = conn.execute(
|
|
197
|
+
"SELECT * FROM messages WHERE sender_id = ? OR receiver_id = ? ORDER BY timestamp DESC LIMIT ?",
|
|
198
|
+
(agent_id, agent_id, limit)).fetchall()
|
|
199
|
+
else:
|
|
200
|
+
rows = conn.execute("SELECT * FROM messages ORDER BY timestamp DESC LIMIT ?", (limit,)).fetchall()
|
|
201
|
+
conn.close()
|
|
202
|
+
return [dict(r) for r in rows]
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def get_message_stats():
|
|
206
|
+
conn = get_db()
|
|
207
|
+
total = conn.execute("SELECT COUNT(*) as c FROM messages").fetchone()["c"]
|
|
208
|
+
delivered = conn.execute("SELECT COUNT(*) as c FROM messages WHERE delivered = 1").fetchone()["c"]
|
|
209
|
+
blocked = conn.execute("SELECT COUNT(*) as c FROM messages WHERE delivered = 0").fetchone()["c"]
|
|
210
|
+
conn.close()
|
|
211
|
+
return {"total": total, "delivered": delivered, "blocked": blocked}
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
# ── Governance Operations ─────────────────────────────────────────────
|
|
215
|
+
|
|
216
|
+
def save_governance_decision(receipt_id, agent_id, action_class, decision, reason,
|
|
217
|
+
decision_hash, message_id=None, human_approver=None):
|
|
218
|
+
conn = get_db()
|
|
219
|
+
conn.execute("""
|
|
220
|
+
INSERT INTO governance_decisions (receipt_id, agent_id, action_class, decision, reason,
|
|
221
|
+
message_id, human_approver, decision_hash, timestamp)
|
|
222
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
223
|
+
""", (receipt_id, agent_id, action_class, decision, reason, message_id,
|
|
224
|
+
human_approver, decision_hash, datetime.now(timezone.utc).isoformat()))
|
|
225
|
+
conn.commit()
|
|
226
|
+
conn.close()
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def get_governance_trail(agent_id=None, limit=100):
|
|
230
|
+
conn = get_db()
|
|
231
|
+
if agent_id:
|
|
232
|
+
rows = conn.execute(
|
|
233
|
+
"SELECT * FROM governance_decisions WHERE agent_id = ? ORDER BY timestamp DESC LIMIT ?",
|
|
234
|
+
(agent_id, limit)).fetchall()
|
|
235
|
+
else:
|
|
236
|
+
rows = conn.execute("SELECT * FROM governance_decisions ORDER BY timestamp DESC LIMIT ?", (limit,)).fetchall()
|
|
237
|
+
conn.close()
|
|
238
|
+
return [dict(r) for r in rows]
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
# ── Event Log ─────────────────────────────────────────────────────────
|
|
242
|
+
|
|
243
|
+
def log_event(event_type, agent_id=None, detail=None):
|
|
244
|
+
conn = get_db()
|
|
245
|
+
conn.execute("""
|
|
246
|
+
INSERT INTO events (event_type, agent_id, detail, timestamp)
|
|
247
|
+
VALUES (?, ?, ?, ?)
|
|
248
|
+
""", (event_type, agent_id, detail, datetime.now(timezone.utc).isoformat()))
|
|
249
|
+
conn.commit()
|
|
250
|
+
conn.close()
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def get_events(limit=100):
|
|
254
|
+
conn = get_db()
|
|
255
|
+
rows = conn.execute("SELECT * FROM events ORDER BY timestamp DESC LIMIT ?", (limit,)).fetchall()
|
|
256
|
+
conn.close()
|
|
257
|
+
return [dict(r) for r in rows]
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
# ── API Key Operations ────────────────────────────────────────────────
|
|
261
|
+
|
|
262
|
+
import hashlib
|
|
263
|
+
import secrets
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def generate_network_key(owner_name, owner_email, permissions=None):
|
|
267
|
+
"""Generate a new API key for network access."""
|
|
268
|
+
raw_key = f"tbn_net_{secrets.token_hex(16)}"
|
|
269
|
+
key_hash = hashlib.sha256(raw_key.encode()).hexdigest()
|
|
270
|
+
key_prefix = raw_key[:12]
|
|
271
|
+
|
|
272
|
+
conn = get_db()
|
|
273
|
+
conn.execute("""
|
|
274
|
+
INSERT INTO api_keys (key_hash, key_prefix, owner_name, owner_email, permissions, created_at)
|
|
275
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
276
|
+
""", (key_hash, key_prefix, owner_name, owner_email,
|
|
277
|
+
json.dumps(permissions or ["connect", "send", "receive", "discover"]),
|
|
278
|
+
datetime.now(timezone.utc).isoformat()))
|
|
279
|
+
conn.commit()
|
|
280
|
+
conn.close()
|
|
281
|
+
|
|
282
|
+
return raw_key
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def validate_key(raw_key):
|
|
286
|
+
"""Validate an API key. Returns key record or None."""
|
|
287
|
+
if not raw_key or not raw_key.startswith("tbn_net_"):
|
|
288
|
+
return None
|
|
289
|
+
|
|
290
|
+
key_hash = hashlib.sha256(raw_key.encode()).hexdigest()
|
|
291
|
+
conn = get_db()
|
|
292
|
+
row = conn.execute("SELECT * FROM api_keys WHERE key_hash = ? AND active = 1", (key_hash,)).fetchone()
|
|
293
|
+
conn.close()
|
|
294
|
+
|
|
295
|
+
if row:
|
|
296
|
+
return dict(row)
|
|
297
|
+
return None
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def list_keys():
|
|
301
|
+
"""List all API keys (prefix only, not full key)."""
|
|
302
|
+
conn = get_db()
|
|
303
|
+
rows = conn.execute("SELECT key_prefix, owner_name, owner_email, active, created_at FROM api_keys").fetchall()
|
|
304
|
+
conn.close()
|
|
305
|
+
return [dict(r) for r in rows]
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
# Initialize on import
|
|
309
|
+
init_db()
|