From c68b5a4fc824ec7209d89dad34877519f433c6fe Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:02:01 -0400 Subject: [PATCH] feat(inference+mobile): sovereign inference on our mesh + installable mobile PWA (twin or box) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two asks, one capstone: run LLMs on OUR infrastructure (not a cloud provider), and see/reach it from a phone. Sovereign inference (tools/inference.py): - a model is an immutable DATA SPHERE (provenance, pinned integrity, residency-fenced); loading weights needs a read Grant; a mutated model is un-citable. - inference_service_workload: serving a model is a GPU workload the compute plane places on a TRUSTED backend (Needs firewall keeps a sensitive model off untrusted/volunteer/vendor). - route_inference: FAIL-CLOSED sovereign-first. A sensitive prompt (or a residency-fenced model) goes to a sovereign endpoint or BLOCKS — it never leaves for a cloud LLM. Non-sensitive may fall back to a vendor connector only when policy allows. This is the difference between "our infrastructure" and "a cloud provider like Claude." Mobile PWA (portal_server.py): - installable (manifest.webmanifest + service worker, offline-ish shell), mobile-responsive, apple web-app tags; a twin/box endpoint badge (SOURCEOS_ENDPOINT). - /api/inference surfaces the sovereign posture + per-model routing; a "Sovereign inference" console section shows sensitive prompts routing sovereign or blocked. Access model: default to the TWIN (always-on rendezvous — the box sleeps, the twin doesn't; grants + coordinator live there), opt-in DIRECT to the box for LAN/offline. Same reference-vs-direct lattice from the mount analysis, applied to control access. capd/sovereign-inference.mesh.capd.json. Tests: +7 inference +4 portal = 156 tools tests green. --- Makefile | 3 + capd/sovereign-inference.mesh.capd.json | 30 ++++++++++ tools/inference.py | 77 +++++++++++++++++++++++++ tools/portal_server.py | 70 ++++++++++++++++++++-- tools/test_inference.py | 67 +++++++++++++++++++++ tools/test_portal_server.py | 23 ++++++++ tools/validate.py | 3 + 7 files changed, 269 insertions(+), 4 deletions(-) create mode 100644 capd/sovereign-inference.mesh.capd.json create mode 100644 tools/inference.py create mode 100644 tools/test_inference.py diff --git a/Makefile b/Makefile index 58f586e..c996675 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,9 @@ lease: ## pull/lease scheduler demo: workers pull WUs, crash-stop re-lending, or sphere: ## data-sphere demo: immutable dm-verity sphere, construction-tenancy, intent x link x durability cd tools && python3 data_sphere.py +inference: ## sovereign inference demo: models as data spheres, fail-closed sovereign routing + cd tools && python3 inference.py + availability: ## report the estate's availability-maturity grades (the Zero-Downtime legend) cd tools && python3 availability.py diff --git a/capd/sovereign-inference.mesh.capd.json b/capd/sovereign-inference.mesh.capd.json new file mode 100644 index 0000000..32d82c0 --- /dev/null +++ b/capd/sovereign-inference.mesh.capd.json @@ -0,0 +1,30 @@ +{ + "capability_id": "caps.inference.sovereign@0.1.0", + "kind": "inference.sovereign", + "status": "experimental", + "name": "Sovereign inference — run LLMs on our own mesh, not a cloud provider", + "description": "The whole point of a sovereign PaaS: a sensitive prompt must never leave for a vendor LLM (OpenAI/Anthropic/Gemini). The mesh serves its own models — weights are immutable data spheres (provenance-tracked, residency ring-fenced, read-Grant-gated), served on a TRUSTED GPU backend the Needs firewall keeps sensitive work off untrusted/volunteer/vendor nodes. Inference routing is fail-closed: sensitive inference goes to a sovereign endpoint or it BLOCKS; it never silently falls back to a cloud connector. Where it runs — the always-on cloud twin or the box (direct/LAN) — is a placement decision; both are sovereign, twin by default.", + "links": { + "engine": "tools/inference.py", + "models": "tools/data_sphere.py", + "placement": "tools/compute_plane.py", + "grant_authority": "tools/mcp_a2a_grant.py", + "portal": "tools/portal_server.py", + "reference_pattern": "self-hosted vLLM/llama.cpp/Ollama/TGI on our mesh vs cloud LLM APIs — sovereign, governed, sensitive-data-safe; models as immutable data spheres" + }, + "composes_with": { + "data_spheres": "caps.data.spheres@0.1.0", + "compute_plane": "caps.compute.mesh-plane@0.1.0", + "control_plane": "caps.infra.paas.continuum-local@0.1.0", + "scales_up_to": "caps.infra.cluster-scaleup.hyperswarm@0.1.0" + }, + "policy": { + "availability": "needs-work", + "sovereign_first": true, + "sensitive_never_vendor": true, + "models_as_data_spheres": true, + "fail_closed": true, + "gpu_trusted_only": true, + "evidence_emitting": true + } +} diff --git a/tools/inference.py b/tools/inference.py new file mode 100644 index 0000000..2ec9bf8 --- /dev/null +++ b/tools/inference.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +"""Sovereign inference — run LLMs on OUR mesh, not a cloud provider. + +The whole point of a sovereign PaaS: a sensitive prompt must NEVER leave for a vendor LLM +(OpenAI/Anthropic/Gemini/…). The mesh serves its own models — weights are immutable DATA SPHERES +(provenance-tracked, residency ring-fenced), served on a TRUSTED GPU backend behind a Grant — and +inference routing is fail-closed: sensitive inference goes to a sovereign endpoint or it BLOCKS; it +never silently falls back to a cloud connector. That is the difference between "our infrastructure" +and "a cloud provider like Claude." + +Where inference runs — the durable **twin** (always-on cloud K3s) or the **box** (direct/LAN when it +is up) — is a placement decision the compute plane already makes; both are sovereign, and the twin +is the default rendezvous because the box sleeps and the twin does not. +""" +from __future__ import annotations + +import data_sphere as ds + +ENGINES = ("vllm", "llama.cpp", "ollama", "tgi") + + +def model_sphere(*, name: str, version: str, weights_digest: str, params_b: float, + engine: str = "vllm", residency: str = "cluster") -> dict: + """A model is a data sphere: immutable weights, pinned integrity, provenance, residency-fenced. + Loading the weights therefore needs a read Grant, and a mutated model is un-citable.""" + s = ds.mint_sphere(name=f"model/{name}", version=version, + content={"weights": weights_digest, "params_b": params_b, "engine": engine}, + residency=residency, direction="ingress", + provenance={"kind": "model-weights", "params_b": params_b, "engine": engine}) + s["model_name"] = name + s["params_b"] = params_b + s["engine"] = engine + return s + + +def inference_service_workload(model: dict, *, replicas: int = 1, sensitivity: str = "sensitive") -> dict: + """Serving a model = a GPU workload the compute plane places on a TRUSTED backend (the Needs + firewall keeps a sensitive model off untrusted/volunteer/vendor backends). Dispatch it with the + executor like any other workload; reading the weights needs a read Grant on the model sphere.""" + return {"name": "infer-" + model["model_name"].replace("/", "-"), + "kind": "inference-service", "engine": model.get("engine", "vllm"), + "model_sphere": model["sphere_id"], "needs_gpu": True, "scalable": True, + "replicas": replicas, "effect": "compute", "sensitivity": sensitivity, + "needs": {"residency": model.get("residency", "cluster")}} + + +def route_inference(*, model: dict, sovereign_endpoints: list, prompt_sensitivity: str = "sensitive", + allow_vendor: bool = False) -> dict: + """Fail-closed sovereign-first routing. Returns {route, endpoint, reason}. A sensitive prompt (or + a residency-fenced model) is sent to a sovereign endpoint or BLOCKED — never a cloud LLM.""" + if sovereign_endpoints: + return {"route": "sovereign", "endpoint": sovereign_endpoints[0], + "reason": "served on our own mesh — the prompt never leaves"} + sovereign_required = (prompt_sensitivity == "sensitive" + or model.get("residency") in ("local", "cluster", "eu")) + if sovereign_required: + return {"route": "blocked", "endpoint": None, + "reason": "no sovereign endpoint up; REFUSING to send sensitive inference to a cloud LLM"} + if allow_vendor: + return {"route": "vendor", "endpoint": "connector", + "reason": "non-sensitive, no sovereign endpoint: policy-allowed vendor fallback"} + return {"route": "blocked", "endpoint": None, + "reason": "no sovereign endpoint and vendor fallback not permitted"} + + +if __name__ == "__main__": + import json + m = model_sphere(name="llama-3-70b", version="q4", weights_digest="sha256:" + "ab" * 32, + params_b=70, engine="vllm", residency="any") + print(json.dumps({ + "model_sphere": m["sphere_id"], + "service": inference_service_workload(m)["name"], + "sensitive_no_endpoint": route_inference(model=m, sovereign_endpoints=[], prompt_sensitivity="sensitive")["route"], + "sensitive_with_endpoint": route_inference(model=m, sovereign_endpoints=["twin:vllm:8000"])["route"], + "normal_vendor_fallback": route_inference(model=m, sovereign_endpoints=[], + prompt_sensitivity="normal", allow_vendor=True)["route"], + }, indent=2)) diff --git a/tools/portal_server.py b/tools/portal_server.py index 6ee2a7c..b046656 100644 --- a/tools/portal_server.py +++ b/tools/portal_server.py @@ -114,8 +114,55 @@ def _commons() -> dict: "cite": r["cite"]} for r in recs]} +def _endpoint() -> str: + """Which surface this portal is — the always-on cloud 'twin' or the 'box' (direct/LAN). Set + SOURCEOS_ENDPOINT=twin on the twin; defaults to box.""" + import os + return os.environ.get("SOURCEOS_ENDPOINT", "box") + + +def _inference() -> dict: + """Sovereign-inference posture: our own models, and where a sensitive prompt would route (never a + cloud LLM). Sovereign endpoints = live trusted GPU backends.""" + inf = _sib("inference") + reg = _registry() + avail = reg.availability() + sovereign_up = [b for b in ("hpc-slurm", "k8s") if avail.get(b, 0) > 0] + models = [inf.model_sphere(name=n, version=v, weights_digest="sha256:" + "ab" * 32, + params_b=p, engine="vllm") + for (n, v, p) in [("llama-3-8b", "q4", 8), ("mixtral-8x7b", "q4", 47), ("nomic-embed", "f16", 0.1)]] + return {"endpoint": _endpoint(), + "posture": "sovereign-first — sensitive inference never leaves for a cloud LLM", + "sovereign_endpoints": sovereign_up, + "models": [{"model": m["model_name"], "params_b": m["params_b"], + "route": inf.route_inference(model=m, sovereign_endpoints=sovereign_up, + prompt_sensitivity="sensitive")["route"]} + for m in models]} + + +_MANIFEST = json.dumps({ + "name": "SourceOS Continuum", "short_name": "Continuum", "start_url": "/", "scope": "/", + "display": "standalone", "background_color": "#0b0d12", "theme_color": "#0b0d12", + "description": "See and reach your infrastructure — twin or box.", + "icons": [{"src": "data:image/svg+xml,", + "sizes": "any", "type": "image/svg+xml", "purpose": "any maskable"}]}) + +# cache-first service worker so the console still loads on a flaky mobile link (offline-ish shell). +_SW = ("const C='continuum-v1';" + "self.addEventListener('install',e=>{self.skipWaiting();e.waitUntil(caches.open(C).then(c=>c.add('/')))});" + "self.addEventListener('activate',e=>e.waitUntil(self.clients.claim()));" + "self.addEventListener('fetch',e=>{if(e.request.method!=='GET')return;" + "e.respondWith(fetch(e.request).then(r=>{const cp=r.clone();caches.open(C).then(c=>c.put(e.request,cp));return r})" + ".catch(()=>caches.match(e.request)))});") + + _CONSOLE = """
-