mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-08-17 22:10:34 +00:00
Add apiProvider service kind, credential gateway, and cupfox routing
- TopologyNode.lean: apiProvider service kind with network-only requirement - server.py: OP_CREDENTIALS handler, RDS-backed wiki/fractal fold layers, /credentials HTTP endpoint via SurfaceHandler - cupfox-config.nix: Caddy routes for /api/credentials/ -> EC2 (100.69.1.43) and /git/ -> local Forgejo
This commit is contained in:
parent
c957fac96e
commit
9a97983a64
3 changed files with 109 additions and 15 deletions
|
|
@ -68,6 +68,7 @@ inductive ServiceKind where
|
||||||
| substrateIndex
|
| substrateIndex
|
||||||
| rgflowFilter
|
| rgflowFilter
|
||||||
| tardyInterpreter
|
| tardyInterpreter
|
||||||
|
| apiProvider
|
||||||
deriving Repr, DecidableEq, Inhabited, BEq
|
deriving Repr, DecidableEq, Inhabited, BEq
|
||||||
|
|
||||||
/-- Hardware requirements per service. -/
|
/-- Hardware requirements per service. -/
|
||||||
|
|
@ -79,6 +80,7 @@ def serviceRequirements : ServiceKind → List HardwareCapability
|
||||||
| .substrateIndex => [.storage, .compute]
|
| .substrateIndex => [.storage, .compute]
|
||||||
| .rgflowFilter => [.compute, .network]
|
| .rgflowFilter => [.compute, .network]
|
||||||
| .tardyInterpreter => [.compute]
|
| .tardyInterpreter => [.compute]
|
||||||
|
| .apiProvider => [.network]
|
||||||
|
|
||||||
/-- Thermodynamic cost to run a service (Q16.16 per tick). -/
|
/-- Thermodynamic cost to run a service (Q16.16 per tick). -/
|
||||||
def serviceCost : ServiceKind → Q16_16
|
def serviceCost : ServiceKind → Q16_16
|
||||||
|
|
@ -89,6 +91,7 @@ def serviceCost : ServiceKind → Q16_16
|
||||||
| .substrateIndex => ofNat 2
|
| .substrateIndex => ofNat 2
|
||||||
| .rgflowFilter => ofNat 3
|
| .rgflowFilter => ofNat 3
|
||||||
| .tardyInterpreter => ofNat 1
|
| .tardyInterpreter => ofNat 1
|
||||||
|
| .apiProvider => ofNat 1
|
||||||
|
|
||||||
-- ═══════════════════════════════════════════════════════════════════════════
|
-- ═══════════════════════════════════════════════════════════════════════════
|
||||||
-- §3 Node State Machine
|
-- §3 Node State Machine
|
||||||
|
|
@ -236,5 +239,7 @@ def exampleFoxTopNode : TopologyNode :=
|
||||||
#eval canRunService exampleCoreNode ServiceKind.architect
|
#eval canRunService exampleCoreNode ServiceKind.architect
|
||||||
#eval canRunService exampleEdgeNode ServiceKind.architect
|
#eval canRunService exampleEdgeNode ServiceKind.architect
|
||||||
#eval canRunService exampleEdgeNode ServiceKind.warden
|
#eval canRunService exampleEdgeNode ServiceKind.warden
|
||||||
|
#eval canRunService exampleEdgeNode ServiceKind.apiProvider
|
||||||
|
#eval serviceCost ServiceKind.apiProvider
|
||||||
|
|
||||||
end Semantics.TopologyNode
|
end Semantics.TopologyNode
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ OP_PLAN_ROUTE = 11
|
||||||
OP_WIKI = 12
|
OP_WIKI = 12
|
||||||
OP_FRACTAL_FOLD = 13
|
OP_FRACTAL_FOLD = 13
|
||||||
OP_META_AUTOTYPE = 14
|
OP_META_AUTOTYPE = 14
|
||||||
|
OP_CREDENTIALS = 15
|
||||||
|
|
||||||
CODEC_NONE = 0
|
CODEC_NONE = 0
|
||||||
CODEC_ZLIB_TEST = 1
|
CODEC_ZLIB_TEST = 1
|
||||||
|
|
@ -139,6 +140,7 @@ def primitive_payload() -> dict[str, Any]:
|
||||||
"wiki",
|
"wiki",
|
||||||
"fractal_fold",
|
"fractal_fold",
|
||||||
"meta_autotype",
|
"meta_autotype",
|
||||||
|
"credentials",
|
||||||
]
|
]
|
||||||
return {
|
return {
|
||||||
"node": PROFILE["node_id"],
|
"node": PROFILE["node_id"],
|
||||||
|
|
@ -267,15 +269,25 @@ def handle_surface_op(op: int, payload: bytes) -> dict[str, Any]:
|
||||||
request = json.loads(payload.decode("utf-8")) if payload else {"op": "recent"}
|
request = json.loads(payload.decode("utf-8")) if payload else {"op": "recent"}
|
||||||
if not isinstance(request, dict):
|
if not isinstance(request, dict):
|
||||||
raise ValueError("wiki payload must be a JSON object")
|
raise ValueError("wiki payload must be a JSON object")
|
||||||
try:
|
use_rds = os.environ.get("RS_USE_RDS", "").lower() in ("1", "true", "yes")
|
||||||
from infra.ene_wiki_layer import ENEWikiLayer
|
if use_rds:
|
||||||
except ModuleNotFoundError:
|
try:
|
||||||
import sys
|
from infra.ene_rds_wiki_layer import ENERDSWikiLayer
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
||||||
|
from infra.ene_rds_wiki_layer import ENERDSWikiLayer
|
||||||
|
|
||||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
wiki = ENERDSWikiLayer()
|
||||||
from infra.ene_wiki_layer import ENEWikiLayer
|
else:
|
||||||
|
try:
|
||||||
|
from infra.ene_wiki_layer import ENEWikiLayer
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
||||||
|
from infra.ene_wiki_layer import ENEWikiLayer
|
||||||
|
|
||||||
wiki = ENEWikiLayer(STATE_DIR / "ene-wiki.db")
|
wiki = ENEWikiLayer(STATE_DIR / "ene-wiki.db")
|
||||||
return wiki.handle_request(request)
|
return wiki.handle_request(request)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
return {"ok": False, "op": "wiki", "error": str(exc)}
|
return {"ok": False, "op": "wiki", "error": str(exc)}
|
||||||
|
|
@ -284,15 +296,25 @@ def handle_surface_op(op: int, payload: bytes) -> dict[str, Any]:
|
||||||
request = json.loads(payload.decode("utf-8")) if payload else {"op": "manifest"}
|
request = json.loads(payload.decode("utf-8")) if payload else {"op": "manifest"}
|
||||||
if not isinstance(request, dict):
|
if not isinstance(request, dict):
|
||||||
raise ValueError("fractal_fold payload must be a JSON object")
|
raise ValueError("fractal_fold payload must be a JSON object")
|
||||||
try:
|
use_rds = os.environ.get("RS_USE_RDS", "").lower() in ("1", "true", "yes")
|
||||||
from infra.ene_fractal_fold import ENEFractalFold
|
if use_rds:
|
||||||
except ModuleNotFoundError:
|
try:
|
||||||
import sys
|
from infra.ene_rds_fractal_fold import ENERDSFractalFold
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
||||||
|
from infra.ene_rds_fractal_fold import ENERDSFractalFold
|
||||||
|
|
||||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
fold = ENERDSFractalFold()
|
||||||
from infra.ene_fractal_fold import ENEFractalFold
|
else:
|
||||||
|
try:
|
||||||
|
from infra.ene_fractal_fold import ENEFractalFold
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
||||||
|
from infra.ene_fractal_fold import ENEFractalFold
|
||||||
|
|
||||||
fold = ENEFractalFold(STATE_DIR / "ene-fractal-fold.db")
|
fold = ENEFractalFold(STATE_DIR / "ene-fractal-fold.db")
|
||||||
return fold.handle_request(request)
|
return fold.handle_request(request)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
return {"ok": False, "op": "fractal_fold", "error": str(exc)}
|
return {"ok": False, "op": "fractal_fold", "error": str(exc)}
|
||||||
|
|
@ -312,6 +334,50 @@ def handle_surface_op(op: int, payload: bytes) -> dict[str, Any]:
|
||||||
return handle_request(request)
|
return handle_request(request)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
return {"ok": False, "op": "meta_autotype", "error": str(exc)}
|
return {"ok": False, "op": "meta_autotype", "error": str(exc)}
|
||||||
|
if op == OP_CREDENTIALS:
|
||||||
|
try:
|
||||||
|
request = json.loads(payload.decode("utf-8")) if payload else {}
|
||||||
|
if not isinstance(request, dict):
|
||||||
|
raise ValueError("credentials payload must be a JSON object")
|
||||||
|
try:
|
||||||
|
from credential_provider import (
|
||||||
|
credential_status,
|
||||||
|
provider_manifest,
|
||||||
|
resolve_credential,
|
||||||
|
)
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
try:
|
||||||
|
from infra.credential_provider import (
|
||||||
|
credential_status,
|
||||||
|
provider_manifest,
|
||||||
|
resolve_credential,
|
||||||
|
)
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||||
|
from credential_provider import (
|
||||||
|
credential_status,
|
||||||
|
provider_manifest,
|
||||||
|
resolve_credential,
|
||||||
|
)
|
||||||
|
|
||||||
|
action = request.get("action", "status")
|
||||||
|
if action == "status":
|
||||||
|
return credential_status()
|
||||||
|
if action == "manifest":
|
||||||
|
return provider_manifest()
|
||||||
|
if action == "resolve":
|
||||||
|
provider = request.get("provider", "")
|
||||||
|
if not provider:
|
||||||
|
return {"ok": False, "error": "missing provider name"}
|
||||||
|
cred = resolve_credential(provider)
|
||||||
|
if cred is None:
|
||||||
|
return {"ok": False, "error": f"provider {provider!r} not available"}
|
||||||
|
return {"ok": True, "provider": cred.provider, "value": cred.value}
|
||||||
|
return {"ok": False, "error": f"unknown credentials action {action!r}"}
|
||||||
|
except Exception as exc:
|
||||||
|
return {"ok": False, "op": "credentials", "error": str(exc)}
|
||||||
return {"error": "unknown-op", "op": op}
|
return {"error": "unknown-op", "op": op}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -422,6 +488,20 @@ class SurfaceHandler(BaseHTTPRequestHandler):
|
||||||
if self.path == "/ws":
|
if self.path == "/ws":
|
||||||
self.handle_ws()
|
self.handle_ws()
|
||||||
return
|
return
|
||||||
|
if self.path == "/credentials":
|
||||||
|
try:
|
||||||
|
from credential_provider import credential_status
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
try:
|
||||||
|
from infra.credential_provider import credential_status
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||||
|
from credential_provider import credential_status
|
||||||
|
|
||||||
|
self.send_json(credential_status())
|
||||||
|
return
|
||||||
self.send_json({"error": "not-found"}, HTTPStatus.NOT_FOUND)
|
self.send_json({"error": "not-found"}, HTTPStatus.NOT_FOUND)
|
||||||
|
|
||||||
def handle_ws(self) -> None:
|
def handle_ws(self) -> None:
|
||||||
|
|
|
||||||
|
|
@ -52,13 +52,22 @@ in
|
||||||
logFormat = "level INFO";
|
logFormat = "level INFO";
|
||||||
package = caddyWithPorkbun;
|
package = caddyWithPorkbun;
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
researchstack.info, cupfox.researchstack.info, http://100.126.151.57 {
|
researchstack.info, cupfox.researchstack.info, git.researchstack.info, http://100.126.151.57 {
|
||||||
tls {
|
tls {
|
||||||
dns porkbun {
|
dns porkbun {
|
||||||
api_key {$PORKBUN_API_KEY}
|
api_key {$PORKBUN_API_KEY}
|
||||||
api_secret_key {$PORKBUN_SECRET_KEY}
|
api_secret_key {$PORKBUN_SECRET_KEY}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handle_path /api/credentials/* {
|
||||||
|
reverse_proxy http://100.69.1.43:8444
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_path /git/* {
|
||||||
|
reverse_proxy http://127.0.0.1:3000
|
||||||
|
}
|
||||||
|
|
||||||
root * /var/www/researchstack
|
root * /var/www/researchstack
|
||||||
file_server
|
file_server
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue