File size: 9,340 Bytes
25b7932
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
#!/usr/bin/env python3
"""Substrate Mesh Runtime — Primordial OS Runtime Connector API Wrapper v3.3.1.

FastAPI HTTPS-ready wrapper for GPT Actions. Designed for Hugging Face Docker Space deployment.
Boundary: read-only canonical source/lexicon posture; workspace-local overlay proposals only.
"""
from __future__ import annotations

import json
import os
import re
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, List, Optional

from fastapi import Depends, FastAPI, Header, HTTPException, Request
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field

from src.substrate_connector_core import submit_mesh_command

APP_VERSION = "v3.3.1"
PRESSURE_SPINE = "HIR × OAM"
DEFAULT_RECEIPT_ROOT = Path(os.getenv("SUBSTRATE_RECEIPT_ROOT", "runs/api_receipts"))

app = FastAPI(
    title="Substrate Mesh Runtime Connector API",
    version=APP_VERSION,
    description=(
        "HTTPS API wrapper for Primordial OS Runtime GPT Actions. Routes bounded mesh command "
        "capsules into Substrate connector responses with trace capsules, PPT element matches, "
        "Primordial Periodic Alchemical Output, HIR × OAM receipts, lexicon state, audit pointers, "
        "and workspace-local overlay proposals."
    ),
)

# Helpful for browser/dev testing. Auth still protects command/receipt routes.
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=False,
    allow_methods=["GET", "POST", "OPTIONS"],
    allow_headers=["Authorization", "Content-Type"],
)


def utc_now() -> str:
    return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")


def safe_id(value: str, fallback: str = "workspace_local") -> str:
    value = (value or fallback).strip()
    cleaned = re.sub(r"[^A-Za-z0-9_\-]", "_", value)[:96]
    return cleaned or fallback


def require_bearer(authorization: Optional[str] = Header(default=None)) -> str:
    expected = os.getenv("SUBSTRATE_ACTION_KEY")
    if not expected:
        # Fail closed for deployed environments. The validator sets this env var.
        raise HTTPException(status_code=500, detail="SUBSTRATE_ACTION_KEY is not configured")
    if not authorization or not authorization.startswith("Bearer "):
        raise HTTPException(status_code=401, detail="Missing bearer token")
    token = authorization.split(" ", 1)[1].strip()
    if token != expected:
        raise HTTPException(status_code=403, detail="Invalid bearer token")
    return "authorized"


class MeshCommand(BaseModel):
    command_type: str = Field(default="DISCOVER", description="DISCOVER, BRAINSTORM, COMBINE, EXTEND_RESEARCH, OAM_READ, LEXICON_REVIEW, THREADROOM_POST")
    raw_user_text: str = Field(..., min_length=1, description="Natural-language user command or threadroom post.")
    workspace_id: str = Field(default="workspace_local_001", description="Workspace scope; used for isolated local receipt storage.")
    session_id: str = Field(default="session_local", description="Session/user alias for contributor credit.")
    target_nodes: List[str] = Field(default_factory=list)
    hashtags: List[str] = Field(default_factory=list)
    mention_refs: List[str] = Field(default_factory=list)
    minutes_budget: float = Field(default=5.0)
    claim_boundary: str = Field(default="no final validation claims; human review required")
    privacy_scope: str = Field(default="PRIVATE_WORKSPACE")
    source_mutation_allowed: bool = Field(default=False)


class HealthResponse(BaseModel):
    status: str
    version: str
    pressure_spine: str
    source_mutation_allowed: bool
    canonical_source_posture: str
    canonical_lexicon_posture: str
    overlay_scope: str
    human_review_required: bool
    auth_configured: bool
    timestamp_utc: str


def receipt_paths(workspace_id: str, command_id: str) -> Dict[str, Path]:
    w = safe_id(workspace_id)
    c = safe_id(command_id)
    folder = DEFAULT_RECEIPT_ROOT / w / c
    return {
        "folder": folder,
        "response": folder / "response.json",
        "audit": folder / "audit_log.jsonl",
        "receipt_md": folder / "receipt.md",
    }


def persist_response(response: Dict[str, Any], request_meta: Dict[str, Any]) -> Dict[str, str]:
    paths = receipt_paths(response.get("workspace_id", "workspace_local"), response.get("command_id", "cmd_unknown"))
    paths["folder"].mkdir(parents=True, exist_ok=True)
    response = dict(response)
    response.setdefault("api_wrapper", {})
    response["api_wrapper"].update({
        "version": APP_VERSION,
        "pressure_spine": PRESSURE_SPINE,
        "persisted_at_utc": utc_now(),
        "receipt_folder": str(paths["folder"]),
    })
    # Normalize visible HIR × OAM glyph even if legacy core returns HIR x OAM.
    if isinstance(response.get("receipt"), dict):
        response["receipt"]["pressure_spine"] = PRESSURE_SPINE
    paths["response"].write_text(json.dumps(response, indent=2, ensure_ascii=False), encoding="utf-8")
    audit = {
        "timestamp_utc": utc_now(),
        "event": "api_mesh_command_completed",
        "wrapper_version": APP_VERSION,
        "command_id": response.get("command_id"),
        "workspace_id": response.get("workspace_id"),
        "status": response.get("status"),
        "pressure_spine": PRESSURE_SPINE,
        "source_mutation": response.get("receipt", {}).get("source_mutation"),
        "human_review_required": response.get("human_review_required"),
        "request_meta": request_meta,
    }
    with paths["audit"].open("a", encoding="utf-8") as f:
        f.write(json.dumps(audit, ensure_ascii=False) + "\n")
    alch = response.get("alchemical_output", {})
    receipt = response.get("receipt", {})
    md = f"""# Substrate Mesh Command API Receipt

Command ID: `{response.get('command_id')}`

Workspace: `{response.get('workspace_id')}`

Status: `{response.get('status')}`

Pressure spine: `{PRESSURE_SPINE}`

Source mutation: `{receipt.get('source_mutation')}`

Human review required: `{response.get('human_review_required')}`

## Reference Point

{alch.get('reference_point','')}

## Continuity Read

{alch.get('continuity_read','')}

## Strain Points

"""
    for item in alch.get("strain_points", []):
        md += f"- {item}\n"
    md += "\n## Future Routes\n\n"
    for item in alch.get("future_routes", []):
        md += f"- {item}\n"
    paths["receipt_md"].write_text(md, encoding="utf-8")
    return {"response_path": str(paths["response"]), "receipt_md_path": str(paths["receipt_md"]), "audit_log_path": str(paths["audit"])}


@app.get("/v1/mesh/health", response_model=HealthResponse, operation_id="getSubstrateHealth")
def get_substrate_health() -> HealthResponse:
    return HealthResponse(
        status="HELD",
        version=APP_VERSION,
        pressure_spine=PRESSURE_SPINE,
        source_mutation_allowed=False,
        canonical_source_posture="READ_ONLY",
        canonical_lexicon_posture="READ_ONLY",
        overlay_scope="WORKSPACE_LOCAL_OVERLAY_PROPOSAL_ONLY",
        human_review_required=True,
        auth_configured=bool(os.getenv("SUBSTRATE_ACTION_KEY")),
        timestamp_utc=utc_now(),
    )


@app.post("/v1/mesh/command", operation_id="submitMeshCommand")
def submit_command(command: MeshCommand, request: Request, _: str = Depends(require_bearer)) -> Dict[str, Any]:
    if command.source_mutation_allowed:
        raise HTTPException(status_code=400, detail="Canonical source mutation is not allowed by this connector")
    capsule = command.model_dump()
    capsule["claim_boundary"] = command.claim_boundary or "no final validation claims; human review required"
    capsule["source_mutation_allowed"] = False
    response = submit_mesh_command(capsule)
    response.setdefault("boundary", {})
    response["boundary"].update({
        "canonical_sources": "READ_ONLY",
        "canonical_lexicon": "READ_ONLY",
        "workspace_storage": "ISOLATED_BY_WORKSPACE_ID",
        "write_scope": "OVERLAY_PROPOSAL_ONLY",
        "public_or_canonical_update": "NOT_ALLOWED_BY_API_WRAPPER",
        "human_review_required": True,
    })
    request_meta = {
        "client_host": getattr(request.client, "host", None),
        "command_type": command.command_type,
        "privacy_scope": command.privacy_scope,
        "minutes_budget": command.minutes_budget,
    }
    paths = persist_response(response, request_meta)
    response["api_receipt_paths"] = paths
    return response


@app.get("/v1/mesh/receipt/{command_id}", operation_id="getMeshReceipt")
def get_receipt(command_id: str, workspace_id: str = "workspace_local_001", _: str = Depends(require_bearer)) -> Dict[str, Any]:
    paths = receipt_paths(workspace_id, command_id)
    if not paths["response"].exists():
        raise HTTPException(status_code=404, detail="Receipt not found for command_id/workspace_id")
    return json.loads(paths["response"].read_text(encoding="utf-8"))


@app.get("/", include_in_schema=False)
def root() -> Dict[str, Any]:
    return {
        "name": "Substrate Mesh Runtime Connector API",
        "version": APP_VERSION,
        "health": "/v1/mesh/health",
        "docs": "/docs",
        "openapi": "/openapi.json",
        "boundary": "read-only canonical source/lexicon; workspace-local overlay proposals only; human review required",
    }