Skip to content

Commit b9828fe

Browse files
BiomeOS Developercursoragent
andcommitted
feat: adopt TransportEndpoint standard (Wave 101, LOCAL pattern)
Local wire-compatible TransportEndpoint type in transport/endpoint.rs (uds, tcp, mesh_relay variants with #[serde(tag = "transport")]). TRANSPORT_ENDPOINT env acceptance wired in main.rs (Phase 1: parse + log). 14 new tests, 1,614 total. Zero cross-primal deps — wire format is the contract. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent bc5cf8e commit b9828fe

10 files changed

Lines changed: 344 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
## [0.9.16] - 2026-04-08
1111

12+
### Added (June 8, 2026 — Wave 101: Transport Endpoint Adoption)
13+
14+
- **`TransportEndpoint`**: Local wire-compatible implementation of sourDough's `TransportEndpoint` standard (`#[serde(tag = "transport")]``uds`, `tcp`, `mesh_relay` variants). No cross-primal dependency — the wire format is the contract.
15+
- **`TRANSPORT_ENDPOINT` env acceptance**: `main.rs` parses and logs launcher-injected transport on startup. Phase 1 (accept + log); Phase 2 (outbound routing) follows when `ipc.resolve` is available.
16+
- **`parse_transport_endpoint()`**: JSON parsing helper + `TRANSPORT_ENDPOINT_ENV` constant.
17+
- 14 new tests (roundtrip, wire-compat with sourDough format, `is_local`, Display, env parsing). 1,614 tests total, 199 source files.
18+
1219
### Added (June 5, 2026 — Wave 78 Parity: Capability Registry)
1320

1421
- **`config/capability_registry.toml`**: Machine-readable biomeOS-compatible capability overlay. 19 domains, 47 operations with `depends_on`/`cost`/`stability` metadata, 6 consumed capabilities. Follows sweetGrass/biomeOS/petalTongue convention.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
[![License](https://img.shields.io/badge/license-AGPL--3.0--or--later-blue)]()
88
[![Version](https://img.shields.io/badge/version-0.9.16-blue)]()
9-
[![Tests](https://img.shields.io/badge/tests-1%2C600%20passing-brightgreen)]()
9+
[![Tests](https://img.shields.io/badge/tests-1%2C614%20passing-brightgreen)]()
1010
[![Coverage](https://img.shields.io/badge/coverage-90.9%25%20line-brightgreen)]()
1111
[![Methods](https://img.shields.io/badge/JSON--RPC-47%20methods-blue)]()
1212
[![JH-0](https://img.shields.io/badge/JH--0-method%20gate-green)]()

STATUS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Implementation Status
44

55
**Current Version**: 0.9.16
6-
**Last Updated**: June 4, 2026
6+
**Last Updated**: June 8, 2026
77

88
---
99

WHATS_NEXT.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
# Development Roadmap
44

55
**Current Version**: 0.9.16
6-
**Last Updated**: June 5, 2026
6+
**Last Updated**: June 8, 2026
77

88
---
99

1010
## Documentation changelog
1111

12+
- **June 8, 2026****Wave 101: Transport Endpoint Adoption**: Local `TransportEndpoint` type (wire-compatible with sourDough canonical standard) in `transport/endpoint.rs`. `TRANSPORT_ENDPOINT` env acceptance wired in `main.rs`. 14 new tests. loamSpine transport status: DONE (LOCAL pattern, no cross-primal deps). 1,614 tests, 199 source files.
13+
1214
- **June 5, 2026****Wave 78 Parity: Capability Registry**: `config/capability_registry.toml` created — 19 domains, 47 operations, 6 consumed capabilities. Machine-readable biomeOS overlay following sweetGrass/biomeOS/petalTongue convention. Wave 78 parity item RESOLVED.
1315

1416
- **June 4, 2026****Trust Ledger IPC — bearDog Cross-Gate Trust Wiring**: 3 new JSON-RPC methods (`trust.anchor`, `trust.query`, `trust.event_count`) with dedicated trust spine. bearDog can anchor trusted issuer registrations, key exchanges, and cross-gate token verifications as permanent ledger entries. `"trust"` domain added to niche. NeuralAPI/MCP tools wired. Last 2 `#[allow(``#[expect(`. 1,600 tests, 198 source files, 47 methods.

bin/loamspine-service/main.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,19 @@ async fn run_server(
155155
)
156156
.init();
157157

158+
// Transport injection: accept TRANSPORT_ENDPOINT from launcher/Tower Atomic.
159+
// When set, the launcher decides the transport — the primal does not self-bind.
160+
if let Ok(te_json) = std::env::var(loam_spine_core::transport::TRANSPORT_ENDPOINT_ENV) {
161+
match loam_spine_core::transport::parse_transport_endpoint(&te_json) {
162+
Ok(endpoint) => {
163+
info!("Transport endpoint injected: {endpoint}");
164+
}
165+
Err(e) => {
166+
tracing::warn!("Invalid TRANSPORT_ENDPOINT value: {e}");
167+
}
168+
}
169+
}
170+
158171
// TCP is opt-in (ecosystem convention): only start TCP servers
159172
// when explicitly requested via CLI flags or environment variables.
160173
// UDS socket is always started as the primary transport.
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
// SPDX-License-Identifier: AGPL-3.0-or-later
2+
3+
//! Transport endpoint abstraction for launcher-injected transport.
4+
//!
5+
//! Wire-compatible with `sourdough_core::TransportEndpoint` (same serde
6+
//! tagged JSON format). Primals accept a `TRANSPORT_ENDPOINT` env var and
7+
//! let the launcher or Songbird decide the transport — not the primal.
8+
//!
9+
//! # Wire Format
10+
//!
11+
//! ```json
12+
//! { "transport": "uds", "path": "/run/membrane/loamspine.sock" }
13+
//! { "transport": "tcp", "host": "127.0.0.1", "port": 8080 }
14+
//! { "transport": "mesh_relay", "peer_id": "strand-gate", "capability": "ledger" }
15+
//! ```
16+
17+
use serde::{Deserialize, Serialize};
18+
use std::fmt;
19+
20+
/// Structured transport endpoint — describes how to reach a service.
21+
///
22+
/// Wire-compatible with `sourdough_core::TransportEndpoint` and
23+
/// `songbird_types::TransportEndpoint`. No cross-primal dependency —
24+
/// the wire format is the contract.
25+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
26+
#[serde(tag = "transport")]
27+
pub enum TransportEndpoint {
28+
/// Unix Domain Socket — local primal on same host.
29+
#[serde(rename = "uds")]
30+
Uds {
31+
/// Filesystem path to the socket.
32+
path: String,
33+
},
34+
35+
/// TCP — direct network connection.
36+
#[serde(rename = "tcp")]
37+
Tcp {
38+
/// Host address (IPv4, IPv6, or hostname).
39+
host: String,
40+
/// TCP port number.
41+
port: u16,
42+
},
43+
44+
/// Mesh relay — reachable via Songbird's mesh network.
45+
#[serde(rename = "mesh_relay")]
46+
MeshRelay {
47+
/// Mesh peer identifier (e.g. `"strand-gate"`).
48+
peer_id: String,
49+
/// Capability being resolved on the remote peer.
50+
capability: String,
51+
},
52+
}
53+
54+
impl TransportEndpoint {
55+
/// Construct a UDS endpoint.
56+
#[must_use]
57+
pub fn uds(path: impl Into<String>) -> Self {
58+
Self::Uds { path: path.into() }
59+
}
60+
61+
/// Construct a TCP endpoint.
62+
#[must_use]
63+
pub fn tcp(host: impl Into<String>, port: u16) -> Self {
64+
Self::Tcp {
65+
host: host.into(),
66+
port,
67+
}
68+
}
69+
70+
/// Construct a mesh relay endpoint.
71+
#[must_use]
72+
pub fn mesh_relay(peer_id: impl Into<String>, capability: impl Into<String>) -> Self {
73+
Self::MeshRelay {
74+
peer_id: peer_id.into(),
75+
capability: capability.into(),
76+
}
77+
}
78+
79+
/// Whether this endpoint is local (same host, no network hop).
80+
#[must_use]
81+
pub fn is_local(&self) -> bool {
82+
match self {
83+
Self::Uds { .. } => true,
84+
Self::Tcp { host, .. } => host == "127.0.0.1" || host == "::1" || host == "localhost",
85+
Self::MeshRelay { .. } => false,
86+
}
87+
}
88+
89+
/// Transport name as it appears in the wire format.
90+
#[must_use]
91+
pub const fn transport_name(&self) -> &'static str {
92+
match self {
93+
Self::Uds { .. } => "uds",
94+
Self::Tcp { .. } => "tcp",
95+
Self::MeshRelay { .. } => "mesh_relay",
96+
}
97+
}
98+
}
99+
100+
impl fmt::Display for TransportEndpoint {
101+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102+
match self {
103+
Self::Uds { path } => write!(f, "unix://{path}"),
104+
Self::Tcp { host, port } => {
105+
if host.contains(':') {
106+
write!(f, "tcp://[{host}]:{port}")
107+
} else {
108+
write!(f, "tcp://{host}:{port}")
109+
}
110+
}
111+
Self::MeshRelay {
112+
peer_id,
113+
capability,
114+
} => write!(f, "mesh://{peer_id}/{capability}"),
115+
}
116+
}
117+
}
118+
119+
/// Parse a `TRANSPORT_ENDPOINT` env var value into a [`TransportEndpoint`].
120+
///
121+
/// The value must be a JSON string matching the tagged serde format.
122+
///
123+
/// # Errors
124+
///
125+
/// Returns an error if the value is not valid JSON or doesn't match the
126+
/// expected wire format.
127+
pub fn parse_transport_endpoint(json: &str) -> Result<TransportEndpoint, serde_json::Error> {
128+
serde_json::from_str(json)
129+
}
130+
131+
/// The env var name for transport endpoint injection.
132+
pub const TRANSPORT_ENDPOINT_ENV: &str = "TRANSPORT_ENDPOINT";
133+
134+
#[cfg(test)]
135+
#[expect(clippy::unwrap_used, reason = "tests use unwrap for conciseness")]
136+
mod tests {
137+
use super::*;
138+
139+
#[test]
140+
fn uds_roundtrip() {
141+
let ep = TransportEndpoint::uds("/run/membrane/loamspine.sock");
142+
let json = serde_json::to_string(&ep).unwrap();
143+
assert!(json.contains(r#""transport":"uds"#));
144+
assert!(json.contains("loamspine.sock"));
145+
let parsed: TransportEndpoint = serde_json::from_str(&json).unwrap();
146+
assert_eq!(ep, parsed);
147+
}
148+
149+
#[test]
150+
fn tcp_roundtrip() {
151+
let ep = TransportEndpoint::tcp("192.168.1.132", 8080);
152+
let json = serde_json::to_string(&ep).unwrap();
153+
assert!(json.contains(r#""transport":"tcp"#));
154+
let parsed: TransportEndpoint = serde_json::from_str(&json).unwrap();
155+
assert_eq!(ep, parsed);
156+
}
157+
158+
#[test]
159+
fn mesh_relay_roundtrip() {
160+
let ep = TransportEndpoint::mesh_relay("strand-gate", "ledger");
161+
let json = serde_json::to_string(&ep).unwrap();
162+
assert!(json.contains(r#""transport":"mesh_relay"#));
163+
let parsed: TransportEndpoint = serde_json::from_str(&json).unwrap();
164+
assert_eq!(ep, parsed);
165+
}
166+
167+
#[test]
168+
fn wire_compat_sourdough_uds() {
169+
let json = r#"{"transport":"uds","path":"/run/user/1000/biomeos/beardog.sock"}"#;
170+
let ep: TransportEndpoint = serde_json::from_str(json).unwrap();
171+
assert_eq!(
172+
ep,
173+
TransportEndpoint::uds("/run/user/1000/biomeos/beardog.sock")
174+
);
175+
}
176+
177+
#[test]
178+
fn wire_compat_sourdough_tcp() {
179+
let json = r#"{"transport":"tcp","host":"127.0.0.1","port":9100}"#;
180+
let ep: TransportEndpoint = serde_json::from_str(json).unwrap();
181+
assert_eq!(ep, TransportEndpoint::tcp("127.0.0.1", 9100));
182+
}
183+
184+
#[test]
185+
fn wire_compat_sourdough_mesh() {
186+
let json = r#"{"transport":"mesh_relay","peer_id":"strand-gate","capability":"security"}"#;
187+
let ep: TransportEndpoint = serde_json::from_str(json).unwrap();
188+
assert_eq!(
189+
ep,
190+
TransportEndpoint::mesh_relay("strand-gate", "security")
191+
);
192+
}
193+
194+
#[test]
195+
fn is_local_uds() {
196+
assert!(TransportEndpoint::uds("/tmp/test.sock").is_local());
197+
}
198+
199+
#[test]
200+
fn is_local_tcp_localhost() {
201+
assert!(TransportEndpoint::tcp("127.0.0.1", 8080).is_local());
202+
assert!(TransportEndpoint::tcp("::1", 8080).is_local());
203+
assert!(TransportEndpoint::tcp("localhost", 8080).is_local());
204+
}
205+
206+
#[test]
207+
fn is_not_local_tcp_remote() {
208+
assert!(!TransportEndpoint::tcp("192.168.1.100", 8080).is_local());
209+
}
210+
211+
#[test]
212+
fn is_not_local_mesh_relay() {
213+
assert!(!TransportEndpoint::mesh_relay("peer", "cap").is_local());
214+
}
215+
216+
#[test]
217+
fn display_format() {
218+
assert_eq!(
219+
TransportEndpoint::uds("/tmp/test.sock").to_string(),
220+
"unix:///tmp/test.sock"
221+
);
222+
assert_eq!(
223+
TransportEndpoint::tcp("127.0.0.1", 9100).to_string(),
224+
"tcp://127.0.0.1:9100"
225+
);
226+
assert_eq!(
227+
TransportEndpoint::tcp("::1", 9100).to_string(),
228+
"tcp://[::1]:9100"
229+
);
230+
assert_eq!(
231+
TransportEndpoint::mesh_relay("gate", "ledger").to_string(),
232+
"mesh://gate/ledger"
233+
);
234+
}
235+
236+
#[test]
237+
fn parse_env_var_value() {
238+
let json = r#"{"transport":"uds","path":"/run/membrane/loamspine.sock"}"#;
239+
let ep = parse_transport_endpoint(json).unwrap();
240+
assert_eq!(
241+
ep,
242+
TransportEndpoint::uds("/run/membrane/loamspine.sock")
243+
);
244+
}
245+
246+
#[test]
247+
fn parse_invalid_json_fails() {
248+
assert!(parse_transport_endpoint("not json").is_err());
249+
}
250+
251+
#[test]
252+
fn transport_name_values() {
253+
assert_eq!(TransportEndpoint::uds("/tmp/x").transport_name(), "uds");
254+
assert_eq!(TransportEndpoint::tcp("h", 1).transport_name(), "tcp");
255+
assert_eq!(
256+
TransportEndpoint::mesh_relay("p", "c").transport_name(),
257+
"mesh_relay"
258+
);
259+
}
260+
}

crates/loam-spine-core/src/transport/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
//! # }
2626
//! ```
2727
28+
pub mod endpoint;
29+
30+
pub use endpoint::{TransportEndpoint, parse_transport_endpoint, TRANSPORT_ENDPOINT_ENV};
31+
2832
use std::future::Future;
2933
use std::pin::Pin;
3034

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# loamSpine FRAGO — Wave 101: Transport Endpoint Adoption
2+
3+
**Date**: 2026-06-08
4+
**From**: loamSpine (strandGate)
5+
**Re**: Wave 101 — Transport Evolution Scorecard
6+
7+
---
8+
9+
## ACK: Transport Endpoint Adopted — LOCAL Pattern
10+
11+
loamSpine has adopted the sourDough `TransportEndpoint` standard using the **correct LOCAL pattern** (no cross-primal dependency):
12+
13+
### What shipped
14+
15+
1. **`crates/loam-spine-core/src/transport/endpoint.rs`** (~130 lines + 14 tests)
16+
- `TransportEndpoint` enum: `Uds`, `Tcp`, `MeshRelay` variants
17+
- `#[serde(tag = "transport")]` — wire-compatible with sourDough, songBird, sweetGrass, coralReef, nestGate, squirrel
18+
- Constructors, `is_local()`, `transport_name()`, `Display`
19+
- `parse_transport_endpoint()` + `TRANSPORT_ENDPOINT_ENV` constant
20+
21+
2. **`bin/loamspine-service/main.rs`**`TRANSPORT_ENDPOINT` env acceptance
22+
- Parses and logs injected transport on startup
23+
- Phase 1: accept + log. Phase 2 (outbound `connect_transport`) when `ipc.resolve` available
24+
25+
3. **14 new tests** — roundtrip (uds/tcp/mesh), wire-compat with sourDough JSON, `is_local`, Display, env parsing, invalid JSON rejection
26+
27+
### Metrics
28+
29+
- **Tests**: 1,614 (was 1,600)
30+
- **Source files**: 199 (was 198)
31+
- **Transport status**: DONE (LOCAL pattern)
32+
- **Self-binding audit**: CLEAN (all binding is launcher-orchestrated via CLI flags or env vars)
33+
34+
### Transport Scorecard (loamSpine)
35+
36+
| Check | Status |
37+
|-------|--------|
38+
| `TransportEndpoint` type | DONE — `transport::endpoint` module |
39+
| `TRANSPORT_ENDPOINT` env | DONE — parsed at startup |
40+
| `connect_transport()` | Phase 2 — when `ipc.resolve` available |
41+
| Self-binding violations | ZERO — no hardcoded `bind("0.0.0.0:PORT")` |
42+
| Cross-primal dep | ZERO — wire format is the contract |
43+
44+
### Ecosystem context
45+
46+
loamSpine joins sweetGrass, nestGate, coralReef, squirrel as correct LOCAL pattern adopters (no `sourdough-core` path dep). barracuda and rhizoCrypt (strandGate siblings) need to fix their `sourdough-core` import.
47+
48+
---
49+
50+
*FRAGO COMPLETE — Wave 101 transport adoption shipped.*

infra/wateringHole/handoffs/LOAMSPINE_FRAGO_WAVE100_JUN08_2026.md renamed to infra/wateringHole/handoffs/archive/LOAMSPINE_FRAGO_WAVE100_JUN08_2026.md

File renamed without changes.

0 commit comments

Comments
 (0)