Skip to content

Commit 79199de

Browse files
committed
docs: add service integration guide and middleware setup documentation
https://claude.ai/code/session_01YWxGS8PyEvMTrrG1yBcN3r
1 parent 7627f6f commit 79199de

2 files changed

Lines changed: 344 additions & 0 deletions

File tree

docs/guides/MIDDLEWARE-SETUP.adoc

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
// SPDX-License-Identifier: PMPL-1.0-or-later
2+
= PanLL Middleware Setup Guide
3+
:toc: macro
4+
:icons: font
5+
:source-highlighter: rouge
6+
7+
toc::[]
8+
9+
== Overview
10+
11+
PanLL supports three optional middleware layers that extend its core Gossamer backend
12+
with additional runtime capabilities: Elixir/BEAM for fault-tolerant service supervision,
13+
Julia for numeric analysis and batch processing, and Idris2 for high-assurance interface
14+
verification. All three are optional — PanLL runs without them — but each unlocks specific
15+
capabilities for production or research use.
16+
17+
== Elixir/BEAM Middleware
18+
19+
=== Purpose
20+
21+
The Elixir/BEAM middleware layer provides OTP supervision trees for resilient panel
22+
connections and service lifecycle management. It wraps the external service bridges
23+
(VeriSimDB, BoJ, ECHIDNA, TypeLL, Hypatia) under a supervisor hierarchy, enabling
24+
automatic process restart, health reporting, and fault isolation without coupling those
25+
concerns to the Gossamer Rust core.
26+
27+
=== Location
28+
29+
----
30+
beam/panll_beam
31+
----
32+
33+
=== Setup
34+
35+
[source,bash]
36+
----
37+
cd beam/panll_beam
38+
mix deps.get && mix compile
39+
----
40+
41+
=== API Modes
42+
43+
The BEAM middleware exposes three API surfaces, each toggled via the `PANLL_BEAM_APIS`
44+
environment variable:
45+
46+
HTTP (Bandit/Plug)::
47+
Standard HTTP endpoints served by Bandit with Plug routing. Suitable for REST-style
48+
integration with the Gossamer backend or external tooling.
49+
50+
GraphQL (Absinthe)::
51+
A GraphQL schema over the panel supervision state and service health data, served
52+
via Absinthe and Absinthe.Plug. Useful for Observatory panel queries and dashboards.
53+
54+
gRPC::
55+
Protocol Buffers over gRPC (via the `grpc` and `protobuf` Mix dependencies).
56+
Preferred for low-latency, typed communication between the BEAM layer and
57+
latency-sensitive service bridges.
58+
59+
=== Configuration
60+
61+
Set the `PANLL_BEAM_APIS` environment variable to a comma-separated list of the API
62+
modes to enable before starting the application:
63+
64+
[source,bash]
65+
----
66+
# Enable all three API surfaces
67+
export PANLL_BEAM_APIS=http,graphql,grpc
68+
mix run --no-halt
69+
70+
# Enable HTTP only
71+
export PANLL_BEAM_APIS=http
72+
mix run --no-halt
73+
----
74+
75+
=== When to Use
76+
77+
Use the Elixir/BEAM middleware in production deployments where fault-tolerant service
78+
supervision is required. The OTP supervisor model gives you per-service restart strategies,
79+
cascading failure isolation, and live introspection without modifying the Gossamer core.
80+
It is not needed for local development where all services are started manually.
81+
82+
== Julia Middleware
83+
84+
=== Purpose
85+
86+
The Julia middleware layer handles numeric analysis, calibration, and batch processing
87+
tasks that are impractical in the Rust/ReScript stack. This includes metrics computation
88+
(e.g., proof complexity statistics, type-check latency distributions), statistical
89+
hypothesis testing over telemetry data, and data pipeline processing for research
90+
workflows built on PanLL's proof-carrying data.
91+
92+
=== Location
93+
94+
Julia scripts live alongside the analysis pipelines they serve. Batch scripts are
95+
colocated with the data or reporting stage that consumes them. There is no single
96+
top-level Julia package directory — each analysis pipeline carries its own
97+
`Project.toml` and `Manifest.toml`.
98+
99+
=== Setup
100+
101+
From the directory containing the relevant `Project.toml`:
102+
103+
[source,bash]
104+
----
105+
julia --project=. -e 'using Pkg; Pkg.instantiate()'
106+
----
107+
108+
This resolves and precompiles all declared dependencies for that pipeline's environment.
109+
110+
=== When to Use
111+
112+
Use Julia middleware when:
113+
114+
* Computing aggregate metrics or statistical summaries over proof session telemetry.
115+
* Running statistical tests to validate performance regressions or calibration changes.
116+
* Processing data pipelines that feed into research publications or compliance reports
117+
derived from PanLL's observability output.
118+
* Performing calibration passes over numeric parameters used by ECHIDNA or TypeLL.
119+
120+
Julia is not involved in the real-time request path. All Julia processing is offline
121+
or batch-scheduled.
122+
123+
== Idris2 ABI Middleware
124+
125+
=== Purpose
126+
127+
The Idris2 ABI middleware layer provides high-assurance interface verification for
128+
critical IPC boundaries. Where the circuit breaker pattern and graceful degradation
129+
principles give _operational_ resilience, the Idris2 layer gives _formal_ guarantees:
130+
machine-checked proofs that serialization and deserialization of IPC messages correctly
131+
implement the agreed contract at every trust boundary.
132+
133+
=== Status
134+
135+
[IMPORTANT]
136+
====
137+
This layer is *planned* and has not yet been implemented. The design below describes
138+
the intended architecture.
139+
====
140+
141+
=== Goal
142+
143+
Formally verify the IPC message serialization and deserialization contracts used by the
144+
Gossamer command layer. Each `invoke` command crossing the webview/backend boundary has
145+
a defined schema; the Idris2 ABI layer will generate or check proofs that:
146+
147+
* Every serialized message can be round-tripped without loss of information.
148+
* Every deserializer rejects out-of-contract inputs with a typed error rather than
149+
silently producing a wrong value.
150+
* Schema evolution (adding fields, deprecating variants) preserves backward compatibility
151+
at the ABI level.
152+
153+
=== When to Use
154+
155+
Use the Idris2 ABI layer at critical trust boundaries where "probably correct" is not
156+
a sufficient assurance level. Typical candidates include:
157+
158+
* The serialization boundary between the ReScript frontend and the Gossamer Rust backend
159+
for commands that carry proof certificates or VCL query results.
160+
* Any message type used to authorize a destructive operation (e.g., workspace deletion,
161+
cartridge unload).
162+
* Integration points with external theorem provers where a malformed message could
163+
corrupt proof state.
164+
165+
Until the Idris2 layer is implemented, these boundaries rely on property-based tests
166+
and the existing contract suite in `contracts/`.
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
// SPDX-License-Identifier: PMPL-1.0-or-later
2+
// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath)
3+
= PanLL Service Integration Guide
4+
:author: Jonathan D.A. Jewell
5+
:toc: macro
6+
:icons: font
7+
:source-highlighter: rouge
8+
9+
toc::[]
10+
11+
== Overview
12+
13+
PanLL connects to multiple backend services to deliver its neurosymbolic development
14+
environment. These services handle proof storage, cartridge dispatch, type checking,
15+
theorem proving, and static analysis. Each service runs as an independent process,
16+
typically on localhost, and is accessed by the Gossamer IPC command layer in
17+
`src-gossamer/`.
18+
19+
Because any service may be unavailable — due to a crash, a slow startup, a network
20+
partition, or deliberate teardown during development — every service bridge must
21+
implement a defined failure mode rather than blocking the UI or propagating an
22+
unhandled error to the user.
23+
24+
== Service Inventory
25+
26+
[cols="1,1,2,2", options="header"]
27+
|===
28+
| Service | Port | Purpose | Failure Mode
29+
30+
| VeriSimDB
31+
| 8080
32+
| 8-modality versioned database for proof-carrying data; stores octads, VCL queries,
33+
and proof certificates.
34+
| Fall back to `localStorage` cache. Serve last-known-good octad snapshots until the
35+
service reconnects.
36+
37+
| BoJ
38+
| 7700
39+
| Cartridge server and protocol gateway; loads and invokes `.boj` cartridge modules,
40+
exposes health and topology endpoints.
41+
| Queue outgoing commands in memory. Replay the queue on reconnect. Surface a
42+
degraded-mode banner in the Observatory panel.
43+
44+
| ECHIDNA
45+
| 9000
46+
| Theorem prover dispatch; selects tactics, manages the prover pool, and returns
47+
proof results and tactic recommendations.
48+
| Degrade to cached proofs stored in `localStorage`. Mark obligations as
49+
"proof pending — ECHIDNA offline" rather than failing the session.
50+
51+
| TypeLL
52+
| 7800
53+
| Type verification kernel; checks expressions against PanLL's dependent type
54+
system, evaluates type-level computations.
55+
| Skip active type checks and warn the user with a visible indicator. Re-queue
56+
pending checks when the service comes back online.
57+
58+
| Hypatia
59+
| (dynamic)
60+
| Static analysis; performs code quality analysis, linting, and pattern detection
61+
across the active workspace.
62+
| Degrade gracefully. Continue showing the last successfully computed analysis
63+
results, marked as stale, until a fresh run completes.
64+
|===
65+
66+
== Circuit Breaker Pattern
67+
68+
Each service bridge in `src-gossamer/` should implement the standard three-state
69+
circuit breaker pattern to prevent cascading failures and eliminate repeated
70+
wait-for-timeout overhead when a backend is down.
71+
72+
=== States
73+
74+
Closed (normal operation)::
75+
Requests pass through to the service. Failure count is tracked. If the failure
76+
rate exceeds a threshold within a rolling window, the breaker trips to *Open*.
77+
78+
Open (service assumed failed)::
79+
All calls to this service are rejected immediately without attempting a network
80+
round-trip. A deadline timer is started. The panel receives the degraded-mode
81+
fallback response without blocking. When the timer expires, the breaker moves
82+
to *Half-Open*.
83+
84+
Half-Open (probing for recovery)::
85+
A single probe request is sent to the service. If it succeeds, the breaker
86+
resets to *Closed* and normal operation resumes. If it fails, the breaker
87+
returns to *Open* and resets the timer.
88+
89+
=== Implementation Guidance
90+
91+
All live service modules (`boj_live.rs`, `verisim_live.rs`, `echidna_live.rs`, and
92+
any future service bridges) should wrap their `ServiceEndpoint` calls with a
93+
shared circuit breaker guard. The shared HTTP client in `src-gossamer/src/http_client.rs`
94+
provides the low-level timeout infrastructure; the circuit breaker layer sits above
95+
it and consults the breaker state before handing a request to `http_client::get_json`
96+
or `http_client::post_json`.
97+
98+
State for each breaker (failure count, state enum, next-probe timestamp) should be
99+
held in an `Arc<Mutex<BreakerState>>` registered in the Gossamer application context
100+
at startup, so that all panels share a consistent view of each service's health.
101+
102+
== Graceful Degradation Principles
103+
104+
The following principles apply to all service integrations in PanLL:
105+
106+
* *Never block the UI on a service call.* All service requests must be asynchronous.
107+
The Gossamer command layer runs these on a Tokio runtime; commands must return
108+
immediately with a pending/cached response if the service is unavailable.
109+
110+
* *Always show last-known-good state.* Panels must cache the most recent successful
111+
response for each service query and display it when the live call fails or is
112+
short-circuited by an open breaker. Stale results must be visually marked.
113+
114+
* *Surface service health in the Observatory panel.* The Observatory panel
115+
(`PanelObservatory`, clade `observatory`) is the designated home for cross-panel
116+
health and resource status. Each service bridge should emit health events that
117+
the Observatory can display, including the current breaker state.
118+
119+
* *Log failures to the BoJ latency log for observability.* Service call failures,
120+
breaker state transitions, and degraded-mode activations must be recorded via
121+
the observability subsystem so that developers can diagnose intermittent issues
122+
after the fact.
123+
124+
== Example: Gossamer Command Handler with Service Failure Handling
125+
126+
The following pseudocode (Rust-like syntax) illustrates how a Gossamer command
127+
handler should interact with a service bridge that has a circuit breaker.
128+
129+
[source,rust]
130+
----
131+
// In src-gossamer/src/verisim_live.rs (or a wrapper command module):
132+
133+
pub async fn cmd_get_octad_summary(params: Value) -> String {
134+
let id = params["id"].as_str().unwrap_or("");
135+
136+
// 1. Check circuit breaker state.
137+
let breaker = VERISIMDB_BREAKER.lock().unwrap();
138+
if breaker.is_open() {
139+
// Fast-reject: return cached result without touching the network.
140+
log_service_event("verisimdb", "circuit_open", "fast-reject");
141+
return cached_octad_summary(id)
142+
.map(|v| mark_stale(v))
143+
.unwrap_or_else(|| json!({"error": "VeriSimDB unavailable", "stale": true}).to_string());
144+
}
145+
drop(breaker);
146+
147+
// 2. Attempt the live call.
148+
match verisim_live_get_octad(id).await {
149+
Ok(summary) => {
150+
// Success: reset failure count, store in cache.
151+
VERISIMDB_BREAKER.lock().unwrap().record_success();
152+
store_cached_octad_summary(id, &summary);
153+
summary
154+
}
155+
Err(e) => {
156+
// Failure: record it; breaker may trip to Open.
157+
let tripped = VERISIMDB_BREAKER.lock().unwrap().record_failure();
158+
log_service_event("verisimdb", "call_failed", &e);
159+
if tripped {
160+
log_service_event("verisimdb", "circuit_tripped", "now open");
161+
}
162+
// Fall back to cache.
163+
cached_octad_summary(id)
164+
.map(|v| mark_stale(v))
165+
.unwrap_or_else(|| json!({"error": e, "stale": true}).to_string())
166+
}
167+
}
168+
}
169+
----
170+
171+
Key points:
172+
173+
* The breaker guard is checked *before* any I/O.
174+
* The fallback path never panics or returns an empty string — it always gives the
175+
panel something renderable.
176+
* Failures are logged before returning the fallback, ensuring the BoJ latency log
177+
and Observatory panel receive the signal.
178+
* The cache write happens on the success path, keeping last-known-good state fresh.

0 commit comments

Comments
 (0)