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.
@@ -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,3 @@
1
+ include README.md
2
+ include LICENSE
3
+ include requirements.txt
@@ -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()