mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
New infrastructure components: - rds_probe: Rust database inspection tool with IAM auth - credential_server.py: REST credential provider server - credential_provider.py: credential resolution chain - ene_rds_fractal_fold.py / ene_rds_wiki_layer.py: RDS-backed ENE layers - import_dumps_to_rds.py / export_linear_from_rds.py: ingestion pipeline - recover_credential_server.sh: deployment script (sanitized) Sanitize hardcoded secrets across codebase: - Strip API keys from recover_credential_server.sh → env var lookups - Replace hardcoded Wolfram appid (HYJE3R3R63) → env var in 5 scripts - Strip fallback key values from config/index.js - Add .claude/ and optimized_basis_v3.bin to .gitignore Ingested 2,685 records into RDS: 2,421 Linear issues + 264 wiki pages
319 lines
12 KiB
Python
319 lines
12 KiB
Python
"""Credential server — REST + OpenAPI access point for API keys.
|
|
|
|
Reads credentials from credential_provider (env vars + config file + RDS)
|
|
and serves them via HTTP so no script needs direct env var access.
|
|
|
|
Usage:
|
|
# Standalone
|
|
python3 credential_server.py [--port 8444] [--bind 0.0.0.0]
|
|
|
|
# Via existing embedded surface (if RS_CREDENTIAL_SERVER_PORT is set)
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
import urllib.request
|
|
import urllib.parse
|
|
from http import HTTPStatus
|
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from credential_provider import (
|
|
credential_status,
|
|
provider_manifest,
|
|
resolve_credential,
|
|
_rds_connect,
|
|
_cred_server_url
|
|
)
|
|
|
|
VERSION = "0.4"
|
|
OPENAPI_VERSION = "3.0.3"
|
|
|
|
OPENAPI_SPEC: dict[str, Any] = {
|
|
"openapi": OPENAPI_VERSION,
|
|
"info": {
|
|
"title": "Research Stack Credential Provider",
|
|
"description": "Central API key access point for all stack services. "
|
|
"Set keys once on the microVM, consume from anywhere.",
|
|
"version": VERSION,
|
|
},
|
|
"servers": [
|
|
{"url": "http://localhost:8444", "description": "Local / Tailnet default"},
|
|
],
|
|
"paths": {
|
|
"/": {
|
|
"get": {
|
|
"summary": "Service root",
|
|
"responses": {"200": {"description": "Service info + links"}},
|
|
}
|
|
},
|
|
"/health": {
|
|
"get": {
|
|
"summary": "Health check",
|
|
"responses": {"200": {"description": "OK"}},
|
|
}
|
|
},
|
|
"/openapi.json": {
|
|
"get": {
|
|
"summary": "OpenAPI 3.0 specification",
|
|
"responses": {"200": {"description": "This document"}},
|
|
}
|
|
},
|
|
"/credentials": {
|
|
"get": {
|
|
"summary": "List all available credential providers",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Provider manifest",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"providers": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"properties": {
|
|
"name": {"type": "string"},
|
|
"description": {"type": "string"},
|
|
"available": {"type": "boolean"},
|
|
},
|
|
},
|
|
}
|
|
},
|
|
}
|
|
}
|
|
},
|
|
}
|
|
},
|
|
}
|
|
},
|
|
"/credentials/{provider}": {
|
|
"get": {
|
|
"summary": "Resolve a specific provider credential",
|
|
"parameters": [
|
|
{
|
|
"name": "provider",
|
|
"in": "path",
|
|
"required": True,
|
|
"schema": {"type": "string"},
|
|
"description": "Provider name (deepseek, bedrock, etc.)",
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {"description": "Credential value"},
|
|
"404": {"description": "Provider not found"},
|
|
},
|
|
}
|
|
},
|
|
"/status": {
|
|
"get": {
|
|
"summary": "Credential provider status",
|
|
"responses": {"200": {"description": "Backend info + counts"}},
|
|
}
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
SERVICE_INFO: dict[str, Any] = {
|
|
"service": "credential-provider",
|
|
"version": VERSION,
|
|
"docs": "/openapi.json",
|
|
"credentials": "/credentials",
|
|
"status": "/status",
|
|
"health": "/health",
|
|
}
|
|
|
|
|
|
class CredentialHTTPHandler(BaseHTTPRequestHandler):
|
|
|
|
def log_message(self, fmt, *args):
|
|
sys.stderr.write(f"[credential-server] {args[0]} {args[1]} {args[2]}\n")
|
|
|
|
def _send_json(self, data: Any, status: int = 200):
|
|
body = json.dumps(data, sort_keys=True, indent=2).encode("utf-8") + b"\n"
|
|
self.send_response(status)
|
|
self.send_header("Content-Type", "application/json")
|
|
self.send_header("Content-Length", str(len(body)))
|
|
self.send_header("Access-Control-Allow-Origin", "*")
|
|
self.end_headers()
|
|
self.wfile.write(body)
|
|
|
|
def _send_error(self, status: int, message: str):
|
|
self._send_json({"ok": False, "error": message}, status)
|
|
|
|
def do_GET(self):
|
|
path = self.path.rstrip("/") or "/"
|
|
|
|
if path in ("/", "/api"):
|
|
return self._send_json(SERVICE_INFO)
|
|
|
|
if path in ("/health", "/api/health"):
|
|
return self._send_json({"status": "ok", "version": VERSION})
|
|
|
|
if path == "/openapi.json":
|
|
return self._send_json(OPENAPI_SPEC)
|
|
|
|
if path in ("/status", "/api/status"):
|
|
return self._send_json(credential_status())
|
|
|
|
if path == "/credentials" or path == "/api/credentials":
|
|
return self._send_json(provider_manifest())
|
|
|
|
m = re.match(r"^(?:/api)?/credentials/([a-zA-Z0-9_-]+)$", path)
|
|
if m:
|
|
provider = m.group(1).lower()
|
|
cred = resolve_credential(provider)
|
|
if cred is None:
|
|
return self._send_error(404, f"provider '{provider}' not found")
|
|
return self._send_json({
|
|
"ok": True,
|
|
"provider": cred.provider,
|
|
"key": cred.value,
|
|
"key_name": cred.key_name,
|
|
})
|
|
|
|
return self._send_error(404, f"not found: {path}")
|
|
|
|
def do_POST(self):
|
|
path = self.path.rstrip("/") or "/"
|
|
|
|
if path in ("/api/webhooks/linear", "/webhooks/linear"):
|
|
# Read raw body
|
|
content_length = int(self.headers.get("Content-Length", 0))
|
|
raw_body = self.rfile.read(content_length)
|
|
|
|
# Node identification logic: proxy or process
|
|
node_id = os.environ.get("RS_SURFACE_NODE_ID", "")
|
|
if node_id != "MicroVM-Racknerd":
|
|
# Act as Proxy: Forward POST request to the central microVM
|
|
target_url = f"{_cred_server_url}/api/webhooks/linear"
|
|
req = urllib.request.Request(target_url, data=raw_body, method="POST")
|
|
for k, v in self.headers.items():
|
|
if k.lower() in ("content-type", "linear-signature"):
|
|
req.add_header(k, v)
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=10) as resp:
|
|
resp_data = json.loads(resp.read().decode("utf-8"))
|
|
return self._send_json(resp_data, resp.status)
|
|
except urllib.error.HTTPError as e:
|
|
try:
|
|
err_data = json.loads(e.read().decode("utf-8"))
|
|
return self._send_json(err_data, e.code)
|
|
except Exception:
|
|
return self._send_error(e.code, f"Forwarding failed: {e}")
|
|
except Exception as e:
|
|
return self._send_error(500, f"Forwarding connection error: {e}")
|
|
|
|
# Act as Server: Process Linear webhook body and upsert into Aurora RDS ENE packages table
|
|
try:
|
|
data = json.loads(raw_body.decode("utf-8"))
|
|
except Exception as e:
|
|
return self._send_error(400, f"Invalid JSON payload: {e}")
|
|
|
|
action = data.get("action")
|
|
issue_data = data.get("data", {})
|
|
payload_type = data.get("type")
|
|
|
|
if payload_type != "Issue":
|
|
return self._send_json({"ok": True, "message": f"Ignored non-Issue payload: {payload_type}"})
|
|
|
|
identifier = issue_data.get("identifier")
|
|
if not identifier:
|
|
return self._send_error(400, "Missing issue identifier")
|
|
|
|
pkg_id = f"linear/{identifier}"
|
|
|
|
if action == "remove":
|
|
try:
|
|
conn = _rds_connect()
|
|
cur = conn.cursor()
|
|
cur.execute("DELETE FROM ene.packages WHERE pkg = %s", (pkg_id,))
|
|
conn.commit()
|
|
conn.close()
|
|
return self._send_json({"ok": True, "action": "delete", "pkg": pkg_id})
|
|
except Exception as e:
|
|
return self._send_error(500, f"Database delete error: {e}")
|
|
|
|
# Extract title and description
|
|
title = issue_data.get("title", "")
|
|
description = issue_data.get("description", "")
|
|
full_text = f"{title}\n\n{description}" if description else title
|
|
url = issue_data.get("url", "")
|
|
|
|
# Extract labels
|
|
labels_data = issue_data.get("labels", [])
|
|
labels_list = []
|
|
if isinstance(labels_data, list):
|
|
for l in labels_data:
|
|
if isinstance(l, dict):
|
|
labels_list.append(l.get("name"))
|
|
elif isinstance(l, str):
|
|
labels_list.append(l)
|
|
tags_json = json.dumps(labels_list)
|
|
|
|
import datetime
|
|
now_iso = datetime.datetime.now(datetime.timezone.utc).isoformat()
|
|
|
|
try:
|
|
conn = _rds_connect()
|
|
cur = conn.cursor()
|
|
cur.execute("""
|
|
INSERT INTO ene.packages (
|
|
pkg, version, domain, tier, archetype,
|
|
tags, description, source, indexed_utc
|
|
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
|
|
ON CONFLICT (pkg) DO UPDATE SET
|
|
description = EXCLUDED.description,
|
|
tags = EXCLUDED.tags,
|
|
source = EXCLUDED.source,
|
|
indexed_utc = EXCLUDED.indexed_utc
|
|
""", (
|
|
pkg_id,
|
|
"1.0.0",
|
|
"LINEAR",
|
|
"INTENT",
|
|
"issue",
|
|
tags_json,
|
|
full_text,
|
|
url,
|
|
now_iso
|
|
))
|
|
conn.commit()
|
|
conn.close()
|
|
return self._send_json({"ok": True, "action": action, "pkg": pkg_id})
|
|
except Exception as e:
|
|
return self._send_error(500, f"Database upsert error: {e}")
|
|
|
|
return self._send_error(404, f"not found: {path}")
|
|
|
|
do_HEAD = do_GET
|
|
|
|
|
|
def main():
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description="Research Stack Credential Server")
|
|
parser.add_argument("--port", type=int, default=int(os.environ.get("RS_CREDENTIAL_PORT", "8444")))
|
|
parser.add_argument("--bind", default=os.environ.get("RS_CREDENTIAL_BIND", "0.0.0.0"))
|
|
args = parser.parse_args()
|
|
|
|
server = ThreadingHTTPServer((args.bind, args.port), CredentialHTTPHandler)
|
|
print(f"[credential-server] listening on {args.bind}:{args.port}", flush=True)
|
|
print(f"[credential-server] OpenAPI spec at http://{args.bind}:{args.port}/openapi.json", flush=True)
|
|
try:
|
|
server.serve_forever()
|
|
except KeyboardInterrupt:
|
|
print("\n[credential-server] shutting down", flush=True)
|
|
server.server_close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|