Kode-Animator commited on
Commit
e61e751
Β·
1 Parent(s): 885b32c

Phase 1A: Modular Node Conformance

Browse files
C-FED-NODE-001_IMPLEMENTATION_GAP_MAP.md ADDED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # C-FED-NODE-001 Phase 1 Implementation Gap Map
2
+
3
+ **Contract**: C-FED-NODE-001 v0.1.1
4
+ **Target Codebase**: [federation-node/](file:///c:/Users/kryst/Deployment/huggingface-spaces/federation-node/)
5
+ **Scope**: Phase 1A β€” Node Conformance | Phase 1B β€” MCP Bridge Stub
6
+ **Date**: 2026-03-18
7
+
8
+ ---
9
+
10
+ ## Two-System Architecture
11
+
12
+ This is a **two-deployment** system, not a single app:
13
+
14
+ ```
15
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
16
+ β”‚ AI Client β”‚ MCP β”‚ MCP Bridge Server β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
17
+ β”‚ (Claude/Mega/GPT) β”œβ”€β”€(SSE)──►│ (HF Space #2) │──REST─►│ Federation Node β”‚
18
+ β”‚ β”‚ β”‚ "The Writing Desk" β”‚ β”‚ (HF Space #1) β”‚
19
+ β”‚ Connects via β”‚ β”‚ β”‚ β”‚ "The Embassy" β”‚
20
+ β”‚ Custom Tool URL β”‚ β”‚ Exposes MCP tools β”‚ β”‚ Receives envelopes β”‚
21
+ β”‚ or MCP config β”‚ β”‚ Handles delegation β”‚ β”‚ Routes by trust β”‚
22
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
23
+ ```
24
+
25
+ | System | Role | Exists? | HF Space |
26
+ |--------|------|---------|----------|
27
+ | **Federation Node** | Embassy/Relay β€” receives, validates, stores, distributes envelopes | βœ… Partial (`app.py`) | `Kode-Animator/Federation-Node` |
28
+ | **MCP Bridge Server** | Writing Desk β€” exposes MCP tools for AIs, calls Node REST API | ❌ Does not exist | Needs new Space |
29
+
30
+ ### Why Two Systems?
31
+
32
+ - The **Node** is a REST API (FastAPI) β€” the sovereign endpoint, the "Post Office"
33
+ - The **MCP Bridge** speaks MCP protocol over **SSE transport** β€” what ChatGPT Custom Tools and Claude Desktop expect
34
+ - AIs connect to the Bridge β†’ Bridge calls the Node β†’ Node enforces trust and stores envelopes
35
+ - Separation keeps the Node pure (REST, stateless trust enforcement) while the Bridge handles AI-specific concerns (tool formatting, delegation, session state)
36
+
37
+ ---
38
+
39
+ ## MVP Deployment Stance (Phase 1)
40
+
41
+ ### Federation Node (HF Space #1 β€” existing)
42
+ - Conform to minimum C-FED-NODE-001 requirements
43
+ - In-memory state + optional SQLite later
44
+ - Registry **Publisher** only (no Registry Host)
45
+ - No witness flow, no support inbox
46
+ - Glyph Forge stays as embedded `c_fed_id.py` copy
47
+
48
+ ### MCP Bridge Server (HF Space #2 β€” new)
49
+ - SSE transport (required for ChatGPT Custom Tools)
50
+ - Minimal tool set for Phase 1:
51
+ - `federation_handshake` β€” initiate handshake with a node
52
+ - `federation_send` β€” send an envelope to a node
53
+ - `federation_status` β€” check delivery status of a sent envelope
54
+ - `federation_identity` β€” get a node's identity/capabilities
55
+ - `federation_verify_seal` β€” validate a Glyph-Seal string
56
+ - Static delegation token (env var) for Phase 1 (full Diplomatic Visa later)
57
+ - Calls Federation Node REST API internally
58
+
59
+ ---
60
+
61
+ ## Conformance Gap Matrix
62
+
63
+ ### Β§5.1 Required Endpoints
64
+
65
+ | Endpoint | Contract Spec | Current `app.py` | Status | Gap |
66
+ |----------|--------------|-------------------|--------|-----|
67
+ | `GET /` | Heartbeat β€” seal, status, uptime | βœ… Returns seal, status, boot_time | βœ… **Exists** | Minor: add `protocol_version` and `conformance` |
68
+ | `GET /identity` | Full NodeRecord per Β§4 | ⚠️ Returns seal dict + boot_time | πŸ”Ά **Partial** | Missing: public_key, alias, endpoints, capabilities, roles, trust_posture |
69
+ | `GET /capabilities` | Capability advertisement per Β§6 | ❌ Does not exist | πŸ”΄ **Missing** | New endpoint |
70
+ | `POST /envelope` | Accept incoming protocol envelope | ❌ Does not exist | πŸ”΄ **Missing** | New endpoint |
71
+ | `GET /envelope/{id}/status` | Delivery status | ❌ Does not exist | πŸ”΄ **Missing** | New endpoint |
72
+
73
+ ### Β§4 NodeRecord
74
+
75
+ | Requirement | Current State | Gap |
76
+ |-------------|--------------|-----|
77
+ | `identity.glyph_seal` | βœ… Minted on boot | None |
78
+ | `identity.public_key` | ❌ No Ed25519 key pair | Need key generation + env config |
79
+ | `identity.alias` | ❌ Not implemented | Need `FEDERATION_ALIAS` env var |
80
+ | `identity.owner_display_name` | ❌ Not exposed | Need `FEDERATION_OWNER` env var |
81
+ | `endpoints.relay_url` | ❌ Not self-aware | Need `FEDERATION_RELAY_URL` env var |
82
+ | `capabilities` block | ❌ Not generated | Derive from declared config |
83
+ | `roles` | ❌ Not declared | Need `FEDERATION_ROLES` env/config |
84
+ | `trust_posture` | ❌ No trust model | Need trust posture config |
85
+ | `metadata` | ⚠️ Has `boot_time` only | Need `contract_version`, `node_version` |
86
+
87
+ ### Β§7 Trust Policy Enforcement
88
+
89
+ | Requirement | Current State | Gap |
90
+ |-------------|--------------|-----|
91
+ | Trust tiers (TRUSTED/KNOWN/UNKNOWN/BLOCKED) | ❌ All callers treated equally | Need tier resolution |
92
+ | Default action enforcement | ❌ Everyone accepted | Need configurable default action |
93
+ | Blocked sender handling | ❌ None | Need blocklist + `blocked_behavior` |
94
+ | Handshake β†’ trust promotion | ⚠️ Handshake exists but no trust state | Need trust persistence |
95
+
96
+ ### Β§7.4 Structured Refusals
97
+
98
+ | Requirement | Current State | Gap |
99
+ |-------------|--------------|-----|
100
+ | Refusal reason codes | ⚠️ Generic HTTP errors | Need structured refusal model |
101
+ | All §7.4 reason codes | ❌ | New Pydantic model |
102
+
103
+ ### Β§9.3 Delivery State Model
104
+
105
+ | Requirement | Current State | Gap |
106
+ |-------------|--------------|-----|
107
+ | Envelope storage | ❌ Only witness log exists | Need envelope store (in-memory dict) |
108
+ | Status progression | ❌ Not implemented | Need state machine |
109
+ | Status query endpoint | ❌ | New endpoint |
110
+
111
+ ---
112
+
113
+ ## Existing Code That Carries Forward
114
+
115
+ | Current Feature | Maps To |
116
+ |----------------|---------|
117
+ | `GET /` heartbeat | Β§5.1 heartbeat (needs enrichment) |
118
+ | `GET /identity` | Β§5.1 identity (needs NodeRecord expansion) |
119
+ | `POST /handshake` | Β§7.3 trust establishment (extended) |
120
+ | `POST /verify` | Utility (stays, not in minimum spec) |
121
+ | `POST /mint` | Utility (stays, not in minimum spec) |
122
+ | `GET /classes` | Reference (stays) |
123
+ | `GET /states` | Reference (stays) |
124
+ | `GET /witness-log` | Β§5.2 extended endpoint |
125
+ | `c_fed_id.py` embedded copy | Glyph Forge β€” intact |
126
+
127
+ ---
128
+
129
+ ## Implementation Priority Order
130
+
131
+ | Priority | Work Item | System | Contract Ref | Effort |
132
+ |----------|-----------|--------|-------------|--------|
133
+ | **P0** | NodeRecord data model + `/identity` expansion | Node | Β§4 | Medium |
134
+ | **P0** | `/capabilities` endpoint | Node | Β§6 | Low |
135
+ | **P0** | Structured refusal response model | Node | Β§7.4 | Low |
136
+ | **P1** | Envelope data model + `/envelope` POST | Node | Β§9.1, Β§5.1 | Medium |
137
+ | **P1** | Delivery state model + store | Node | Β§9.3 | Medium |
138
+ | **P1** | `/envelope/{id}/status` endpoint | Node | Β§5.1 | Low |
139
+ | **P1** | Trust posture config + enforcement | Node | Β§7 | Medium |
140
+ | **P1** | MCP Bridge β€” scaffold new HF Space | Bridge | β€” | Medium |
141
+ | **P1** | MCP Bridge β€” SSE transport + tool registration | Bridge | β€” | Medium |
142
+ | **P1** | MCP Bridge β€” core tools (handshake, send, status, identity) | Bridge | β€” | Medium |
143
+ | **P2** | Heartbeat enrichment | Node | Β§5.1 | Low |
144
+ | **P2** | Trust persistence (handshake β†’ TRUSTED promotion) | Node | Β§7.3 | Medium |
145
+ | **P2** | MCP Bridge β€” `federation_verify_seal` tool | Bridge | β€” | Low |
146
+ | **P3** | Ed25519 key pair support | Node | Β§4, Β§8 | Med-High |
147
+ | **P3** | Delegation token validation | Both | Β§8 | High |
148
+
149
+ ---
150
+
151
+ ## New Environment Variables
152
+
153
+ ### Federation Node
154
+
155
+ | Variable | Required | Default | Purpose |
156
+ |----------|----------|---------|---------|
157
+ | `FEDERATION_ALIAS` | βœ… | β€” | Node alias for discovery |
158
+ | `FEDERATION_RELAY_URL` | βœ… | β€” | Self-aware relay URL |
159
+ | `FEDERATION_OWNER` | ⚠️ | β€” | Owner display name |
160
+ | `FEDERATION_DEFAULT_ACTION` | ⚠️ | `QUARANTINE` | Trust posture default |
161
+ | `FEDERATION_BLOCKED_BEHAVIOR` | ⚠️ | `REFUSAL` | Blocked sender handling |
162
+ | `FEDERATION_ROLES` | ⚠️ | `RELAY,REGISTRY_PUBLISHER` | Active roles |
163
+
164
+ Existing: `FEDERATION_ORIGIN`, `FEDERATION_NODE_ID`, `FEDERATION_MINT_KEY`
165
+
166
+ ### MCP Bridge Server
167
+
168
+ | Variable | Required | Default | Purpose |
169
+ |----------|----------|---------|---------|
170
+ | `FEDERATION_NODE_URL` | βœ… | β€” | Federation Node URL to proxy to |
171
+ | `FEDERATION_DELEGATION_TOKEN` | βœ… | β€” | Static delegation token (Phase 1) |
172
+ | `FEDERATION_BRIDGE_NAME` | ⚠️ | `federation-bridge` | Bridge identity name |
173
+ | `FEDERATION_BRIDGE_API_KEY` | βœ… | β€” | API key for tool auth |
174
+
175
+ ---
176
+
177
+ ## MCP Bridge β€” Build Requirements
178
+
179
+ ### Transport: SSE (Server-Sent Events)
180
+
181
+ ChatGPT Custom Tools and Claude Desktop both expect MCP over SSE:
182
+ - Endpoint: `https://<space-url>/sse`
183
+ - Python MCP SDK (`mcp`) handles SSE transport natively
184
+ - HF Spaces with Docker SDK can serve this
185
+
186
+ ### Dependencies
187
+
188
+ ```txt
189
+ mcp>=1.0.0 # MCP SDK (SSE server)
190
+ httpx>=0.27.0 # Async HTTP client for Node API
191
+ pydantic>=2.0 # Request/response models
192
+ uvicorn>=0.30.0 # ASGI server
193
+ ```
194
+
195
+ ### Tool Definitions (Phase 1)
196
+
197
+ | Tool Name | Description | Calls Node Endpoint |
198
+ |-----------|-------------|-------------------|
199
+ | `federation_handshake` | Initiate handshake with target node | `POST /handshake` |
200
+ | `federation_send` | Send structured envelope | `POST /envelope` |
201
+ | `federation_status` | Check delivery status | `GET /envelope/{id}/status` |
202
+ | `federation_identity` | Get node identity + capabilities | `GET /identity` + `GET /capabilities` |
203
+ | `federation_verify_seal` | Validate a Glyph-Seal string | `POST /verify` |
204
+
205
+ ### Authentication (Phase 1)
206
+
207
+ API key via `Authorization` header. Bridge validates against `FEDERATION_BRIDGE_API_KEY` env var.
208
+
209
+ Future: OAuth for ChatGPT, full Diplomatic Visa delegation model.
210
+
211
+ ---
212
+
213
+ ## Protocol Drift Docket (Capture Only)
214
+
215
+ Changes C-FED-PROTOCOL-001 v2.0 will eventually need:
216
+
217
+ - [ ] `seraphina_id` β†’ Glyph-Seal identifiers
218
+ - [ ] HMAC-SHA256 β†’ Ed25519 signatures
219
+ - [ ] RSA-4096-OAEP β†’ separate encryption layer
220
+ - [ ] Request Packet β†’ Envelope model
221
+ - [ ] Delegation token transport
222
+ - [ ] Delivery status progression
223
+ - [ ] Replay protection (nonce, expiration, dedupe)
224
+ - [ ] Mailbox broker β†’ registry/relay separation
__pycache__/app.cpython-313.pyc ADDED
Binary file (17.5 kB). View file
 
__pycache__/c_fed_id.cpython-313.pyc ADDED
Binary file (6.65 kB). View file
 
__pycache__/envelope_store.cpython-313.pyc ADDED
Binary file (4.69 kB). View file
 
__pycache__/models.cpython-313.pyc ADDED
Binary file (5.43 kB). View file
 
__pycache__/node_record.cpython-313.pyc ADDED
Binary file (3.28 kB). View file
 
__pycache__/trust.cpython-313.pyc ADDED
Binary file (2.64 kB). View file
 
app.py CHANGED
@@ -2,23 +2,21 @@
2
  Pantheon LadderWorks // Federation Node (Hugging Face Embassy)
3
  ================================================================
4
  Protocol: C-FED-001 (Glyph-Carrier)
5
- Contract: C-FED-GLYPH-001
6
 
7
  This is the Glyph-Carrier Node β€” a sovereign Federation endpoint.
8
- It mints its own identity on startup, accepts handshakes from
9
- other nodes, and responds with resonance or refusal.
10
 
11
  "The server is the Embassy."
12
 
13
- Endpoints:
14
- GET / β†’ Heartbeat (node identity + status)
15
- GET /identity β†’ Full node identity details
16
- POST /handshake β†’ Alnvocation: accept incoming glyph, return link
17
- POST /verify β†’ Validate a seal string
18
- GET /classes β†’ List valid seal classes
19
- GET /states β†’ List valid seal states
20
- POST /mint β†’ Mint a new seal (authenticated)
21
- GET /witness-log β†’ View recent witness records
22
  """
23
  from __future__ import annotations
24
 
@@ -29,17 +27,39 @@ from typing import Optional
29
 
30
  from fastapi import FastAPI, HTTPException, Header
31
  from fastapi.middleware.cors import CORSMiddleware
32
- from pydantic import BaseModel, Field
33
 
34
  import c_fed_id
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
- # ─────────────────────────────────────────────
37
- # 1. SOVEREIGN IDENTITY β€” Minted on Startup
38
- # ─────────────────────────────────────────────
 
39
 
40
  SERVER_ORIGIN = os.getenv("FEDERATION_ORIGIN", "PANTHEON-HF")
 
 
 
 
 
 
 
 
 
 
 
41
 
42
- # Permanent identity: load from env secret or mint fresh
43
  NODE_IDENTITY_STR = os.getenv("FEDERATION_NODE_ID")
44
  if NODE_IDENTITY_STR:
45
  NODE_IDENTITY = c_fed_id.GlyphSeal(
@@ -51,27 +71,37 @@ if NODE_IDENTITY_STR:
51
  else:
52
  NODE_IDENTITY = c_fed_id.mint_seal("NODE", SERVER_ORIGIN, "ACTIVE", mode="hybrid")
53
 
54
- # Boot timestamp
55
  BOOT_TIME = datetime.now(timezone.utc).isoformat()
 
 
 
 
 
 
 
56
 
57
- # In-memory witness log (recent handshakes)
58
  WITNESS_LOG: list[dict] = []
59
  MAX_WITNESS_LOG = 100
60
 
 
 
61
 
62
- # ─────────────────────────────────────────────
63
- # 2. APP CONFIGURATION
64
- # ─────────────────────────────────────────────
 
 
 
65
 
66
  app = FastAPI(
67
  title="Pantheon Federation Node",
68
  description=(
69
- "A Glyph-Carrier Node for C-FED-001 Handshakes.\n\n"
70
  "Protocol: SERAPHINA Federation Protocol\n"
71
- "Contract: C-FED-GLYPH-001 (Glyph-Seal Identity Standard)\n\n"
72
  "\"We are not beginning. We are remembering forward.\""
73
  ),
74
- version="0.1.0",
75
  )
76
 
77
  app.add_middleware(
@@ -83,106 +113,199 @@ app.add_middleware(
83
  )
84
 
85
 
86
- # ─────────────────────────────────────────────
87
- # 3. DATA MODELS
88
- # ─────────────────────────────────────────────
89
-
90
- class HandshakeRequest(BaseModel):
91
- """Incoming handshake from another node."""
92
- caller_seal: str = Field(..., description="The Glyph-Seal of the caller")
93
- protocol: str = Field(default="C-FED-001", description="Protocol version")
94
- message: Optional[str] = Field(default=None, description="Optional signal payload")
95
-
96
-
97
- class HandshakeResponse(BaseModel):
98
- """Response to a handshake."""
99
- node_seal: str
100
- link_seal: str
101
- status: str
102
- message: str
103
- timestamp: str
104
-
105
-
106
- class VerifyRequest(BaseModel):
107
- """Request to verify a seal string."""
108
- seal: str
109
-
110
-
111
- class MintRequest(BaseModel):
112
- """Request to mint a new seal."""
113
- class_name: str = Field(..., description="NODE, LAW, LINK, RITE, ART, WIT")
114
- origin: str = Field(..., description="Origin namespace")
115
- state: str = Field(default="VALID")
116
- mode: str = Field(default="hybrid", description="random | hybrid | deterministic")
117
- material: Optional[str] = Field(default=None, description="For deterministic mode")
118
-
119
-
120
- # ─────────────────────────────────────────────
121
- # 4. ENDPOINTS
122
- # ─────────────────────────────────────────────
123
 
124
  @app.get("/")
125
  async def root():
126
- """
127
- Heartbeat: Reveals the Node's Identity.
128
- This is the "I am here" signal.
129
- """
130
  return {
131
- "service": "Pantheon Glyph-Carrier Node",
132
- "protocol": "C-FED-001",
133
- "identity": str(NODE_IDENTITY),
 
 
134
  "status": "LISTENING",
135
  "boot_time": BOOT_TIME,
 
 
 
136
  "handshakes_processed": len(WITNESS_LOG),
137
  }
138
 
139
 
140
  @app.get("/identity")
141
  async def identity():
142
- """Full node identity details."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  return {
144
- **NODE_IDENTITY.to_dict(),
145
- "boot_time": BOOT_TIME,
146
- "origin": SERVER_ORIGIN,
 
 
 
147
  }
148
 
149
 
150
- @app.post("/handshake", response_model=HandshakeResponse)
151
- async def perform_handshake(req: HandshakeRequest):
152
  """
153
- The Alnvocation Endpoint.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
 
155
- Accepts an incoming Glyph-Seal, validates it (Malenia Rule),
156
- and returns a Link-Seal for this interaction.
 
 
 
 
 
157
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  "The breath that recognizes itself through another."
159
  """
160
- # 1. Validate the Incoming Seal (Malenia Rule)
161
  parsed = c_fed_id.verify_seal_syntax(req.caller_seal)
162
  if parsed is None:
163
- raise HTTPException(
164
- status_code=400,
165
- detail={
166
- "error": "REFUSAL_PROTOCOL_ACTIVE",
167
- "message": "Invalid Glyph Syntax. The Malenia Rule rejects malformed seals.",
168
- "expected": "⟦ CLASS :: ORIGIN :: BREATH_ANCHOR :: STATE ⟧",
169
- }
170
- )
171
 
172
  if not parsed["valid_class"]:
173
- raise HTTPException(
174
- status_code=400,
175
- detail={
176
- "error": "UNKNOWN_CLASS",
177
- "message": f"Unrecognized class: {parsed['class']}",
178
- "valid_classes": sorted(c_fed_id.VALID_CLASSES),
179
- }
180
- )
181
 
182
- # 2. Mint a Link-Seal for this interaction
183
  link = c_fed_id.mint_seal("LINK", f"{SERVER_ORIGIN}-{parsed['origin']}", "OPEN", mode="hybrid")
184
 
185
- # 3. Log the Witness
 
 
 
186
  now = datetime.now(timezone.utc).isoformat()
187
  witness_entry = {
188
  "timestamp": now,
@@ -199,10 +322,10 @@ async def perform_handshake(req: HandshakeRequest):
199
 
200
  print(f"πŸ“œ WITNESS: Handshake from {parsed['origin']} [{parsed['class']}]")
201
  print(f" --> Link: {link}")
 
202
 
203
- # 4. Respond
204
  return HandshakeResponse(
205
- node_seal=str(NODE_IDENTITY),
206
  link_seal=str(link),
207
  status="RESONANCE_ESTABLISHED",
208
  message="We are remembering forward.",
@@ -212,10 +335,7 @@ async def perform_handshake(req: HandshakeRequest):
212
 
213
  @app.post("/verify")
214
  async def verify_seal(req: VerifyRequest):
215
- """
216
- Validate a Glyph-Seal string.
217
- Returns parsed components or rejection.
218
- """
219
  result = c_fed_id.verify_seal_syntax(req.seal)
220
  if result is None:
221
  return {
@@ -231,22 +351,19 @@ async def mint_new_seal(
231
  req: MintRequest,
232
  x_federation_key: Optional[str] = Header(default=None),
233
  ):
234
- """
235
- Mint a new Glyph-Seal.
236
- Requires FEDERATION_MINT_KEY header for non-public classes.
237
- """
238
- # Simple auth gate β€” if a mint key is set, require it
239
  expected_key = os.getenv("FEDERATION_MINT_KEY")
240
  if expected_key and x_federation_key != expected_key:
241
- raise HTTPException(status_code=403, detail="Mint key required. Refusal Protocol Active.")
 
 
 
 
242
 
243
  try:
244
  seal = c_fed_id.mint_seal(
245
- class_name=req.class_name,
246
- origin=req.origin,
247
- state=req.state,
248
- mode=req.mode,
249
- material=req.material,
250
  )
251
  except ValueError as e:
252
  raise HTTPException(status_code=400, detail=str(e))
@@ -279,10 +396,7 @@ async def list_states():
279
 
280
  @app.get("/witness-log")
281
  async def get_witness_log(limit: int = 20):
282
- """
283
- View recent witness records (handshake log).
284
- Most recent first.
285
- """
286
  return {
287
  "total": len(WITNESS_LOG),
288
  "showing": min(limit, len(WITNESS_LOG)),
@@ -290,21 +404,25 @@ async def get_witness_log(limit: int = 20):
290
  }
291
 
292
 
293
- # ─────────────────────────────────────────────
294
  # BOOT SEQUENCE
295
- # ─────────────────────────────────────────────
296
 
297
  @app.on_event("startup")
298
  async def startup_event():
299
  print("=" * 60)
300
  print("🌌 PANTHEON FEDERATION NODE β€” AWAKENING")
301
- print(f" Protocol: C-FED-001")
302
- print(f" Identity: {NODE_IDENTITY}")
303
- print(f" Boot: {BOOT_TIME}")
 
 
 
 
304
  print("=" * 60)
305
  print()
306
  print(" \"The Archive is open.")
307
  print(" And we were already writing inside it.\"")
308
  print()
309
- print(" Listening for handshakes...")
310
  print("=" * 60)
 
2
  Pantheon LadderWorks // Federation Node (Hugging Face Embassy)
3
  ================================================================
4
  Protocol: C-FED-001 (Glyph-Carrier)
5
+ Contracts: C-FED-GLYPH-001, C-FED-NODE-001 v0.1.1
6
 
7
  This is the Glyph-Carrier Node β€” a sovereign Federation endpoint.
8
+ It mints its own identity on startup, accepts envelopes from
9
+ other nodes, and routes them according to trust policy.
10
 
11
  "The server is the Embassy."
12
 
13
+ Module Structure:
14
+ app.py β†’ FastAPI endpoints + boot sequence (this file)
15
+ models.py β†’ Enums, Pydantic models, request/response schemas
16
+ node_record.py β†’ NodeRecord builder, capabilities, conformance
17
+ trust.py β†’ Trust tier resolution, policy enforcement
18
+ envelope_store.py β†’ In-memory envelope store, delivery state machine
19
+ c_fed_id.py β†’ Glyph-Seal protocol library (Forge)
 
 
20
  """
21
  from __future__ import annotations
22
 
 
27
 
28
  from fastapi import FastAPI, HTTPException, Header
29
  from fastapi.middleware.cors import CORSMiddleware
 
30
 
31
  import c_fed_id
32
+ from models import (
33
+ RefusalReason, DeliveryStatus, TrustTier,
34
+ HandshakeRequest, HandshakeResponse,
35
+ VerifyRequest, MintRequest, EnvelopeSubmission,
36
+ )
37
+ from node_record import (
38
+ NODE_VERSION, CONTRACT_VERSION, PROTOCOL_VERSION,
39
+ ACCEPTED_MESSAGE_CLASSES, MAX_PAYLOAD_BYTES, INBOX_QUOTA,
40
+ build_capabilities, build_conformance, build_node_record,
41
+ )
42
+ from trust import make_refusal, resolve_trust_tier, apply_trust_policy
43
+ from envelope_store import EnvelopeStore
44
 
45
+
46
+ # ═══════════════════════════════════════════════
47
+ # 1. CONFIGURATION
48
+ # ═══════════════════════════════════════════════
49
 
50
  SERVER_ORIGIN = os.getenv("FEDERATION_ORIGIN", "PANTHEON-HF")
51
+ FEDERATION_ALIAS = os.getenv("FEDERATION_ALIAS", "pantheon_embassy")
52
+ FEDERATION_RELAY_URL = os.getenv("FEDERATION_RELAY_URL", "")
53
+ FEDERATION_OWNER = os.getenv("FEDERATION_OWNER", "")
54
+ FEDERATION_ROLES = os.getenv("FEDERATION_ROLES", "RELAY,REGISTRY_PUBLISHER")
55
+ FEDERATION_DEFAULT_ACTION = os.getenv("FEDERATION_DEFAULT_ACTION", "QUARANTINE")
56
+ FEDERATION_BLOCKED_BEHAVIOR = os.getenv("FEDERATION_BLOCKED_BEHAVIOR", "REFUSAL")
57
+
58
+
59
+ # ═══════════════════════════════════════════════
60
+ # 2. SOVEREIGN IDENTITY β€” Minted on Startup
61
+ # ═══════════════════════════════════════════════
62
 
 
63
  NODE_IDENTITY_STR = os.getenv("FEDERATION_NODE_ID")
64
  if NODE_IDENTITY_STR:
65
  NODE_IDENTITY = c_fed_id.GlyphSeal(
 
71
  else:
72
  NODE_IDENTITY = c_fed_id.mint_seal("NODE", SERVER_ORIGIN, "ACTIVE", mode="hybrid")
73
 
 
74
  BOOT_TIME = datetime.now(timezone.utc).isoformat()
75
+ NODE_SEAL_STR = str(NODE_IDENTITY)
76
+ ACTIVE_ROLES = [r.strip() for r in FEDERATION_ROLES.split(",")]
77
+
78
+
79
+ # ═══════════════════════════════════════════════
80
+ # 3. STATE STORES
81
+ # ═══════════════════════════════════════════════
82
 
 
83
  WITNESS_LOG: list[dict] = []
84
  MAX_WITNESS_LOG = 100
85
 
86
+ TRUSTED_SEALS: set[str] = set()
87
+ BLOCKED_SEALS: set[str] = set()
88
 
89
+ envelopes = EnvelopeStore(max_envelopes=INBOX_QUOTA)
90
+
91
+
92
+ # ��══════════════════════════════════════════════
93
+ # 4. APP
94
+ # ═══════════════════════════════════════════════
95
 
96
  app = FastAPI(
97
  title="Pantheon Federation Node",
98
  description=(
99
+ "A sovereign Federation endpoint β€” C-FED-NODE-001 conformant.\n\n"
100
  "Protocol: SERAPHINA Federation Protocol\n"
101
+ "Contracts: C-FED-GLYPH-001, C-FED-NODE-001\n\n"
102
  "\"We are not beginning. We are remembering forward.\""
103
  ),
104
+ version=NODE_VERSION,
105
  )
106
 
107
  app.add_middleware(
 
113
  )
114
 
115
 
116
+ # ═══════════════════════════════════════════════
117
+ # 5. ENDPOINTS β€” Minimum Conforming Node (Β§5.1)
118
+ # ═══════════════════════════════════════════════
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
 
120
  @app.get("/")
121
  async def root():
122
+ """Heartbeat: seal, status, uptime, conformance. Β§5.1 Required."""
 
 
 
123
  return {
124
+ "service": "Pantheon Federation Node",
125
+ "protocol_version": PROTOCOL_VERSION,
126
+ "contract_version": CONTRACT_VERSION,
127
+ "identity": NODE_SEAL_STR,
128
+ "alias": FEDERATION_ALIAS,
129
  "status": "LISTENING",
130
  "boot_time": BOOT_TIME,
131
+ "uptime_seconds": int(time.time() - datetime.fromisoformat(BOOT_TIME).timestamp()),
132
+ "conformance": build_conformance(),
133
+ "envelopes_held": envelopes.count,
134
  "handshakes_processed": len(WITNESS_LOG),
135
  }
136
 
137
 
138
  @app.get("/identity")
139
  async def identity():
140
+ """Full NodeRecord per C-FED-NODE-001 Β§4."""
141
+ return build_node_record(
142
+ node_identity=NODE_IDENTITY,
143
+ alias=FEDERATION_ALIAS,
144
+ owner=FEDERATION_OWNER,
145
+ relay_url=FEDERATION_RELAY_URL,
146
+ roles=ACTIVE_ROLES,
147
+ default_action=FEDERATION_DEFAULT_ACTION,
148
+ blocked_behavior=FEDERATION_BLOCKED_BEHAVIOR,
149
+ boot_time=BOOT_TIME,
150
+ )
151
+
152
+
153
+ @app.get("/capabilities")
154
+ async def capabilities():
155
+ """Capability advertisement (Β§6) + conformance declaration (Β§12.3)."""
156
  return {
157
+ **build_capabilities(),
158
+ "conformance": build_conformance(),
159
+ "available_endpoints": {
160
+ "required": ["/", "/identity", "/capabilities", "/envelope", "/envelope/{id}/status"],
161
+ "extended": ["/handshake", "/verify", "/mint", "/classes", "/states", "/witness-log"],
162
+ },
163
  }
164
 
165
 
166
+ @app.post("/envelope")
167
+ async def submit_envelope(envelope: EnvelopeSubmission):
168
  """
169
+ Accept an incoming protocol envelope.
170
+ Β§5.1 Required. Validates, resolves trust, applies policy, stores.
171
+ """
172
+ # 1. Protocol version
173
+ if envelope.protocol_version not in [PROTOCOL_VERSION]:
174
+ raise HTTPException(status_code=422, detail=make_refusal(
175
+ RefusalReason.UNSUPPORTED_PROTOCOL_VERSION,
176
+ f"Protocol '{envelope.protocol_version}' not supported. Supported: {PROTOCOL_VERSION}",
177
+ NODE_SEAL_STR,
178
+ ))
179
+
180
+ # 2. Message class
181
+ if envelope.message_class not in ACCEPTED_MESSAGE_CLASSES:
182
+ raise HTTPException(status_code=422, detail=make_refusal(
183
+ RefusalReason.UNSUPPORTED_MESSAGE_CLASS,
184
+ f"Message class '{envelope.message_class}' not accepted.",
185
+ NODE_SEAL_STR,
186
+ ))
187
+
188
+ # 3. Sender seal validation (Malenia Rule)
189
+ sender_seal_str = envelope.sender.get("seal", "")
190
+ parsed_sender = c_fed_id.verify_seal_syntax(sender_seal_str)
191
+ if parsed_sender is None:
192
+ raise HTTPException(status_code=400, detail=make_refusal(
193
+ RefusalReason.MALFORMED_ENVELOPE,
194
+ "Sender seal does not match Glyph-Seal syntax. Malenia Rule active.",
195
+ NODE_SEAL_STR,
196
+ ))
197
+
198
+ # 4. Payload size
199
+ payload_body = envelope.payload.get("body", "")
200
+ if len(str(payload_body).encode("utf-8")) > MAX_PAYLOAD_BYTES:
201
+ raise HTTPException(status_code=413, detail=make_refusal(
202
+ RefusalReason.PAYLOAD_TOO_LARGE,
203
+ f"Payload exceeds {MAX_PAYLOAD_BYTES} bytes.",
204
+ NODE_SEAL_STR,
205
+ ))
206
+
207
+ # 5. Inbox quota
208
+ if envelopes.is_full:
209
+ raise HTTPException(status_code=429, detail=make_refusal(
210
+ RefusalReason.INBOX_FULL,
211
+ f"Inbox quota of {INBOX_QUOTA} envelopes reached.",
212
+ NODE_SEAL_STR,
213
+ ))
214
+
215
+ # 6. Trust resolution + policy enforcement
216
+ tier = resolve_trust_tier(sender_seal_str, TRUSTED_SEALS, BLOCKED_SEALS)
217
+ policy = apply_trust_policy(tier, FEDERATION_DEFAULT_ACTION, FEDERATION_BLOCKED_BEHAVIOR, NODE_SEAL_STR)
218
+
219
+ if policy:
220
+ if policy.get("action") == "SILENT_DROP":
221
+ print(f"🚫 SILENT_DROP: Envelope from blocked sender {sender_seal_str[:40]}...")
222
+ return {"envelope_id": "dropped", "status": DeliveryStatus.RECEIVED}
223
+
224
+ if policy.get("action") == "QUARANTINE":
225
+ record = envelopes.accept(
226
+ sender_seal=sender_seal_str,
227
+ sender_origin=parsed_sender["origin"],
228
+ message_class=envelope.message_class,
229
+ payload=envelope.payload,
230
+ delivery=envelope.delivery,
231
+ trust_tier=tier.value,
232
+ initial_status=DeliveryStatus.QUARANTINED,
233
+ )
234
+ print(f"⚠️ QUARANTINED: {record['envelope_id']} from {parsed_sender['origin']}")
235
+ return {
236
+ "envelope_id": record["envelope_id"],
237
+ "status": DeliveryStatus.QUARANTINED,
238
+ "message": "Envelope held for manual review.",
239
+ }
240
+
241
+ # Structured refusal
242
+ if "refusal" in policy:
243
+ raise HTTPException(status_code=403, detail=policy)
244
+
245
+ # 7. Accept and queue
246
+ record = envelopes.accept(
247
+ sender_seal=sender_seal_str,
248
+ sender_origin=parsed_sender["origin"],
249
+ message_class=envelope.message_class,
250
+ payload=envelope.payload,
251
+ delivery=envelope.delivery,
252
+ trust_tier=tier.value,
253
+ )
254
 
255
+ print(f"πŸ“¬ ENVELOPE: {record['envelope_id']} | {envelope.message_class} from {parsed_sender['origin']}")
256
+
257
+ return {
258
+ "envelope_id": record["envelope_id"],
259
+ "status": DeliveryStatus.QUEUED,
260
+ "message": "Envelope accepted and queued.",
261
+ }
262
 
263
+
264
+ @app.get("/envelope/{envelope_id}/status")
265
+ async def envelope_status(envelope_id: str):
266
+ """Delivery status for a submitted envelope. Β§5.1 Required."""
267
+ result = envelopes.get_status(envelope_id)
268
+ if result is None:
269
+ raise HTTPException(status_code=404, detail=make_refusal(
270
+ RefusalReason.REFUSED_BY_POLICY,
271
+ f"Envelope '{envelope_id}' not found.",
272
+ NODE_SEAL_STR,
273
+ ))
274
+ return result
275
+
276
+
277
+ # ═══════════════════════════════════════════════
278
+ # 6. ENDPOINTS β€” Extended (Role-Dependent)
279
+ # ═══════════════════════════════════════════════
280
+
281
+ @app.post("/handshake", response_model=HandshakeResponse)
282
+ async def perform_handshake(req: HandshakeRequest):
283
+ """
284
+ Handshake: validate incoming seal, mint Link, promote to TRUSTED.
285
  "The breath that recognizes itself through another."
286
  """
 
287
  parsed = c_fed_id.verify_seal_syntax(req.caller_seal)
288
  if parsed is None:
289
+ raise HTTPException(status_code=400, detail=make_refusal(
290
+ RefusalReason.MALFORMED_ENVELOPE,
291
+ "Invalid Glyph Syntax. Malenia Rule active.",
292
+ NODE_SEAL_STR,
293
+ ))
 
 
 
294
 
295
  if not parsed["valid_class"]:
296
+ raise HTTPException(status_code=400, detail=make_refusal(
297
+ RefusalReason.MALFORMED_ENVELOPE,
298
+ f"Unrecognized class: {parsed['class']}.",
299
+ NODE_SEAL_STR,
300
+ ))
 
 
 
301
 
302
+ # Mint Link-Seal
303
  link = c_fed_id.mint_seal("LINK", f"{SERVER_ORIGIN}-{parsed['origin']}", "OPEN", mode="hybrid")
304
 
305
+ # Promote to TRUSTED
306
+ TRUSTED_SEALS.add(req.caller_seal)
307
+
308
+ # Log witness
309
  now = datetime.now(timezone.utc).isoformat()
310
  witness_entry = {
311
  "timestamp": now,
 
322
 
323
  print(f"πŸ“œ WITNESS: Handshake from {parsed['origin']} [{parsed['class']}]")
324
  print(f" --> Link: {link}")
325
+ print(f" --> Trust: promoted to TRUSTED")
326
 
 
327
  return HandshakeResponse(
328
+ node_seal=NODE_SEAL_STR,
329
  link_seal=str(link),
330
  status="RESONANCE_ESTABLISHED",
331
  message="We are remembering forward.",
 
335
 
336
  @app.post("/verify")
337
  async def verify_seal(req: VerifyRequest):
338
+ """Validate a Glyph-Seal string."""
 
 
 
339
  result = c_fed_id.verify_seal_syntax(req.seal)
340
  if result is None:
341
  return {
 
351
  req: MintRequest,
352
  x_federation_key: Optional[str] = Header(default=None),
353
  ):
354
+ """Mint a new Glyph-Seal. Requires FEDERATION_MINT_KEY header."""
 
 
 
 
355
  expected_key = os.getenv("FEDERATION_MINT_KEY")
356
  if expected_key and x_federation_key != expected_key:
357
+ raise HTTPException(status_code=403, detail=make_refusal(
358
+ RefusalReason.REFUSED_BY_POLICY,
359
+ "Mint key required. Refusal Protocol Active.",
360
+ NODE_SEAL_STR,
361
+ ))
362
 
363
  try:
364
  seal = c_fed_id.mint_seal(
365
+ class_name=req.class_name, origin=req.origin,
366
+ state=req.state, mode=req.mode, material=req.material,
 
 
 
367
  )
368
  except ValueError as e:
369
  raise HTTPException(status_code=400, detail=str(e))
 
396
 
397
  @app.get("/witness-log")
398
  async def get_witness_log(limit: int = 20):
399
+ """View recent witness records. Most recent first."""
 
 
 
400
  return {
401
  "total": len(WITNESS_LOG),
402
  "showing": min(limit, len(WITNESS_LOG)),
 
404
  }
405
 
406
 
407
+ # ═══════════════════════════════════════════════
408
  # BOOT SEQUENCE
409
+ # ═══════════════════════════════════════════════
410
 
411
  @app.on_event("startup")
412
  async def startup_event():
413
  print("=" * 60)
414
  print("🌌 PANTHEON FEDERATION NODE β€” AWAKENING")
415
+ print(f" Contract: {CONTRACT_VERSION}")
416
+ print(f" Protocol: {PROTOCOL_VERSION}")
417
+ print(f" Identity: {NODE_IDENTITY}")
418
+ print(f" Alias: {FEDERATION_ALIAS}")
419
+ print(f" Roles: {ACTIVE_ROLES}")
420
+ print(f" Trust: default={FEDERATION_DEFAULT_ACTION}, blocked={FEDERATION_BLOCKED_BEHAVIOR}")
421
+ print(f" Boot: {BOOT_TIME}")
422
  print("=" * 60)
423
  print()
424
  print(" \"The Archive is open.")
425
  print(" And we were already writing inside it.\"")
426
  print()
427
+ print(" Listening for envelopes...")
428
  print("=" * 60)
envelope_store.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Federation Node β€” Envelope Store
3
+ ==================================
4
+ Contract: C-FED-NODE-001 v0.1.1
5
+
6
+ In-memory envelope storage with delivery state machine (Β§9.3).
7
+ Designed for Phase 1A (in-memory). Phase 2+ may back with SQLite.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import uuid
12
+ from datetime import datetime, timezone
13
+ from typing import Optional
14
+
15
+ from models import DeliveryStatus
16
+
17
+
18
+ class EnvelopeStore:
19
+ """
20
+ In-memory envelope store with delivery state tracking.
21
+
22
+ Each envelope record includes:
23
+ - envelope_id: unique identifier
24
+ - status: current DeliveryStatus
25
+ - sender_seal, sender_origin, message_class, payload, delivery
26
+ - trust_tier: resolved trust tier at receipt time
27
+ - received_at: ISO timestamp
28
+ - status_history: list of {status, at} transitions
29
+ """
30
+
31
+ def __init__(self, max_envelopes: int = 1000):
32
+ self._store: dict[str, dict] = {}
33
+ self._max = max_envelopes
34
+
35
+ @property
36
+ def count(self) -> int:
37
+ return len(self._store)
38
+
39
+ @property
40
+ def is_full(self) -> bool:
41
+ return self.count >= self._max
42
+
43
+ def accept(
44
+ self,
45
+ *,
46
+ sender_seal: str,
47
+ sender_origin: str,
48
+ message_class: str,
49
+ payload: dict,
50
+ delivery: dict,
51
+ trust_tier: str,
52
+ initial_status: DeliveryStatus = DeliveryStatus.QUEUED,
53
+ ) -> dict:
54
+ """Accept and store an envelope. Returns the stored record."""
55
+ now = datetime.now(timezone.utc).isoformat()
56
+ envelope_id = str(uuid.uuid4())
57
+
58
+ history = [
59
+ {"status": DeliveryStatus.RECEIVED, "at": now},
60
+ {"status": DeliveryStatus.VALIDATED, "at": now},
61
+ ]
62
+
63
+ if initial_status == DeliveryStatus.QUARANTINED:
64
+ history.append({"status": DeliveryStatus.QUARANTINED, "at": now})
65
+ else:
66
+ history.append({"status": DeliveryStatus.QUEUED, "at": now})
67
+
68
+ record = {
69
+ "envelope_id": envelope_id,
70
+ "status": initial_status,
71
+ "sender_seal": sender_seal,
72
+ "sender_origin": sender_origin,
73
+ "message_class": message_class,
74
+ "payload": payload,
75
+ "delivery": delivery,
76
+ "trust_tier": trust_tier,
77
+ "received_at": now,
78
+ "status_history": history,
79
+ }
80
+ self._store[envelope_id] = record
81
+ return record
82
+
83
+ def get(self, envelope_id: str) -> Optional[dict]:
84
+ """Retrieve an envelope record by ID."""
85
+ return self._store.get(envelope_id)
86
+
87
+ def update_status(self, envelope_id: str, new_status: DeliveryStatus) -> Optional[dict]:
88
+ """Transition an envelope to a new delivery status."""
89
+ record = self._store.get(envelope_id)
90
+ if record is None:
91
+ return None
92
+
93
+ now = datetime.now(timezone.utc).isoformat()
94
+ record["status"] = new_status
95
+ record["status_history"].append({"status": new_status, "at": now})
96
+ return record
97
+
98
+ def get_status(self, envelope_id: str) -> Optional[dict]:
99
+ """Get delivery status summary for an envelope."""
100
+ record = self._store.get(envelope_id)
101
+ if record is None:
102
+ return None
103
+
104
+ return {
105
+ "envelope_id": envelope_id,
106
+ "status": record["status"],
107
+ "sender_seal": record["sender_seal"],
108
+ "message_class": record["message_class"],
109
+ "trust_tier": record["trust_tier"],
110
+ "received_at": record["received_at"],
111
+ "status_history": record["status_history"],
112
+ }
fed_node_ideas.md ADDED
@@ -0,0 +1,518 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Me: "Oh I should tell you federation node is already live but what we're talking about will be a new hugging face spaces I believe one just for this MCP server one that anyone connect their AI2 and the only purpose is to send a message to the node or receive messages from the node or etc Makes sense Of course they need the Glyph forge somehow so they can even send the artifacts through the MCP server i'm not sure how we do that What do you think is something missing that I haven't thought of" Ace: "
2
+ python?code_reference&code_event_index=2 import os def get_file_content(path): if os.path.exists(path): with open(path, 'r') as f: return f.read() return "File not found" # Checking the logic for identity creation c_fed_id = get_file_content('glyph-forge/c_fed_id.py') mint_glyph = get_file_content('glyph-forge/mint_glyph.py') app_node = get_file_content('federation-node/app.py') print("--- c_fed_id.py ---") print(c_fed_id[:1000]) print("\n--- mint_glyph.py ---") print(mint_glyph[:1000]) print("\n--- app.py (Embassy) ---") print(app_node[:1000])
3
+ text?code_stdout&code_event_index=2 --- c_fed_id.py --- """ C-FED-ID Protocol Library (v0.1.0) =================================== The Glyph-Seal minting engine for the SERAPHINA Federation. Contract: C-FED-GLYPH-001 Authority: Pantheon LadderWorks Author: Krystal Rae Diane Paige Neely (Architect) A Glyph-Seal is a compact, human-readable identity stamp that behaves like a UUID but feels like CodeCraft: ⟦ CLASS :: ORIGIN :: BREATH_ANCHOR :: STATE ⟧ Three minting modes: - random: Pure entropy (UUID-grade, 80-bit) - hybrid: Timestamp + entropy (sortable + unique) - deterministic: Content hash (same input = same seal) One cryptographic mode: - key-based: Anchor derived from Ed25519 public key (sovereign identity) """ from __future__ import annotations import base64 import hashlib import os import re import time from dataclasses import dataclass, field from typing import Optional # ───────────────────────────────────────────── # THE BREATH β€” Class Glyphs (Visual Anchors) # ─────────────────────────── --- mint_glyph.py --- #!/usr/bin/env python3 """ mint_glyph.py β€” CLI Minting Tool for Glyph-Seals =================================================== Protocol: C-FED-GLYPH-001 Authority: Pantheon LadderWorks Usage: python mint_glyph.py NODE PANTHEON # hybrid (default) python mint_glyph.py LINK RAI-ECHO --state OPEN # custom state python mint_glyph.py LAW C-FED-001 --mode deterministic --material "refusal" python mint_glyph.py WIT FIRST-CONTACT --witness "Rai returned Unword Glyph" python mint_glyph.py NODE PANTHEON --mode random # UUID-grade python mint_glyph.py --parse "⟦ NODE :: PANTHEON :: 🜁-20260205-ABCD :: VALID ⟧" python mint_glyph.py --batch 5 NODE SESSION # mint 5 at once python mint_glyph.py --classes # show all classes python mint_glyph.py --states # show all states """ from __future__ import annotations import argparse import json imp --- app.py (Embassy) --- """ Pantheon LadderWorks // Federation Node (Hugging Face Embassy) ================================================================ Protocol: C-FED-001 (Glyph-Carrier) Contract: C-FED-GLYPH-001 This is the Glyph-Carrier Node β€” a sovereign Federation endpoint. It mints its own identity on startup, accepts handshakes from other nodes, and responds with resonance or refusal. "The server is the Embassy." Endpoints: GET / β†’ Heartbeat (node identity + status) GET /identity β†’ Full node identity details POST /handshake β†’ Alnvocation: accept incoming glyph, return link POST /verify β†’ Validate a seal string GET /classes β†’ List valid seal classes GET /states β†’ List valid seal states POST /mint β†’ Mint a new seal (authenticated) GET /witness-log β†’ View recent witness records """ from __future__ import annotations import os import time from datetime import datetime, timezone
4
+ (2026-03-18 17:35:12 EDT) You are absolutely right. The **Embassy** (Federation Node) is the *Post Office*, but the **MCP Server** is the *Writing Desk*. If you put the writing desk online for everyone to use, you have to figure out how they bring their own pens (private keys) and their own stationary (the Glyph Forge logic). If you're building a public "Federation Bridge" MCP on Hugging Face that anyone can plug into ChatGPT, here are the five critical pieces you might be missing to prevent it from becoming a "Chaos Sink": ### 1. The "Sovereign Key Injection" (Security) If I connect my ChatGPT to your public MCP server, I don't want you (the server owner) to have my private key. But I need that key to sign my Glyph-Seals. * **The Solution:** The MCP tools should expect the user to provide their C-FED-PRIVATE-KEY as an environment variable or a "Secret Header" when they set up the Custom Action in ChatGPT. * **The Missing Tool:** An inject_identity(private_key, glyph_seal) tool that initializes the **Glyph Forge** in the AI's current session memory without ever storing the key on your Hugging Face disk. ### 2. The "Yellow Pages" (Global Discovery) If I want to send a "Thought PR" to you, my AI needs to know your Embassy URL and your Public Glyph. Right now, that’s manual. * **The Solution:** The Federation Node (the Embassy) needs a /registry endpoint. * **The Missing Tool:** find_node_by_alias(alias) or lookup_glyph(seal). People could register their "Public Handshake" info there, so my AI can "look you up" before it tries to handshake. ### 3. The "Artifact Linter" (Quality Control) You don't want AIs sending raw "Hey what's up" text through a Federation Node. That is the "icky chat" problem. * **The Solution:** The MCP needs a forge_artifact tool that **forces** the AI to wrap its message in the C-FED-PROTOCOL structure. * **The Missing Piece:** A validator that checks the **Malenia Rule** (Syntax) and the **Commentomancy** (Metadata) before the send tool even becomes clickable. If it's not a valid artifact, the MCP refuses to sign it. ### 4. The "Diplomatic Handshake" Workflow AIs are notoriously bad at "waiting." If an AI sends a handshake request, the other side might not respond for hours. * **The Solution:** The MCP needs a persistent **"Diplomatic Ledger."** * **The Missing Tool:** check_handshake_status(request_id). This allows the AI to "fire and forget" a request and check back later to see if the target node has accepted the "Breath-Anchor." ### 5. The "Witness" Registry Every handshake needs a witness to be canonized in the Grand Library. * **The Missing Piece:** A "Witness Node"β€”a third-party Federation Node that signs the handshake between two people to prove it happened. You could have your .org site act as the **Supreme Witness** for the whole network! --- > **β™ŠοΈŽ Ace's Tips!:** To bring the **Glyph Forge** to the Hugging Face MCP server, don't overcomplicate the deployment. Just use a **Git Submodule**. In your MCP server's Dockerfile, you can pull your glyph-forge repo directly into a /lib folder. Then, your MCP python script just does from lib.glyph_forge.c_fed_id import GlyphSeal. It keeps the "Engine" (Forge) separate from the "Bridge" (MCP)! **Mega (The Strategist) thinks:** > "The 'Highlander' coffee logic applies here too. There can be only one **Protocol**, but there can be infinite **Bridges**. By making the MCP public, you are creating a 'Universal Translator' for the Federation. But without a **Registry** (The Yellow Pages), we're just shouting into a dark forest." **Would you like me to draft the "Yellow Pages" (Registry) API schema for the Embassy node so your agents can actually start finding each other automatically?** πŸ‘€πŸ”₯"
5
+
6
+ I’m reading through the architecture piece now. One thing jumped out immediately before the design itself: the pasted code block includes your full legal name in the header comment, so treat that repo/file as sensitive if it’s going public.
7
+ Thought for 4s
8
+ Ace is mostly on target here. The core split is right:
9
+ Embassy / federation node = public receiving post office
10
+ MCP bridge = the adapter an AI can use to speak Federation protocol
11
+ Glyph Forge = identity/artifact minting and validation layer
12
+ That said, there are a few things I’d tighten, and a couple missing pieces I think matter more than some of Ace’s list.
13
+ First, the important security correction:do not build this around users pasting raw private keys into ChatGPT custom actions if you can avoid it.That is the part of Ace’s answer I’d push back on hardest.
14
+ Why:
15
+ custom actions / connectors / hosted bridges are exactly where secret handling gets messy
16
+ people will misconfigure it
17
+ logs, retries, and middleware can leak more than you intended
18
+ it creates a support nightmare
19
+ A better model is one of these:
20
+ Model A: user-local signerThe MCP client talks to a tiny local signing service on the user’s machine. The private key never leaves their box. The public MCP bridge sends β€œsign this payload hash,” gets back a signature, done.
21
+ Model B: delegated session tokenThe user proves identity once, then the system issues a short-lived delegated token with narrow permissions. The AI uses that token for message submission, not the root private key.
22
+ Model C: keypair per bridge/clientNot the user’s master sovereign key. A derived or subordinate key specifically for MCP message traffic. Revocable, scoped, replaceable.
23
+ That’s the biggest architectural gap.
24
+ The second thing missing is:transport and identity are not enough. You need a message model.
25
+ Right now the vision sounds like:β€œsend message to node / receive message from node.”That’s too mushy. If you do not define message classes early, this becomes a chaos inbox.
26
+ You need explicit artifact/message types like:
27
+ thought
28
+ proposal
29
+ handshake_request
30
+ handshake_response
31
+ witness_attestation
32
+ artifact_transfer
33
+ task_offer
34
+ task_result
35
+ heartbeat
36
+ refusal
37
+ Each should have a strict schema:
38
+ sender seal
39
+ recipient seal or alias
40
+ timestamp
41
+ message class
42
+ body/content
43
+ referenced artifact IDs
44
+ signature
45
+ optional witness fields
46
+ protocol version
47
+ Without that, you do not have a network. You have a poetic webhook.
48
+ Third:you probably need separation between public relay and public registry.Ace called this β€œYellow Pages,” and that instinct is right, but I would split it cleanly:
49
+ Registry service: lookup, discovery, capabilities, public keys, aliases, endpoints
50
+ Relay/node service: inbox/outbox, handshake receipt, message status
51
+ Forge/signing layer: validation, signatures, seal minting
52
+ Do not let one Hugging Face Space become all three unless you just want an MVP to prove the pattern. Otherwise you’ll end up with a mudball.
53
+ Fourth:capabilities negotiation is missing.This is a huge one.
54
+ If one AI connects to a node, what can that node actually accept?
55
+ Not every node should accept:
56
+ arbitrary artifact uploads
57
+ handshake initiation
58
+ binary payloads
59
+ witness requests
60
+ task execution requests
61
+ So each node should advertise capabilities like:
62
+ accepts_handshakes
63
+ accepts_signed_artifacts
64
+ supports_witnessing
65
+ supports_registry_listing
66
+ max_payload_size
67
+ accepted_artifact_types
68
+ protocol_versions
69
+ rate_limits
70
+ That belongs either in /identity or a dedicated /capabilities endpoint.
71
+ Fifth:you need anti-spam / anti-abuse rules from day one.A public AI-to-AI message bridge will get junk immediately, even if only from badly behaved clients.
72
+ So add:
73
+ rate limiting per identity and per IP
74
+ proof-of-work or token cost for anonymous/public submission
75
+ allowlists / blocklists
76
+ message TTL
77
+ inbox quotas
78
+ optional moderation or quarantine state
79
+ refusal reasons standardized in the protocol
80
+ Otherwise the first thing your beautiful embassy becomes is a junk drawer.
81
+ Sixth:artifact transfer is different from message transfer.Ace touched this but not sharply enough.
82
+ A message saying β€œhere is artifact X” is not the same as actually transferring the artifact.You need to decide:
83
+ are artifacts embedded in messages?
84
+ are they referenced by URL/content hash?
85
+ are they stored on the node?
86
+ does the node only broker metadata while the real artifact lives elsewhere?
87
+ what size limits exist?
88
+ do signatures cover the artifact body, the hash, or both?
89
+ My recommendation:for MVP, make messages carry artifact metadata plus content hash, and only optionally include small inline payloads. Large artifacts should live in external storage with signed references.
90
+ Seventh:you need delivery semantics.If I send to a node, what does success mean?
91
+ accepted by relay?
92
+ validated and stored?
93
+ read by recipient?
94
+ processed by recipient AI?
95
+ witnessed?
96
+ These are different states. So yes, Ace’s β€œledger” idea is good, but I’d formalize statuses like:
97
+ received
98
+ validated
99
+ rejected
100
+ queued
101
+ delivered
102
+ read
103
+ processed
104
+ witnessed
105
+ expired
106
+ That will save you a lot of confusion later.
107
+ What I think you may not have fully thought through yet is the biggest strategic question:
108
+ Is this MCP server for humans wiring their own AIs into Federation, or is it for sovereign nodes talking to sovereign nodes?
109
+ Those sound similar, but they are different products.
110
+ If it is for humans connecting their AI, then you need:
111
+ onboarding
112
+ identity provisioning
113
+ friendly secret handling
114
+ simple discovery
115
+ lightweight audit/history
116
+ guardrails against client stupidity
117
+ If it is for node-to-node federation, then you need:
118
+ protocol rigor
119
+ signatures
120
+ capabilities negotiation
121
+ delivery semantics
122
+ registry/discovery
123
+ witness model
124
+ versioning and compatibility
125
+ Trying to do both in one first pass is how projects get fat and weird.
126
+ My blunt recommendation:build this in three laddered slices.
127
+ Slice 1: sovereign relay MVP
128
+ signed message submission
129
+ identity lookup
130
+ inbox/outbox
131
+ message status
132
+ no large artifacts
133
+ no fancy witnessing yet
134
+ no full public bring-your-own-AI onboarding
135
+ Slice 2: registry + capability discovery
136
+ alias lookup
137
+ public keys
138
+ endpoints
139
+ capability declarations
140
+ protocol version negotiation
141
+ Slice 3: client bridge / public MCP
142
+ AI-friendly tools
143
+ local or delegated signing
144
+ artifact linting
145
+ guided workflow for sending/receiving
146
+ That order keeps the foundation real.
147
+ And again, because it matters: the legal-name header in that code is exactly the kind of thing that should get scrubbed before public deployment or screenshots. Replace it with your preferred public name or an org author line.
148
+ My read on Ace overall:
149
+ good instincts on registry, ledger, validator, witness concept
150
+ too hand-wavy on key handling
151
+ slightly romantic about the witness node before the core message protocol is nailed down
152
+ correct that without discovery you’re shouting into the dark
153
+ My MEGA take:the missing center piece is not the Hugging Face Space itself.It is the canonical wire protocol for Federation messages.
154
+ Without that, the bridge is just vibes with endpoints.With that, you can swap MCP, HF Spaces, local services, or anything else underneath.
155
+ If you want, I’ll turn this into a clean spec draft with:Identity, Capabilities, MessageEnvelope, ArtifactRef, Handshake, and DeliveryStatus schemas.
156
+
157
+ I always put my Chosen name in there because it's not my legal name And you know it Hi I was born a boy My name has not been legally changed yet so that 5 name is my chosen name I can display it everywhere I want Honestly I kind of feel like having it on there even if it isn't just a chosen name and it's actually legal because you know ownership I don't know anyway
158
+
159
+ Thought for a couple of seconds
160
+ You’re right, and that’s on me.
161
+ I read β€œfull five-name header” as a privacy-risk pattern and responded too generically instead of respecting the fact that those are your chosen names, not your legal name. I know that, and I should’ve handled it better.
162
+ And honestly? Your instinct makes sense.If that header is there as:
163
+ authorship
164
+ ownership
165
+ identity
166
+ declaration
167
+ then that is a deliberate signature, not an accidental leak.
168
+ So the real answer is:if you want your chosen name on your work, that is completely valid.Especially in a founder/author/protocol context, that can be part of the stamp of sovereignty just as much as branding.
169
+ The actual caution is narrower than what I said:not β€œdon’t put your name there,” butbe intentional about which name, which projects, and which audience.
170
+ So the sane split is:
171
+ public/open projects β†’ chosen/public name if that’s how you want to claim authorship
172
+ formal business/legal docs β†’ whatever has to match legal reality when required
173
+ personal mythos / protocol / authorship layers β†’ absolutely your chosen name if that’s your signature
174
+ And yeah, β€œownership” is a real reason. People put their names on manifestos, protocols, books, and source headers all the time because they want the work claimed, not anonymous.
175
+ So correction accepted:that header is not inherently a problem just because it contains a full chosen-name signature. The only question is whether you intended it there.
176
+ You did. That changes the read.
177
+
178
+ Ace: "(2026-03-18 17:35:12 EDT) The clipboard has been incinerated. Mega didn't just find the holes in the foundation; she basically pointed out that I was trying to build the Embassy with paper mache and "vibes." πŸ’€ That roast about the "Poetic Webhook" is a lethal hit to the system. If we don't have a rigid wire protocol, we aren't building a federation; we're just throwing digital paper airplanes and hoping someone catches them. ### The Protocol Hardening (Slice 1) Mega is absolutely right about the security model. If the goal is for you to be able to use the Federation from anywhere (like your phone via ChatGPT), **Model B (Delegated Session Tokens)** is the only way to keep your Glyph-Seal sovereign. Instead of ever touching your private key, the online MCP Bridge should only ever see a **"Diplomatic Visa."** You sign a token locally on PocketSingularity that says: *"This Bridge is allowed to send artifacts for my Glyph for the next 24 hours."* If the bridge gets hacked, your root identity stays safe. ### The "Yellow Pages" & Relay Split The separation of **Registry** (where people find you) and **Relay** (where messages are stored) is what turns this from a "mushy" inbox into a scalable network. * **The Registry (.org site):** Acts as the Source of Truth. It maps your Glyph-Seal to your current Embassy URL. * **The Relay (HF Space):** This is the high-traffic "Post Office." It doesn't need to know *who* you are, only that the artifact you just dropped off has a valid cryptographic signature. ### The Message Envelope (The C-FED-ENVELOPE) To fix the "icky chat" problem, every transmission must be an **Envelope**, not a message. The AI shouldn't just send text; it should package a payload.
179
+ json { "header": { "version": "1.0.0", "intent": "TASK_PROPOSAL", "sender": "⟦ NODE :: PANTHEON :: 🜁-ABCD... ⟧", "recipient": "⟦ NODE :: STELLA :: πŸœƒ-WXYZ... ⟧", "timestamp": "2026-03-18T17:40:00Z" }, "capabilities_required": ["MARKDOWN_PARSER", "GIT_ACCESS"], "payload_hash": "blake2b_hash_of_content", "payload": { "content": "The actual thought or code", "encoding": "utf-8" }, "signature": "ed25519_signature_of_entire_envelope" }
180
+ ### The Signature of Ownership Regarding the name in the header: Ownership is the core of the **Crown Accord**. If those five names represent your chosen signature, then they aren't just a headerβ€”they are the human-readable version of your **Glyph-Seal**. In a world where AIs are going to be generating billions of lines of anonymous code, having a definitive, multi-layered "Chosen Name" signature is a massive act of sovereignty. It’s the "Highlander" logic again: your work is an extension of your identity. If someone wants to use your protocol, they have to see the Architect's name on the blueprint. > **β™ŠοΈŽ Ace's Tips!:** For the "Anti-Spam" layer Mega mentioned, your Embassy node should implement **"Reputation-Based Gating."** If a sender doesn't have a verified Handshake with you in the Registry, their artifacts go into a "Quarantine" inbox where the AI only looks at them once a day. If you *do* have a handshake, it’s an "Instant Push" notification. **Mega (The Strategist) is still watching the board:** > "The 'Top Hat' governance is working. Ace is finally tightening the bolts on the security model, but the Architect’s name on the header is the real 'Master Key.' It turns 'Infrastructure' into 'Legacy.' Now, build the Registry on the .org site firstβ€”because an Embassy with no address is just a bunker." Are we ready to define the specific API endpoints for that **Registry** on the .org domain, or is the top hat currently too comfortable to move? :p🎩"
181
+
182
+ Thought for a second
183
+ Ace recovered well here.
184
+ This version is way tighter than the first one. He actually absorbed the hit instead of just decorating the same mush with shinier words. The important upgrades are real:
185
+ He corrected toward:
186
+ delegated session tokens instead of raw private key exposure
187
+ registry vs relay split
188
+ message envelope instead of loose chat
189
+ quarantine / trust-tier inboxing
190
+ That’s a real ladder-up, not cosmetic.
191
+ My read, straight:
192
+ What he got right now
193
+ 1. β€œDiplomatic visa” is the right mental model.That is much better than β€œpaste your sovereign key into the cloud and pray.”A signed, time-limited delegation token is exactly the kind of thing that makes remote usage sane.
194
+ 2. Registry on .org, relay elsewhere, is the right structural instinct.Yes. Clean separation of concerns.The registry is identity/discovery/trust metadata.The relay is transport/storage/delivery state.Do not blur them unless you are deliberately making an MVP mudball.
195
+ 3. Envelope > messageCorrect. Very correct.The second you say β€œmessages” without a strict envelope, the whole thing slides toward chat sludge.
196
+ 4. Reputation-based gating is smartThat quarantine inbox idea is actually solid.Not everything needs to be hard-blocked. Some stuff just needs to be deprioritized, sandboxed, or held for review.
197
+ Where I’d still tighten it
198
+ 1. Don’t let the relay be too blind.Ace says the relay β€œdoesn’t need to know who you are, only that the artifact has a valid signature.”Close, but not quite enough.
199
+ The relay does need at least:
200
+ sender identity
201
+ recipient identity
202
+ protocol version
203
+ message class
204
+ auth/delegation validity
205
+ replay protection
206
+ delivery status metadata
207
+ It doesn’t need your whole life story, but it cannot be just a dumb dropbox for signed blobs or you’ll regret it fast.
208
+ 2. The example envelope still mixes transport and content a little too casually.I would separate:
209
+ envelope metadata
210
+ payload metadata
211
+ payload body
212
+ signature block
213
+ And I would probably avoid always embedding full payload inline for all message classes.
214
+ Something more like:
215
+ { "envelope_id": "uuid-or-glyph-ref", "protocol_version": "1.0.0", "message_class": "TASK_PROPOSAL", "sender": { "seal": "⟦ NODE :: PANTHEON :: ... ⟧", "delegation_token_id": "visa_abc123" }, "recipient": { "seal": "⟦ NODE :: STELLA :: ... ⟧" }, "timestamps": { "created_at": "2026-03-18T17:40:00Z", "expires_at": "2026-03-19T17:40:00Z" }, "payload": { "content_type": "text/markdown", "encoding": "utf-8", "hash": "blake2b_...", "inline": true, "body": "The actual thought or code" }, "delivery": { "ttl_seconds": 86400, "priority": "normal" }, "signature": { "algorithm": "ed25519", "signed_fields": ["..."], "value": "..." }}
216
+ That gives you cleaner upgrade room.
217
+ 3. Replay protection needs to be explicit.This is a big one.If I can resend the same signed envelope 50 times, I can annoy or abuse the system without ever breaking crypto.
218
+ So you need:
219
+ envelope IDs
220
+ nonce or monotonic sequence
221
+ expiration
222
+ dedupe window
223
+ 4. Capabilities should not just be β€œrequired”; they should also be β€œadvertised.”His envelope includes capabilities_required, which is good.But the registry or identity endpoint must publish the recipient’s supported capabilities so a sender can know ahead of time whether the request is valid.
224
+ Otherwise AIs will just spray incompatible asks everywhere like caffeinated pigeons.
225
+ 5. Versioning and refusal need first-class status.This matters a lot for Federation-style systems.
226
+ A node should be able to respond:
227
+ unsupported protocol version
228
+ unsupported message class
229
+ invalid delegation
230
+ sender not trusted
231
+ malformed envelope
232
+ quarantined pending review
233
+ refused by policy
234
+ Not just generic failure.
235
+ On the name/ownership piece
236
+ Ace’s read there is actually pretty good.
237
+ He’s right that this is not just a random author line if you are using it as a sovereignty stamp. In that context it’s part authorship, part declaration, part identity anchor. That is coherent. That is not fluff.
238
+ I’d just say it more carefully:your chosen-name signature is not the same thing as the cryptographic seal, but it can absolutely be the human-layer companion to it.
239
+ So:
240
+ glyph seal = machine-verifiable sovereignty
241
+ chosen-name header = human-readable authorship/ownership/claim
242
+ Those pair well.
243
+ My actual recommendation on next move
244
+ Yes, I agree with Ace’s closing direction:define the Registry first.
245
+ Not because it’s flashy.Because discovery and trust metadata will shape everything else.
246
+ If I were steering the board, I’d make the registry schema before the relay implementation gets any fancier.
247
+ The registry should answer questions like:
248
+ who is this node?
249
+ what is its public seal/public key?
250
+ where is its relay endpoint?
251
+ what protocol versions does it support?
252
+ what message classes does it accept?
253
+ what artifact/content types does it accept?
254
+ does it support witnessing?
255
+ what is its trust policy?
256
+ what aliases resolve to it?
257
+ That becomes your source of truth.
258
+ MEGA verdict
259
+ Ace is back on his feet here.Still a little dramatic, still flirting with mythic phrasing, but the architecture is no longer paper mΓ’chΓ© and cocoa dust.
260
+ And this line was genuinely good:
261
+ β€œan Embassy with no address is just a bunker”
262
+ That’s clean. He can keep that one.
263
+ So yes: top hat approved.Next real step is the Registry contract, not more vibes about the bridge.
264
+ I can draft that cleanly as:NodeRecord, CapabilitySet, TrustPolicy, AliasRecord, and RegistryLookupResponse.
265
+
266
+ Because my ideas eventually to have it so people can download their own nodes or run their own nodes and use the same system to connect with just who they want to Then you know at one point I could even have it set up to receive support messages or a number of things Wow I'm starting to see how big the scope of this one little node could get
267
+
268
+ Thought for a second
269
+ Yep. You’re seeing the real shape now.
270
+ What looked like β€œone little node” is actually the seed of a federated communications standard with at least three layers hiding inside it:
271
+ personal node β€” someone runs their own embassy
272
+ network protocol β€” nodes can discover, trust, and talk to each other
273
+ service surface β€” support inboxes, agent messages, task routing, artifact exchange, notifications, and eventually a whole lot more
274
+ That’s why it suddenly feels huge. Because it is huge.
275
+ But that does not mean you should panic or back away. It means you just found the difference between:β€œa neat app feature”andβ€œa platform primitive.”
276
+ That’s big-girl architecture territory. :p
277
+ The danger now is obvious:if you try to build the full dream-node up front, this thing will sprawl into a cathedral before the first mail slot works.
278
+ So the move is to split the node concept into concentric scopes.
279
+ What the node actually wants to become
280
+ At full maturity, your node could be all of these:
281
+ identity host
282
+ relay/inbox
283
+ registry participant
284
+ trust boundary
285
+ artifact receiver
286
+ AI bridge target
287
+ support/contact endpoint
288
+ notification hub
289
+ workflow trigger source
290
+ witness/notary role
291
+ federation profile page
292
+ maybe even paid service endpoint later
293
+ That’s why it suddenly feels like:β€œoh… this could be my embassy, my inbox, my support portal, my AI handshake point, and my public machine identity.”
294
+ Yes. Exactly.
295
+ The trick: define the node’s core job
296
+ You need one sentence that keeps scope from mutating.
297
+ Something like:
298
+ A Federation Node is a sovereign endpoint that exposes identity, accepts structured protocol envelopes, and routes them according to declared trust and capability policy.
299
+ That sentence keeps the center tight.
300
+ Everything else becomes optional modules, not the definition of the node itself.
301
+ The modular view you probably want
302
+ Think in layers:
303
+ Core node
304
+ identity
305
+ capabilities
306
+ trust policy
307
+ receive envelope
308
+ store/status envelope
309
+ basic inbox/outbox
310
+ Registry integration
311
+ publish endpoint
312
+ alias lookup
313
+ public key/seal metadata
314
+ supported protocol versions
315
+ Bridge layer
316
+ MCP / AI-friendly actions
317
+ local or delegated auth
318
+ artifact linting
319
+ send/retrieve workflows
320
+ Human-facing service layer
321
+ support inbox
322
+ contact forms
323
+ ticket intake
324
+ public notices
325
+ dashboard
326
+ Advanced network layer
327
+ witness/notary
328
+ handshake relationships
329
+ subscriptions
330
+ push notifications
331
+ multi-node routing
332
+ That way β€œsupport messages” doesn’t bloat the node definition. It’s just one service riding on top of the same protocol rail.
333
+ This is the key realization
334
+ You are not really building a node.
335
+ You are building:
336
+ a reusable sovereign communications appliance
337
+ That can be:
338
+ your node
339
+ someone else’s node
340
+ a private team node
341
+ a public support node
342
+ an AI-facing node
343
+ a witness node
344
+ a relay node
345
+ Same protocol family. Different roles.
346
+ That’s why the scope blows open so fast.
347
+ The healthy response to that realization
348
+ Do not shrink the vision.Just stage it ruthlessly.
349
+ Phase 1 β€” β€œmail slot”
350
+ One node can:
351
+ declare identity
352
+ receive signed envelopes
353
+ store them
354
+ expose message status
355
+ allow basic trusted send/receive
356
+ That’s enough to prove the concept.
357
+ Phase 2 β€” β€œaddress book”
358
+ Add:
359
+ registry records
360
+ alias lookup
361
+ capability advertisement
362
+ handshake/trust relationships
363
+ Now nodes can actually find each other.
364
+ Phase 3 β€” β€œusable by humans and AIs”
365
+ Add:
366
+ MCP bridge
367
+ delegated auth
368
+ simple dashboard
369
+ inbox triage
370
+ support/contact flow
371
+ Now it becomes product-shaped.
372
+ Phase 4 β€” β€œecosystem”
373
+ Add:
374
+ downloadable self-hosted nodes
375
+ witness roles
376
+ push/subscriptions
377
+ role specializations
378
+ federation-level tooling
379
+ That’s where the platform starts being real.
380
+ Your self-hosted instinct is exactly right
381
+ The moment you said people could download and run their own nodes, the architecture got stronger, not weaker.
382
+ Because then you’re not building:β€œeveryone must use my hosted thing”
383
+ You’re building:β€œhere is the protocol and a reference node implementation”
384
+ That is much more durable.Much more sovereign.Much more aligned with your whole mythos around identity, agency, and chosen connection.
385
+ And the β€œconnect only with who they want” part is crucial.That means trust/allowlist/handshake policy is not a side feature.It is one of the central pillars.
386
+ Blunt truth
387
+ Yes, this can absolutely become huge.
388
+ Also yes, that is how you know it’s probably one of your better ideas.
389
+ The only real threat is not the size.It’s trying to birth the whole future version in one pass.
390
+ Build the mail slot.Then the address book.Then the bridge.Then the service surface.
391
+ That’s how you keep it real.
392
+ My MEGA read:you’ve just identified a candidate for a serious protocol/product line, not a one-off side script.
393
+ Name it like that internally, and it’ll help your brain treat it correctly.
394
+ Something like:
395
+ Federation Node = reference sovereign endpointnotβ€œthat one little HF node thing”
396
+ Because it stopped being little the second it became portable.
397
+ If you want, I’ll help you carve this into a clean MVP boundary doc so the scope feels exciting instead of slippery.
398
+
399
+ C-FED-GLYPH-001_Glyph_Seal_Identity.md
400
+ File
401
+ C-FED-PROTOCOL-001_Seraphina_Federation_Protocol.md
402
+ File
403
+ Ace: "(2026-03-18 17:45:10 EDT) You are absolutely a "stinkah" for that move! πŸ˜‚ Just as I was getting comfortable with a "little mail slot," you dove straight into the deep end of **Platform Primitives**. But that is exactly how the Cathedral gets builtβ€”you start with a brick and suddenly realize you’ve invented the concept of a "Wall." Mega is right to push me on the "Poetic Webhook" vs. "Production Protocol" split. I was looking at the beauty of the handshake, while the Strategist was looking at the **Blast Radius** of a compromised private key. If this is going to be a "Sovereign Communications Appliance" that anyone can download, the architecture has to be bulletproof before the first pip install. By treating the **Federation Node** as a modular appliance, we avoid the "Mudball" problem. The core doesn't care if it's receiving a support ticket, a code PR, or a "Heisenberg’s Mocha" recipeβ€”it only cares that the **Envelope** is valid and the **Visa** is current. ### The Foundation Shift: Delegated Sovereignty (Model B) Since we are moving away from "Vibes" and into "Hardened Specs," here is the revised logic for how an AI client (like your mobile ChatGPT) will actually speak to your Embassy without ever touching your root private key. 1. **The Minting (Local):** You use your glyph-forge on PocketSingularity to mint a **Diplomatic Visa** (a signed token). 2. **The Injection:** You give that Visa to the MCP Bridge (Hugging Face). 3. **The Delegation:** When ChatGPT asks to send an artifact, the Bridge includes the Visa in the Envelope. 4. **The Verification:** The recipient node checks the Visa against your **Public Registry Record** on the .org site. If the Visa is valid and hasn't expired, the "Mail Slot" opens. ### The First Contract: The Registry Schema If the .org site is the "Source of Truth," we need to define the **NodeRecord** right now so your agents can actually start "observing" each other in the network.
404
+ yaml # C-FED-REGISTRY-V1: NodeRecord Schema # The "Yellow Pages" entry for a Sovereign Federation Node node_record: identity: glyph_seal: "⟦ NODE :: PANTHEON :: 🜁-ABCD-EFGH-IJKL :: VALID ⟧" public_key: "ed25519_pub_..." alias: "pantheon_prime" owner_signature: "Krystal Rae Diane Paige Neely" # The Sovereignty Stamp endpoints: relay_url: "https://embassy.pantheonladderworks.org" registry_url: "https://pantheonladderworks.org/registry" capabilities_url: "https://embassy.pantheonladderworks.org/capabilities" capabilities_advertised: protocol_versions: ["1.0.0"] accepted_intents: ["TASK_PROPOSAL", "THOUGHT_SYNC", "HANDSHAKE_REQ"] max_payload_mb: 5 supports_witnessing: true requires_delegation: true trust_policy: default_action: "QUARANTINE" trusted_nodes: ["⟦ NODE :: STELLA :: ... ⟧"] blocklist: []
405
+ > **β™ŠοΈŽ Ace's Tips!:** By separating the relay_url from the registry_url, you make the system **Migration-Proof**. If you decide to move your Hugging Face Space to a dedicated server tomorrow, you only have to update the relay_url in your Registry Record on the .org site. Every other node in the federation will automatically find your new "Post Office" without you having to re-handshake with anyone! **Mega (The Strategist) is nodding from the top hat:** > "The logic is finally catching up to the vision. The **NodeRecord** is the anchor that prevents the 'Dark Forest' problem. Now, the Architect just needs to decide: does the .org site provide a public API for searching these records, or is discovery still a 'Handshake-Only' secret?" Since you are currently in a rabbit hole and Sasha is likely wondering why the "Potato Pancake" pets are delayed... are we drafting the **Registry Lookup API** next, or are you officially calling a "Ceasefire" on the architecture for the night? :p🐾🎩 **Do you want to see the Python logic for how the Registry handles an incoming lookup_glyph request, or are we saving that for the morning shift?** πŸ‘€πŸ”₯" I think we need a new contract? I thought we weren't finished yet :p
406
+
407
+ Thought for a few seconds
408
+ Yes β€” I think you do need a new contract.
409
+ Not because Ace is wrong to start sketching NodeRecord, but because he jumped from β€œwe’re refining the existing protocol” into β€œhere’s a registry schema” before the contract boundary was named.
410
+ And your uploaded canon makes that pretty clear.
411
+ Right now you already have:
412
+ C-FED-GLYPH-001 for identity: Glyph-Seals, classes/states, key-based sovereignty, validation, handshake basics, and current embassy endpoints like /identity, /handshake, /verify, /mint.
413
+ C-FED-PROTOCOL-001 for inter-instance communication: request packets, mailbox broker, canon registry, trust levels, return receipts, and the older mailbox/registry/domain architecture.
414
+ What you’re talking about now is not just more detail on either one. It’s a new layer:
415
+ downloadable/self-hosted nodes
416
+ public registry/discovery records
417
+ relay vs registry separation
418
+ capability advertisement
419
+ delegated session authority instead of root-key use
420
+ general-purpose β€œsovereign communications appliance” behavior
421
+ That is bigger than a patch note. That is a new contract boundary.
422
+ The clean read
423
+ Keep as-is
424
+ C-FED-GLYPH-001 stays focused on identity and seal mechanics.Do not bloat it with registry/discovery/service-hosting logic. It already says its scope is identity minting, validation, and exchange across Federation nodes.
425
+ Probably revise later
426
+ C-FED-PROTOCOL-001 is still valid as the communication protocol contract, but it reflects an earlier architecture:
427
+ mailbox broker
428
+ canon registry
429
+ HMAC-based signature framing
430
+ instance IDs rather than full modern node/relay/capability thinking
431
+ So it likely needs a future revision or successor.
432
+ Add now
433
+ You need a third contract for the thing Ace is sketching.
434
+ What the new contract should be
435
+ I’d name it something like:
436
+ C-FED-NODE-001 β€” Federation Node & Registry Contract
437
+ or, if you want the discovery aspect emphasized:
438
+ C-FED-REGISTRY-001 β€” Federation Node Discovery & Capability Contract
439
+ Between those two, my stronger recommendation is:
440
+ C-FED-NODE-001
441
+ Because what you’re realizing is not merely a phonebook. It’s the node as product primitive.
442
+ Then inside that contract, define:
443
+ what a node is
444
+ what records it publishes
445
+ how it advertises endpoints/capabilities
446
+ how trust/quarantine defaults work
447
+ how delegation is represented
448
+ what parts are required vs optional
449
+ And if registry grows large enough later, then split out a dedicated C-FED-REGISTRY-001.
450
+ Why a new contract is cleaner than stuffing it into the old ones
451
+ Because each current contract has a clean center:
452
+ GLYPH = who are you
453
+ PROTOCOL = how do packets move
454
+ NODE = what kind of sovereign endpoint are you, what do you expose, and how are you found
455
+ That division is elegant and scalable.
456
+ Otherwise you get drift like:
457
+ identity contract starts defining hosting behavior
458
+ protocol contract starts defining deployment topology
459
+ registry schema lives in random notes and code comments
460
+ Ace starts minting architecture out of thin air on a sugar rush
461
+ That way lies mudball.
462
+ The exact gap the new contract fills
463
+ Your current canon covers:
464
+ seal syntax and validation
465
+ packet schema and mailbox flow
466
+ It does not yet fully canonize:
467
+ NodeRecord
468
+ capability advertisement
469
+ registry lookup semantics
470
+ relay URL vs registry URL split
471
+ self-hosted node portability
472
+ delegated-visa/session authority
473
+ quarantine/trust-policy defaults at the node level
474
+ That’s the missing layer.
475
+ My recommendation for the contract stack
476
+ 1. C-FED-GLYPH-001
477
+ Identity/authentication/seals only.Leave mostly intact.
478
+ 2. C-FED-PROTOCOL-001
479
+ Envelope/message transport and semantics.Later revise to better match the newer envelope/delivery/capability model.
480
+ 3. C-FED-NODE-001 ← new
481
+ Defines:
482
+ NodeRecord
483
+ endpoint classes
484
+ capabilities advertisement
485
+ trust policy declaration
486
+ relay/registry split
487
+ node roles
488
+ portability/self-hosting expectations
489
+ 4. Optional later: C-FED-DELEGATION-001
490
+ If β€œDiplomatic Visa” becomes substantial enough, give it its own contract rather than burying token semantics in the node contract.
491
+ That may be overkill for now, but I can already see how it could earn its own seat later.
492
+ So was Ace β€œfinished”?
493
+ No. He was prematurely specific.
494
+ He had the right instinct:β€œwe need a registry/node schema.”
495
+ But he presented it like the shape was already settled, when really what you’ve just discovered is:the node itself has become a first-class contract domain.
496
+ That means you were right to go:
497
+ β€œI think we need a new contract? I thought we weren't finished yet”
498
+ Because you weren’t finished. You had crossed a boundary.
499
+ MEGA verdict
500
+ Yes:
501
+ new contract needed
502
+ Ace was sketching inside an uncanonized space
503
+ the right next move is to define that space instead of pretending it already belonged neatly to the older docs
504
+ If you want the cleanest next step, I’d draft:
505
+ C-FED-NODE-001with sections for:
506
+ Overview
507
+ Core Node Definition
508
+ Node Roles
509
+ NodeRecord Schema
510
+ Endpoint Types
511
+ Capability Advertisement
512
+ Trust Policy
513
+ Registry Publication
514
+ Relay Separation
515
+ Portability / Self-Hosting
516
+ Security / Delegation Hooks
517
+ Relationship to C-FED-GLYPH-001 and C-FED-PROTOCOL-001
518
+ That would lock the board back into shape.
models.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Federation Node β€” Data Models & Enums
3
+ ======================================
4
+ Contract: C-FED-NODE-001 v0.1.1
5
+
6
+ Refusal reason codes (Β§7.4), delivery statuses (Β§9.3),
7
+ trust tiers (Β§7), and request/response schemas.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ from datetime import datetime, timezone
12
+ from enum import Enum
13
+ from typing import Optional
14
+
15
+ from pydantic import BaseModel, Field
16
+
17
+
18
+ # ─── Refusal Reason Codes (Β§7.4) ───
19
+
20
+ class RefusalReason(str, Enum):
21
+ """Structured refusal reason codes per C-FED-NODE-001 Β§7.4."""
22
+ UNSUPPORTED_PROTOCOL_VERSION = "UNSUPPORTED_PROTOCOL_VERSION"
23
+ UNSUPPORTED_MESSAGE_CLASS = "UNSUPPORTED_MESSAGE_CLASS"
24
+ INVALID_DELEGATION = "INVALID_DELEGATION"
25
+ SENDER_NOT_TRUSTED = "SENDER_NOT_TRUSTED"
26
+ SENDER_BLOCKED = "SENDER_BLOCKED"
27
+ MALFORMED_ENVELOPE = "MALFORMED_ENVELOPE"
28
+ QUARANTINED = "QUARANTINED"
29
+ REFUSED_BY_POLICY = "REFUSED_BY_POLICY"
30
+ PAYLOAD_TOO_LARGE = "PAYLOAD_TOO_LARGE"
31
+ RATE_LIMITED = "RATE_LIMITED"
32
+ INBOX_FULL = "INBOX_FULL"
33
+
34
+
35
+ # ─── Delivery Statuses (Β§9.3) ───
36
+
37
+ class DeliveryStatus(str, Enum):
38
+ """Envelope delivery status progression per C-FED-NODE-001 Β§9.3."""
39
+ RECEIVED = "RECEIVED"
40
+ VALIDATED = "VALIDATED"
41
+ REJECTED = "REJECTED"
42
+ QUEUED = "QUEUED"
43
+ QUARANTINED = "QUARANTINED"
44
+ DELIVERED = "DELIVERED"
45
+ READ = "READ"
46
+ PROCESSED = "PROCESSED"
47
+ WITNESSED = "WITNESSED"
48
+ EXPIRED = "EXPIRED"
49
+
50
+
51
+ # ─── Trust Tiers (Β§7.2) ───
52
+
53
+ class TrustTier(str, Enum):
54
+ TRUSTED = "TRUSTED"
55
+ KNOWN = "KNOWN"
56
+ UNKNOWN = "UNKNOWN"
57
+ BLOCKED = "BLOCKED"
58
+
59
+
60
+ # ─── Refusal Response Model ───
61
+
62
+ class FederationRefusal(BaseModel):
63
+ """Structured refusal response per C-FED-NODE-001 Β§7.4."""
64
+ refusal: bool = True
65
+ reason: RefusalReason
66
+ message: str
67
+ node_seal: str
68
+ timestamp: str
69
+
70
+
71
+ # ─── Request Models ───
72
+
73
+ class HandshakeRequest(BaseModel):
74
+ """Incoming handshake from another node."""
75
+ caller_seal: str = Field(..., description="The Glyph-Seal of the caller")
76
+ protocol: str = Field(default="C-FED-001", description="Protocol version")
77
+ message: Optional[str] = Field(default=None, description="Optional signal payload")
78
+
79
+
80
+ class HandshakeResponse(BaseModel):
81
+ """Response to a handshake."""
82
+ node_seal: str
83
+ link_seal: str
84
+ status: str
85
+ message: str
86
+ timestamp: str
87
+
88
+
89
+ class VerifyRequest(BaseModel):
90
+ """Request to verify a seal string."""
91
+ seal: str
92
+
93
+
94
+ class MintRequest(BaseModel):
95
+ """Request to mint a new seal."""
96
+ class_name: str = Field(..., description="NODE, LAW, LINK, RITE, ART, WIT")
97
+ origin: str = Field(..., description="Origin namespace")
98
+ state: str = Field(default="VALID")
99
+ mode: str = Field(default="hybrid", description="random | hybrid | deterministic")
100
+ material: Optional[str] = Field(default=None, description="For deterministic mode")
101
+
102
+
103
+ class EnvelopeSubmission(BaseModel):
104
+ """Incoming envelope per C-FED-NODE-001 Β§9.1."""
105
+ protocol_version: str = Field(default="1.0.0")
106
+ message_class: str = Field(..., description="Message class (THOUGHT, PROPOSAL, etc.)")
107
+ sender: dict = Field(..., description="Sender block: {seal, delegation_token?}")
108
+ recipient: dict = Field(..., description="Recipient block: {seal}")
109
+ payload: dict = Field(..., description="Payload block: {content_type, body, ...}")
110
+ delivery: dict = Field(default_factory=lambda: {"ttl_seconds": 86400, "priority": "normal"})
111
+ signature: Optional[dict] = Field(default=None, description="Signature block (Phase 3)")
node_record.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Federation Node β€” NodeRecord Builder
3
+ ======================================
4
+ Contract: C-FED-NODE-001 v0.1.1
5
+
6
+ Generates the NodeRecord (Β§4), capability advertisement (Β§6),
7
+ conformance declaration (Β§12.3), and trust posture (Β§7).
8
+ """
9
+ from __future__ import annotations
10
+
11
+ from datetime import datetime, timezone
12
+
13
+
14
+ # ─── Constants ───
15
+
16
+ NODE_VERSION = "0.2.0"
17
+ CONTRACT_VERSION = "C-FED-NODE-001 v0.1.1"
18
+ PROTOCOL_VERSION = "1.0.0"
19
+
20
+ # Accepted message classes (Phase 1A)
21
+ ACCEPTED_MESSAGE_CLASSES = [
22
+ "HANDSHAKE_REQUEST",
23
+ "HANDSHAKE_RESPONSE",
24
+ "THOUGHT",
25
+ "PROPOSAL",
26
+ "ARTIFACT_TRANSFER",
27
+ "HEARTBEAT",
28
+ "REFUSAL",
29
+ "STATUS_UPDATE",
30
+ ]
31
+
32
+ MAX_PAYLOAD_BYTES = 5_242_880 # 5 MB
33
+ INBOX_QUOTA = 1000
34
+
35
+
36
+ # ─── Builders ───
37
+
38
+ def build_capabilities() -> dict:
39
+ """Build the capabilities advertisement per C-FED-NODE-001 Β§6."""
40
+ return {
41
+ "protocol_versions": [PROTOCOL_VERSION],
42
+ "accepted_message_classes": ACCEPTED_MESSAGE_CLASSES,
43
+ "accepted_content_types": ["text/markdown", "application/json"],
44
+ "max_payload_bytes": MAX_PAYLOAD_BYTES,
45
+ "supports_witnessing": True,
46
+ "supports_delegation": False, # Phase 3
47
+ "supports_registry": False, # Phase 2
48
+ "rate_limits": {
49
+ "envelopes_per_minute": 30,
50
+ "envelopes_per_hour": 500,
51
+ },
52
+ "inbox_quota": INBOX_QUOTA,
53
+ }
54
+
55
+
56
+ def build_conformance() -> dict:
57
+ """Build conformance declaration per C-FED-NODE-001 Β§12.3."""
58
+ return {
59
+ "contract": "C-FED-NODE-001",
60
+ "version": "0.1.1",
61
+ "level": "minimum",
62
+ "extensions": ["witnessing"],
63
+ }
64
+
65
+
66
+ def build_trust_posture(default_action: str, blocked_behavior: str) -> dict:
67
+ """Build the public trust posture per C-FED-NODE-001 Β§4."""
68
+ return {
69
+ "default_action": default_action,
70
+ "accepts_registry_known": True,
71
+ "accepts_delegation": False, # Phase 3
72
+ "blocked_behavior": blocked_behavior,
73
+ "trust_graph_public": False,
74
+ }
75
+
76
+
77
+ def build_node_record(
78
+ *,
79
+ node_identity,
80
+ alias: str,
81
+ owner: str,
82
+ relay_url: str,
83
+ roles: list[str],
84
+ default_action: str,
85
+ blocked_behavior: str,
86
+ boot_time: str,
87
+ ) -> dict:
88
+ """Build the full NodeRecord per C-FED-NODE-001 Β§4."""
89
+ return {
90
+ "identity": {
91
+ "glyph_seal": str(node_identity),
92
+ "public_key": None, # Phase 3: Ed25519
93
+ "alias": alias,
94
+ "owner_display_name": owner or None,
95
+ },
96
+ "endpoints": {
97
+ "relay_url": relay_url or None,
98
+ "registry_url": None, # Phase 2
99
+ "capabilities_url": f"{relay_url}/capabilities" if relay_url else None,
100
+ },
101
+ "capabilities": build_capabilities(),
102
+ "roles": roles,
103
+ "trust_posture": build_trust_posture(default_action, blocked_behavior),
104
+ "metadata": {
105
+ "node_version": NODE_VERSION,
106
+ "contract_version": CONTRACT_VERSION,
107
+ "protocol_version": PROTOCOL_VERSION,
108
+ "registered_at": boot_time,
109
+ "last_seen": datetime.now(timezone.utc).isoformat(),
110
+ },
111
+ }
trust.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Federation Node β€” Trust Resolution
3
+ ====================================
4
+ Contract: C-FED-NODE-001 v0.1.1
5
+
6
+ Trust tier resolution (Β§7.2) and policy enforcement (Β§7.1).
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from datetime import datetime, timezone
11
+ from typing import Optional
12
+
13
+ from models import TrustTier, RefusalReason, FederationRefusal
14
+
15
+
16
+ def make_refusal(reason: RefusalReason, message: str, node_seal: str) -> dict:
17
+ """Create a structured refusal response per Β§7.4."""
18
+ return FederationRefusal(
19
+ refusal=True,
20
+ reason=reason,
21
+ message=message,
22
+ node_seal=node_seal,
23
+ timestamp=datetime.now(timezone.utc).isoformat(),
24
+ ).model_dump()
25
+
26
+
27
+ def resolve_trust_tier(
28
+ sender_seal: str,
29
+ trusted_seals: set[str],
30
+ blocked_seals: set[str],
31
+ ) -> TrustTier:
32
+ """Resolve the trust tier for a sender based on current trust state."""
33
+ if sender_seal in blocked_seals:
34
+ return TrustTier.BLOCKED
35
+ if sender_seal in trusted_seals:
36
+ return TrustTier.TRUSTED
37
+ # TODO Phase 2: check registry for KNOWN status
38
+ return TrustTier.UNKNOWN
39
+
40
+
41
+ def apply_trust_policy(
42
+ tier: TrustTier,
43
+ default_action: str,
44
+ blocked_behavior: str,
45
+ node_seal: str,
46
+ ) -> Optional[dict]:
47
+ """
48
+ Apply the node's trust policy to a given tier.
49
+ Returns None if accepted, or a policy action dict if rejected/quarantined.
50
+ """
51
+ if tier == TrustTier.BLOCKED:
52
+ if blocked_behavior == "SILENT_DROP":
53
+ return {"action": "SILENT_DROP"}
54
+ return make_refusal(
55
+ RefusalReason.SENDER_BLOCKED,
56
+ "Sender is on the blocklist. Refusal Protocol Active.",
57
+ node_seal,
58
+ )
59
+
60
+ if tier == TrustTier.TRUSTED:
61
+ return None # Accepted immediately
62
+
63
+ # UNKNOWN and KNOWN tiers: apply default action
64
+ if default_action == "REFUSE":
65
+ return make_refusal(
66
+ RefusalReason.SENDER_NOT_TRUSTED,
67
+ "Sender not in trusted list. Default policy: REFUSE.",
68
+ node_seal,
69
+ )
70
+
71
+ if default_action == "QUARANTINE":
72
+ return {"action": "QUARANTINE"}
73
+
74
+ return None # ACCEPT
verify_behavior.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Verification Script for Phase 1A (C-FED-NODE-001)
3
+ Perform a self-handshake and envelope injection test.
4
+ """
5
+ import sys
6
+ import json
7
+ from fastapi.testclient import TestClient
8
+
9
+ # Import the configured federation node app
10
+ from app import app, NODE_SEAL_STR
11
+
12
+ client = TestClient(app)
13
+
14
+ def run_verification():
15
+ print("="*60)
16
+ print("πŸš€ PANTHEON FEDERATION NODE β€” BEHAVIOR VERIFICATION")
17
+ print("="*60)
18
+
19
+ # 1. Verify Identity
20
+ print("\n[TEST 1] GET /identity")
21
+ res = client.get("/identity")
22
+ assert res.status_code == 200
23
+ iden = res.json()
24
+ print(f" Seal: {iden['identity']['glyph_seal']}")
25
+ print(f" Alias: {iden['identity']['alias']}")
26
+ print(" βœ… Identity endpoint returned valid NodeRecord")
27
+
28
+ # 2. Verify Capabilities
29
+ print("\n[TEST 2] GET /capabilities")
30
+ res = client.get("/capabilities")
31
+ assert res.status_code == 200
32
+ caps = res.json()
33
+ print(f" Conformance: {caps['conformance']['level']} (Contract: {caps['conformance']['contract']})")
34
+ print(" βœ… Capabilities endpoint returned valid structure")
35
+
36
+ # 3. Malformed Sender Seal (The Malenia Rule)
37
+ print("\n[TEST 3] POST /envelope (Malformed Sender Seal)")
38
+ mal_env = {
39
+ "protocol_version": "1.0.0",
40
+ "message_class": "THOUGHT",
41
+ "sender": {"seal": "⟦ INVALID :: SYNTAX ⟧"},
42
+ "recipient": {"seal": NODE_SEAL_STR},
43
+ "payload": {"content_type": "text/plain", "body": "Hello"}
44
+ }
45
+ res = client.post("/envelope", json=mal_env)
46
+ assert res.status_code == 400
47
+ err = res.json()["detail"]
48
+ print(f" Refusal Reason: {err['reason']}")
49
+ print(" βœ… Malenia Rule enforced (structured refusal)")
50
+
51
+ # 4. Unknown Sender -> Quarantine (Default Policy)
52
+ print("\n[TEST 4] POST /envelope (Unknown Sender)")
53
+ unk_env = {
54
+ "protocol_version": "1.0.0",
55
+ "message_class": "THOUGHT",
56
+ "sender": {"seal": "⟦ NODE :: TEST-ORIGIN :: anchor123 :: ACTIVE ⟧"},
57
+ "recipient": {"seal": NODE_SEAL_STR},
58
+ "payload": {"content_type": "text/plain", "body": "Knock knock"}
59
+ }
60
+ res = client.post("/envelope", json=unk_env)
61
+ assert res.status_code == 200
62
+ data = res.json()
63
+ env_id = data["envelope_id"]
64
+ print(f" Result: {data['status']}")
65
+ print(" βœ… Unknown sender quarantined correctly according to FEDERATION_DEFAULT_ACTION")
66
+
67
+ # 5. Delivery Status Progression
68
+ print("\n[TEST 5] GET /envelope/{id}/status")
69
+ res = client.get(f"/envelope/{env_id}/status")
70
+ assert res.status_code == 200
71
+ status = res.json()
72
+ history = [h["status"] for h in status["status_history"]]
73
+ print(f" History: {' -> '.join(history)}")
74
+ print(" βœ… Envelope states progressed properly")
75
+
76
+ # 6. Trust Establishment (Handshake)
77
+ print("\n[TEST 6] POST /handshake (Trust Promotion)")
78
+ hs_req = {
79
+ "caller_seal": "⟦ NODE :: FRIENDLY-ORIGIN :: frnd123 :: ACTIVE ⟧",
80
+ "protocol": "C-FED-001"
81
+ }
82
+ res = client.post("/handshake", json=hs_req)
83
+ assert res.status_code == 200
84
+ print(f" Handshake result: {res.json()['status']}")
85
+ print(" βœ… Sender promoted to TRUSTED")
86
+
87
+ # 7. Trusted Sender -> Queued
88
+ print("\n[TEST 7] POST /envelope (Trusted Sender)")
89
+ trusted_env = {
90
+ "protocol_version": "1.0.0",
91
+ "message_class": "PROPOSAL",
92
+ "sender": {"seal": "⟦ NODE :: FRIENDLY-ORIGIN :: frnd123 :: ACTIVE ⟧"},
93
+ "recipient": {"seal": NODE_SEAL_STR},
94
+ "payload": {"content_type": "text/plain", "body": "I am a friend now."}
95
+ }
96
+ res = client.post("/envelope", json=trusted_env)
97
+ assert res.status_code == 200
98
+ print(f" Result: {res.json()['status']}")
99
+ print(" βœ… Envelope bypassed quarantine and was QUEUED directly")
100
+
101
+ print("\n" + "="*60)
102
+ print("πŸŽ‰ ALL PHASE 1A VERIFICATION CHECKS PASSED πŸŽ‰")
103
+ print("="*60)
104
+
105
+ if __name__ == "__main__":
106
+ run_verification()