feat(railiance): adopt state-hub edge relay beachhead for WP-0015
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Build and Publish Container Image / build-and-push (push) Successful in 1m12s

Deploy actcore-statehub-edge-relay, point STATE_HUB_URL at it, retire the
bespoke state-hub bridge, and accept edge-relay queued write receipts in
report and ops evidence sinks.
This commit is contained in:
tegwick 2026-07-09 01:04:24 +02:00
parent 1a0636845a
commit cdbe4de2bf
13 changed files with 256 additions and 136 deletions

View file

@ -10,7 +10,7 @@ data:
TEMPORAL_HOST: actcore-temporal:7233
TEMPORAL_NAMESPACE: default
NATS_URL: nats://actcore-nats:4222
STATE_HUB_URL: http://state-hub.state-hub.svc.cluster.local:8000
STATE_HUB_URL: http://actcore-statehub-edge-relay:8000
LLM_CONNECT_URL: http://llm-connect.activity-core.svc.cluster.local:8080
LLM_CONNECT_TIMEOUT_SECONDS: "300"
REPO_SCOPING_URL: http://repo-scoping.repo-scoping.svc.cluster.local:8020
@ -572,28 +572,29 @@ data:
kind: coordination-service
lifecycle_state: observed
health_status: observed_ok
environment: local
environment: railiance01
owner_repos:
- state-hub
- the-custodian
runtime:
type: local-process
host: local-workstation
type: k3s
cluster: railiance01-k3s
namespace: state-hub
endpoints:
- id: state-hub-local-api
- id: state-hub-edge-relay-health
type: http
url: "http://actcore-state-hub-bridge:8000/state/health"
url: "http://actcore-statehub-edge-relay:8000/edge/health"
expected_status: 200
expected_signal: "health response"
expected_signal: "edge relay health"
backing_stores:
- "postgresql:state-hub"
access_paths:
- type: http
target: "http://actcore-state-hub-bridge:8000"
target: "http://actcore-statehub-edge-relay:8000"
status: observed_ok
evidence: []
gaps:
- "Future cluster deployment readiness still needs ops evidence."
- "Overnight triage proof after relay deploy still needs operator evidence."
- id: inter-hub
name: "Inter-Hub"
kind: governance-service
@ -774,16 +775,31 @@ spec:
storage: 1Gi
---
apiVersion: v1
kind: Service
kind: PersistentVolumeClaim
metadata:
name: actcore-state-hub-bridge
name: actcore-statehub-edge-outbox
namespace: activity-core
labels:
app.kubernetes.io/name: actcore-state-hub-bridge
app.kubernetes.io/name: actcore-statehub-edge-relay
app.kubernetes.io/part-of: activity-core
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Service
metadata:
name: actcore-statehub-edge-relay
namespace: activity-core
labels:
app.kubernetes.io/name: actcore-statehub-edge-relay
app.kubernetes.io/part-of: activity-core
spec:
selector:
app.kubernetes.io/name: actcore-state-hub-bridge
app.kubernetes.io/name: actcore-statehub-edge-relay
ports:
- name: http
port: 8000
@ -792,97 +808,75 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
name: actcore-state-hub-bridge
name: actcore-statehub-edge-relay
namespace: activity-core
labels:
app.kubernetes.io/name: actcore-state-hub-bridge
app.kubernetes.io/name: actcore-statehub-edge-relay
app.kubernetes.io/part-of: activity-core
spec:
replicas: 0
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: actcore-state-hub-bridge
app.kubernetes.io/name: actcore-statehub-edge-relay
template:
metadata:
labels:
app.kubernetes.io/name: actcore-state-hub-bridge
app.kubernetes.io/name: actcore-statehub-edge-relay
app.kubernetes.io/part-of: activity-core
spec:
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
containers:
- name: proxy
image: activity-core:railiance01-prod
imagePullPolicy: Never
- name: relay
# Published by Forgejo CI on state-hub main (edge read-cache, 1cf949b).
image: forgejo.coulomb.social/coulomb/state-hub:main-1cf949b
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 18080
containerPort: 8000
env:
- name: STATEHUB_UPSTREAM_URL
value: http://state-hub.state-hub.svc.cluster.local:8000
- name: STATEHUB_OUTBOX_PATH
value: /var/statehub/edge-outbox.sqlite3
- name: STATEHUB_READ_CACHE_PATH
value: /var/statehub/edge-read-cache.sqlite3
command:
- python
- -c
- |
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
TARGET = "http://127.0.0.1:18000"
HOP_HEADERS = {"connection", "host", "keep-alive", "proxy-authenticate",
"proxy-authorization", "te", "trailers",
"transfer-encoding", "upgrade"}
class Proxy(BaseHTTPRequestHandler):
def do_GET(self):
self._proxy()
def do_POST(self):
self._proxy()
def do_PATCH(self):
self._proxy()
def _proxy(self):
length = int(self.headers.get("content-length", "0") or "0")
body = self.rfile.read(length) if length else None
headers = {
key: value
for key, value in self.headers.items()
if key.lower() not in HOP_HEADERS
}
request = Request(
TARGET + self.path,
data=body,
headers=headers,
method=self.command,
)
try:
timeout = 360 if self.command == "POST" else 30
with urlopen(request, timeout=timeout) as response:
payload = response.read()
self.send_response(response.status)
for key, value in response.headers.items():
if key.lower() not in HOP_HEADERS:
self.send_header(key, value)
self.end_headers()
self.wfile.write(payload)
except HTTPError as exc:
payload = exc.read()
self.send_response(exc.code)
self.end_headers()
self.wfile.write(payload)
except URLError as exc:
self.send_response(502)
self.end_headers()
self.wfile.write(str(exc).encode())
ThreadingHTTPServer(("0.0.0.0", 18080), Proxy).serve_forever()
- uvicorn
- api.edge.relay:app
- --host
- 0.0.0.0
- --port
- "8000"
volumeMounts:
- name: edge-outbox
mountPath: /var/statehub
readinessProbe:
httpGet:
path: /state/health
path: /edge/health
port: http
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
livenessProbe:
httpGet:
path: /edge/health
port: http
initialDelaySeconds: 15
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
volumes:
- name: edge-outbox
persistentVolumeClaim:
claimName: actcore-statehub-edge-outbox
---
apiVersion: v1
kind: Service
metadata:

View file

@ -43,8 +43,11 @@ and a persistent working-memory volume mounted at
Before trusting the daily 07:20
Europe/Berlin schedule, verify both runtime dependencies:
- `actcore-state-hub-bridge` can reach the State Hub API through the node-local
tunnel expected at `127.0.0.1:18000`.
- `actcore-statehub-edge-relay` is ready and reports upstream reachability at
`GET /edge/health` (upstream is the in-cluster State Hub API at
`state-hub.state-hub.svc.cluster.local:8000`). `STATE_HUB_URL` points at the
relay so allowlisted `GET` reads can be served from cache during brief upstream
outages and queueable writes survive until replay.
- `LLM_CONNECT_URL` points at the verified in-namespace llm-connect Service,
`http://llm-connect.activity-core.svc.cluster.local:8080`, and the
operator-owned provider Secret lets that Service serve the