From cd6316dc7172e97cce2630e698aa947e6df15640 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 02:00:26 +0900 Subject: [PATCH 01/18] test(readiness): reproduce delayed and absent Caido handshake --- .github/workflows/ci.yml | 19 +++++ Cargo.toml | 17 +++++ tests/readiness_contract.rs | 138 ++++++++++++++++++++++++++++++++++++ 3 files changed, 174 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 Cargo.toml create mode 100644 tests/readiness_contract.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..077b37ad --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,19 @@ +name: CI + +on: + pull_request: + push: + branches: [develop] + +permissions: + contents: read + +jobs: + rust: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo test --all-targets + - run: cargo clippy --all-targets -- -D warnings + - run: cargo fmt --all -- --check diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..8871eaa1 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "quarantine-sandbox-readiness" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" +description = "Fail-closed loopback readiness evidence for quarantine sandboxes" + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +sha2 = "0.10" + +[dev-dependencies] + +[[bin]] +name = "qsr-readiness" +path = "src/main.rs" diff --git a/tests/readiness_contract.rs b/tests/readiness_contract.rs new file mode 100644 index 00000000..cac9b527 --- /dev/null +++ b/tests/readiness_contract.rs @@ -0,0 +1,138 @@ +use quarantine_sandbox_readiness::{ + probe_readiness, FailureKind, ProbeRequest, ReadinessOutcome, +}; +use std::io::{Read, Write}; +use std::net::{Shutdown, TcpListener}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +fn request(origin: String) -> ProbeRequest { + ProbeRequest { + runtime_image_digest: + "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".into(), + instance_id: "strix-caido-42".into(), + origin, + method: "POST".into(), + path: "/graphql".into(), + request_body: br#"{"query":"mutation { loginAsGuest { token } }"}"#.to_vec(), + expected_status: 200, + expected_body_substring: Some("\"token\":\"guest\"".into()), + timeout: Duration::from_secs(2), + poll_interval: Duration::from_millis(10), + } +} + +fn serve_after(delay: Duration, response: &'static [u8]) -> (String, thread::JoinHandle<()>) { + let reservation = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = reservation.local_addr().unwrap(); + drop(reservation); + let handle = thread::spawn(move || { + thread::sleep(delay); + let listener = TcpListener::bind(address).unwrap(); + let (mut stream, _) = listener.accept().unwrap(); + let mut request_bytes = Vec::new(); + stream + .set_read_timeout(Some(Duration::from_millis(250))) + .unwrap(); + let _ = stream.read_to_end(&mut request_bytes); + assert!(String::from_utf8_lossy(&request_bytes).starts_with("POST /graphql HTTP/1.1")); + stream.write_all(response).unwrap(); + stream.shutdown(Shutdown::Write).unwrap(); + let mut eof = [0_u8; 1]; + assert_eq!(stream.read(&mut eof).unwrap(), 0, "client must close its connection"); + }); + (format!("http://{address}"), handle) +} + +#[test] +fn delayed_loopback_handshake_emits_digest_bound_ready_receipt() { + let (origin, server) = serve_after( + Duration::from_millis(75), + b"HTTP/1.1 200 OK\r\nContent-Length: 17\r\nConnection: close\r\n\r\n{"token":"guest"}", + ); + let cancelled = AtomicBool::new(false); + let receipt = probe_readiness(&request(origin), || cancelled.load(Ordering::Relaxed)); + server.join().unwrap(); + + assert_eq!(receipt.schema_version, "qsr.readiness.v1"); + assert_eq!(receipt.outcome, ReadinessOutcome::Ready); + assert_eq!(receipt.runtime_image_digest, request("http://127.0.0.1:1".into()).runtime_image_digest); + assert_eq!(receipt.instance_id, "strix-caido-42"); + assert!(receipt.attempts > 1); + assert!(receipt.completed_monotonic_ms >= receipt.started_monotonic_ms); + assert_eq!(receipt.http_status, Some(200)); + assert_eq!(receipt.failure_kind, None); + assert_eq!(receipt.request_sha256.len(), 64); + assert_eq!(receipt.response_sha256.as_deref().map(str::len), Some(64)); +} + +#[test] +fn never_listening_proxy_fails_closed_as_sandbox_unavailable() { + let reservation = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = reservation.local_addr().unwrap(); + drop(reservation); + let cancelled = AtomicBool::new(false); + let mut req = request(format!("http://{address}")); + req.timeout = Duration::from_millis(80); + + let receipt = probe_readiness(&req, || cancelled.load(Ordering::Relaxed)); + + assert_eq!(receipt.outcome, ReadinessOutcome::Failed); + assert_eq!(receipt.failure_kind, Some(FailureKind::SandboxUnavailable)); + assert!(receipt.http_status.is_none()); +} + +#[test] +fn successful_http_without_login_marker_is_not_ready() { + let (origin, server) = serve_after( + Duration::ZERO, + b"HTTP/1.1 200 OK\r\nContent-Length: 14\r\nConnection: close\r\n\r\n{"token":null}", + ); + let receipt = probe_readiness(&request(origin), || false); + server.join().unwrap(); + + assert_eq!(receipt.outcome, ReadinessOutcome::Failed); + assert_eq!(receipt.failure_kind, Some(FailureKind::SandboxUnavailable)); + assert_eq!(receipt.http_status, Some(200)); +} + +#[test] +fn caller_cancellation_is_distinct_from_sandbox_failure() { + let cancelled = Arc::new(AtomicBool::new(false)); + let flag = Arc::clone(&cancelled); + let canceller = thread::spawn(move || { + thread::sleep(Duration::from_millis(25)); + flag.store(true, Ordering::Relaxed); + }); + let mut req = request("http://127.0.0.1:9".into()); + req.timeout = Duration::from_secs(1); + + let receipt = probe_readiness(&req, || cancelled.load(Ordering::Relaxed)); + canceller.join().unwrap(); + + assert_eq!(receipt.outcome, ReadinessOutcome::Failed); + assert_eq!(receipt.failure_kind, Some(FailureKind::UserCancelled)); +} + +#[test] +fn non_loopback_and_unpinned_runtime_identity_are_rejected() { + let mut external = request("http://192.0.2.1:48080".into()); + let receipt = probe_readiness(&external, || false); + assert_eq!(receipt.failure_kind, Some(FailureKind::InvalidRequest)); + + external.origin = "http://127.0.0.1:48080".into(); + external.runtime_image_digest = "latest".into(); + let receipt = probe_readiness(&external, || false); + assert_eq!(receipt.failure_kind, Some(FailureKind::InvalidRequest)); +} + +#[test] +fn receipt_schema_reserves_non_sandbox_terminal_causes() { + assert_eq!(serde_json::to_string(&FailureKind::ProviderTerminated).unwrap(), ""provider_terminated""); + assert_eq!( + serde_json::to_string(&FailureKind::ModelCommunicationFailed).unwrap(), + ""model_communication_failed"" + ); +} From c05d378cfc736e4257594d69bb06871893ef2d0f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 02:25:32 +0900 Subject: [PATCH 02/18] fix(application-service): require bounded HTTP protocol readiness Preserve TCP reachability semantics, but require a bounded loopback HTTP response before issuing an HTTP lease. Add positive and server-error protocol cases while retaining timeout cleanup. No caller origin or consumer/provider taxonomy is introduced. --- src/infrastructure/podman.rs | 30 ++++++-- .../application_service_http_readiness_red.rs | 69 ++++++++++++++++++- 2 files changed, 92 insertions(+), 7 deletions(-) diff --git a/src/infrastructure/podman.rs b/src/infrastructure/podman.rs index f606fe00..22d1d521 100644 --- a/src/infrastructure/podman.rs +++ b/src/infrastructure/podman.rs @@ -1,6 +1,7 @@ //! Rootless Podman infrastructure adapter for isolated application services. use std::{ + io::{Read, Write}, net::{Ipv4Addr, SocketAddrV4, TcpStream}, path::PathBuf, process::Output, @@ -14,12 +15,14 @@ use sha2::{Digest, Sha256}; use super::bounded_command::{BoundedCommandError, BoundedCommandRunner}; use crate::{ ApplicationServiceError, ApplicationServiceLease, ApplicationServiceRequest, CleanupReceipt, - IsolationPolicy, ServiceEndpoint, sandbox_execution::RuntimeLeaseMetadata, + IsolationPolicy, ServiceEndpoint, ServiceProtocol, sandbox_execution::RuntimeLeaseMetadata, }; const PODMAN_BACKEND_ID: &str = "rootless_podman"; const DEFAULT_COMMAND_TIMEOUT: Duration = Duration::from_secs(30); const DEFAULT_COMMAND_OUTPUT_LIMIT_BYTES: usize = 64 * 1024; +const HTTP_READINESS_REQUEST: &[u8] = + b"GET / HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n"; #[derive(Clone, Debug, Deserialize, PartialEq, Eq)] struct PodmanInfo { @@ -365,7 +368,7 @@ impl RootlessPodmanAdapter { } }; - if wait_for_readiness(host_port, policy).is_err() { + if wait_for_readiness(host_port, request.protocol, policy).is_err() { self.cleanup_started_container(&plan, policy.shutdown_grace_seconds)?; return Err(ApplicationServiceError::ReadinessTimeout); } @@ -851,6 +854,7 @@ fn parse_loopback_port(stdout: &[u8]) -> Option { fn wait_for_readiness( host_port: u16, + protocol: ServiceProtocol, policy: &IsolationPolicy, ) -> Result<(), ApplicationServiceError> { let deadline = Instant::now() + Duration::from_millis(policy.readiness_timeout_millis); @@ -862,10 +866,28 @@ fn wait_for_readiness( return Err(ApplicationServiceError::ReadinessTimeout); } let remaining = deadline.saturating_duration_since(now); - if TcpStream::connect_timeout(&address.into(), poll.min(remaining)).is_ok() { - return Ok(()); + if let Ok(mut stream) = TcpStream::connect_timeout(&address.into(), poll.min(remaining)) { + if protocol == ServiceProtocol::Tcp { + return Ok(()); + } + let probe_timeout = poll.min(deadline.saturating_duration_since(Instant::now())); + if http_response_is_ready(&mut stream, probe_timeout) { + return Ok(()); + } } let after_probe = Instant::now(); thread::sleep(poll.min(deadline.saturating_duration_since(after_probe))); } } + +fn http_response_is_ready(stream: &mut TcpStream, timeout: Duration) -> bool { + if timeout.is_zero() + || stream.set_read_timeout(Some(timeout)).is_err() + || stream.set_write_timeout(Some(timeout)).is_err() + || stream.write_all(HTTP_READINESS_REQUEST).is_err() + { + return false; + } + let mut status_prefix = [0_u8; 10]; + stream.read_exact(&mut status_prefix).is_ok() && status_prefix == *b"HTTP/1.1 2" +} diff --git a/tests/application_service_http_readiness_red.rs b/tests/application_service_http_readiness_red.rs index 7c229f7d..a53611ad 100644 --- a/tests/application_service_http_readiness_red.rs +++ b/tests/application_service_http_readiness_red.rs @@ -1,13 +1,15 @@ -//! RED for protocol-level HTTP readiness above the application-service P0 boundary. +//! Protocol-level HTTP readiness tests for the application-service boundary. #![cfg(target_os = "linux")] use std::{ fs, + io::Write, net::TcpListener, os::unix::fs::PermissionsExt, path::PathBuf, sync::atomic::{AtomicU64, Ordering}, + thread, time::{SystemTime, UNIX_EPOCH}, }; @@ -95,10 +97,17 @@ fn remove_fixture(program: PathBuf, log: PathBuf) { let _ = fs::remove_file(log); } +fn spawn_http_response(listener: TcpListener, response: &'static [u8]) -> thread::JoinHandle<()> { + thread::spawn(move || { + let (mut stream, _) = listener.accept().expect("readiness probe should connect"); + stream + .write_all(response) + .expect("HTTP response should be writable"); + }) +} + #[test] fn http_service_does_not_become_ready_from_tcp_acceptance_alone() { - // Keep a TCP listener open but intentionally provide no HTTP response. The - // current adapter treats connect(2) success as sufficient even for `Http`. let listener = TcpListener::bind(("127.0.0.1", 0)).expect("loopback listener should bind"); let ready_port = listener .local_addr() @@ -117,3 +126,57 @@ fn http_service_does_not_become_ready_from_tcp_acceptance_alone() { assert!(calls.contains("rm --force")); assert!(calls.contains("network rm --force")); } + +#[test] +fn http_service_becomes_ready_after_a_bounded_success_response() { + let listener = TcpListener::bind(("127.0.0.1", 0)).expect("loopback listener should bind"); + let ready_port = listener + .local_addr() + .expect("listener should expose its address") + .port(); + let responder = spawn_http_response( + listener, + b"HTTP/1.1 204 No Content\r\nContent-Length: 0\r\nConnection: close\r\n\r\n", + ); + let (program, log) = write_fake_podman(ready_port); + let adapter = RootlessPodmanAdapter::new(program.clone()); + + let lease = adapter + .launch_at(&request(), &policy(), 1_780_000_000) + .expect("valid HTTP response should establish protocol readiness"); + responder.join().expect("HTTP responder should finish"); + assert_eq!(lease.endpoint().protocol(), ServiceProtocol::Http); + assert_eq!(lease.endpoint().host(), "127.0.0.1"); + assert_eq!(lease.endpoint().port(), ready_port); + + adapter + .terminate_at(&lease, 1_780_000_001) + .expect("successful HTTP lease should remain cleanable"); + remove_fixture(program, log); +} + +#[test] +fn http_server_error_is_not_readiness() { + let listener = TcpListener::bind(("127.0.0.1", 0)).expect("loopback listener should bind"); + let ready_port = listener + .local_addr() + .expect("listener should expose its address") + .port(); + let responder = spawn_http_response( + listener, + b"HTTP/1.1 503 Service Unavailable\r\nContent-Length: 0\r\nConnection: close\r\n\r\n", + ); + let (program, log) = write_fake_podman(ready_port); + let adapter = RootlessPodmanAdapter::new(program.clone()); + + assert_eq!( + adapter.launch_at(&request(), &policy(), 1_780_000_000), + Err(ApplicationServiceError::ReadinessTimeout) + ); + responder.join().expect("HTTP responder should finish"); + let calls = fs::read_to_string(&log).expect("cleanup calls should be recorded"); + assert!(calls.contains("stop --time 2")); + assert!(calls.contains("rm --force")); + assert!(calls.contains("network rm --force")); + remove_fixture(program, log); +} From 59cc738f1428d78eaf7a7999e65cc247307b990d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 02:35:46 +0900 Subject: [PATCH 03/18] test(application-service): preserve TCP process-boundary fixtures --- tests/podman_application_service.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/podman_application_service.rs b/tests/podman_application_service.rs index 04647d5a..5900553d 100644 --- a/tests/podman_application_service.rs +++ b/tests/podman_application_service.rs @@ -44,7 +44,7 @@ fn request() -> ApplicationServiceRequest { request_id: "process_boundary_request".to_owned(), image_reference: digest_image(), container_port: 8_080, - protocol: ServiceProtocol::Http, + protocol: ServiceProtocol::Tcp, command: vec!["serve".to_owned()], resources: ResourceRequest { memory_bytes: 256 * 1024 * 1024, @@ -120,7 +120,7 @@ fn launch_requires_rootless_backend_and_returns_loopback_lease_then_cleans_up() assert_eq!(lease.request_id(), "process_boundary_request"); assert_eq!(lease.endpoint().host(), "127.0.0.1"); assert_eq!(lease.endpoint().port(), ready_port); - assert_eq!(lease.endpoint().protocol(), ServiceProtocol::Http); + assert_eq!(lease.endpoint().protocol(), ServiceProtocol::Tcp); assert_eq!(lease.image_reference(), digest_image()); assert_eq!(lease.backend_id(), "rootless_podman"); assert_eq!(lease.policy_id(), "process_boundary_policy_v1"); From 236d1a67eb90f6d7c10c4714dd2d1d55faee72c6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 02:44:40 +0900 Subject: [PATCH 04/18] fix(application-service): compose bounded HTTP readiness probe --- src/infrastructure/podman.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/infrastructure/podman.rs b/src/infrastructure/podman.rs index 22d1d521..19f17c5b 100644 --- a/src/infrastructure/podman.rs +++ b/src/infrastructure/podman.rs @@ -881,13 +881,11 @@ fn wait_for_readiness( } fn http_response_is_ready(stream: &mut TcpStream, timeout: Duration) -> bool { - if timeout.is_zero() - || stream.set_read_timeout(Some(timeout)).is_err() - || stream.set_write_timeout(Some(timeout)).is_err() - || stream.write_all(HTTP_READINESS_REQUEST).is_err() - { - return false; - } let mut status_prefix = [0_u8; 10]; - stream.read_exact(&mut status_prefix).is_ok() && status_prefix == *b"HTTP/1.1 2" + stream + .set_read_timeout(Some(timeout)) + .and_then(|()| stream.set_write_timeout(Some(timeout))) + .and_then(|()| stream.write_all(HTTP_READINESS_REQUEST)) + .and_then(|()| stream.read_exact(&mut status_prefix)) + .is_ok_and(|()| status_prefix == *b"HTTP/1.1 2") } From a07b3a96df1efb5d695ce56beb6834f754102c81 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 02:45:16 +0900 Subject: [PATCH 05/18] docs(application-service): define protocol-aware readiness --- docs/TRD.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/TRD.md b/docs/TRD.md index 7c186724..f0c7f62b 100644 --- a/docs/TRD.md +++ b/docs/TRD.md @@ -147,10 +147,10 @@ The immutable image reference is appended before application argv, so applicatio 5. Inspect process seccomp/capability/LSM evidence and fail closed unless every implemented P0 isolation control is positively verified. 6. Query the requested port mapping only after isolation verification succeeds. 7. Accept only a single IPv4 loopback `127.0.0.1:` mapping. -8. Poll bounded TCP readiness using operator timeout/poll policy. +8. Poll protocol-aware readiness within the operator-owned timeout/poll budget: `tcp` requires a successful loopback TCP connection; `http` sends a bounded HTTP/1.1 request to the runtime-derived loopback endpoint and requires a final 2xx status-class response before readiness is accepted. 9. Return a lease only after effective-isolation checks and readiness succeed. -P0 HTTP readiness deliberately uses TCP reachability because no consumer-supplied health path is accepted yet. A future typed HTTP health contract may refine this without accepting arbitrary URLs. +P0 HTTP readiness never accepts a caller-supplied URL, origin, host, or arbitrary health path. The probe is fixed to `/` on the runtime-owned loopback mapping, so consumer-specific authentication or bootstrap semantics remain outside this bounded context. A future typed application-specific health contract may version this boundary without turning arbitrary network destinations into runtime authority. ### Cleanup From f81e401a2f9468659dac0a88240884e5dab4387d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 02:45:40 +0900 Subject: [PATCH 06/18] docs(application-service): bind product readiness to protocol --- docs/PRD.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/PRD.md b/docs/PRD.md index 8ce7f507..5a86d13a 100644 --- a/docs/PRD.md +++ b/docs/PRD.md @@ -51,9 +51,9 @@ The runtime is deliberately not the authority for the consumer's business decisi - Enforce CPU, RAM, PID, tmpfs, lease, readiness, and shutdown bounds. - Create a per-sandbox internal DNS-disabled network and publish exactly one service to host IPv4 loopback on a random port. - Invoke the application without a shell. -- Return an endpoint only after bounded readiness. +- Return an endpoint only after protocol-aware bounded readiness: TCP services require loopback transport reachability; declared HTTP services require a successful bounded HTTP response from the runtime-derived loopback endpoint. - Return a versioned lease and cleanup receipt. -- Provide no request fields for credentials, arbitrary environment variables, broad host mounts, devices, privileged mode, host namespaces, runtime sockets, or arbitrary Internet egress. +- Provide no request fields for credentials, arbitrary environment variables, broad host mounts, devices, privileged mode, host namespaces, runtime sockets, arbitrary health URLs, or arbitrary Internet egress. ### Artifact-analysis foundation @@ -110,7 +110,7 @@ The runtime is deliberately not the authority for the consumer's business decisi - Existing artifact-analysis public Rust API remains available after DDD directory migration. - Tag-only images and over-budget resource requests fail closed. - Launch plan has no privileged/host-network/runtime-socket path and explicitly enforces P0 isolation flags. -- Process-boundary tests prove direct argv invocation, readiness gating, error cleanup, and explicit termination behavior. +- Process-boundary tests prove direct argv invocation, protocol-aware readiness gating, error cleanup, and explicit termination behavior; an HTTP declaration cannot become ready from TCP acceptance alone, and a non-2xx HTTP response is not readiness. - Real rootless Podman E2E proves the effective security boundary before the capability is called release-ready. - `contextual-orchestrator` integration occurs through its owner issue/ACL and a published runtime artifact; no direct consumer Podman calls. - Wardnet verdict policy remains outside this repository. From 5f2a982cab25dddced29e381e82b9e4108d20639 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 02:46:03 +0900 Subject: [PATCH 07/18] docs(adr): record protocol-aware readiness decision --- docs/adr/0006-isolated-application-service.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/adr/0006-isolated-application-service.md b/docs/adr/0006-isolated-application-service.md index 023e324c..70ab8345 100644 --- a/docs/adr/0006-isolated-application-service.md +++ b/docs/adr/0006-isolated-application-service.md @@ -9,6 +9,8 @@ This ADR remains Proposed while PR #1 is Draft and the exact candidate has not p Chat/Agent systems sometimes need a real application process rather than an in-process tool function. Running such applications on the orchestrator host with ambient credentials, host networking, writable filesystem, or container-engine access creates an unacceptable authority boundary. +A loopback port accepting TCP is not sufficient evidence that an application declared as HTTP is ready for a consumer security bootstrap. The runtime therefore needs protocol-aware readiness while keeping consumer-specific login, authorization, and arbitrary health URLs outside the isolation bounded context. + ## Decision The first `application_service` infrastructure adapter is rootless Podman. A valid request must use an immutable OCI image digest and bounded service/resources. The adapter: @@ -24,19 +26,23 @@ The first `application_service` infrastructure adapter is rootless Podman. A val - disables restart and container logging in the P0 profile; - applies CPU/RAM/PID and container lifetime limits; - publishes one service port to random host port on `127.0.0.1` only; -- performs a bounded readiness check before returning a lease; +- performs protocol-aware bounded readiness before returning a lease: `tcp` requires a successful loopback connection, while `http` sends a fixed HTTP/1.1 request to `/` on the runtime-derived loopback endpoint and requires a final 2xx status-class response; +- never accepts a caller-supplied readiness URL, origin, host, or arbitrary path in the P0 contract; - removes container and network on explicit termination and on partial-launch/readiness failures; - returns an attested versioned lease and cleanup receipt. -The application request has no fields for privileged mode, host namespaces, devices, arbitrary mounts, environment variables, runtime sockets, or external network enablement. +The application request has no fields for privileged mode, host namespaces, devices, arbitrary mounts, environment variables, runtime sockets, external network enablement, or caller-controlled health destinations. Consumer-specific authentication/bootstrap remains consumer-owned and occurs only after the runtime has returned an attested ready lease. ## Alternatives - **Consumer-owned Podman calls:** rejected because isolation policy would be duplicated and domain code would depend on infrastructure. +- **TCP reachability for HTTP readiness:** rejected because a listening socket can exist before the declared HTTP service is usable and can issue a lease that immediately fails consumer bootstrap. +- **Caller-supplied health URL/path:** rejected for P0 because it expands the runtime into an outbound-request authority and mixes consumer application semantics into the isolation contract. +- **Consumer login as runtime readiness:** rejected because login credentials and application authorization belong to the consumer, not the reusable isolation runtime. - **Docker socket sidecar:** rejected because the socket is a high-authority control channel. - **gVisor first:** deferred. gVisor is a planned stronger OCI backend but P0 first establishes the consumer-neutral contract and rootless lifecycle with broadly available Podman. - **Kubernetes first:** deferred because a cluster is not required for standalone/local operation. ## Verification rule -Command-plan and fake-Podman process tests are necessary but not sufficient. Release claims about container isolation require a real rootless Podman E2E lane that checks effective filesystem, namespace, capability, resource, network, and cleanup behavior. gVisor/containerd/Kubernetes adapters require their own parity and security evidence. +Command-plan and fake-Podman process tests are necessary but not sufficient. Protocol readiness tests must prove that TCP-only acceptance does not satisfy an HTTP declaration, that a bounded 2xx response does, and that non-2xx/stalled responses fail closed. Release claims about container isolation require a real rootless Podman E2E lane that checks effective filesystem, namespace, capability, resource, network, and cleanup behavior. gVisor/containerd/Kubernetes adapters require their own parity and security evidence. From 8a577918650c6d63d03fb445914b40b19affd74c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 02:46:42 +0900 Subject: [PATCH 08/18] docs(doctoring): record HTTP readiness causal evidence --- ...ION_SERVICE_HTTP_READINESS_TRACEABILITY.md | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/docs/doctoring/APPLICATION_SERVICE_HTTP_READINESS_TRACEABILITY.md b/docs/doctoring/APPLICATION_SERVICE_HTTP_READINESS_TRACEABILITY.md index 5588b61b..57a120c4 100644 --- a/docs/doctoring/APPLICATION_SERVICE_HTTP_READINESS_TRACEABILITY.md +++ b/docs/doctoring/APPLICATION_SERVICE_HTTP_READINESS_TRACEABILITY.md @@ -4,15 +4,15 @@ Proposed on 2026-09-09. Issue #103 / Draft PR #104 owns the first HTTP-readiness repair inside the existing `application_service` Supporting bounded context and `sandbox_execution` Core lifecycle. It does not create a separate readiness product, move Strix/Caido application authorization into this repository, or assign LLM/provider failure taxonomy to this runtime. -Canonical dependency root for this RED is Draft PR #1 exact `5c6a44bb2b35eb17d0315d72db242f4488c3c426`; protected/default `develop` is `60a85c7633e03b425b67159ec6822c8178cf87ea`. +Canonical dependency root for this lane is Draft PR #1 exact `5c6a44bb2b35eb17d0315d72db242f4488c3c426`; protected/default `develop` is `60a85c7633e03b425b67159ec6822c8178cf87ea`. -## Production evidence that motivates the RED +## Production evidence that motivated the RED The consumer failure recorded in issue #103 occurred before a Strix model-backed scan when the local Caido endpoint on `127.0.0.1:48080` never became usable. The centralized caller correctly failed closed as `STRIX_SANDBOX_UNAVAILABLE`; adding retries at the consumer would not prove sandbox readiness. -On the canonical root, `RootlessPodmanAdapter::launch_at` verifies isolation and the loopback mapping, then calls `wait_for_readiness`. That function treats a successful TCP connection as sufficient readiness for both `ServiceProtocol::Tcp` and `ServiceProtocol::Http`. The current TRD explicitly documents this P0 behavior and says a future typed HTTP health contract may refine it. +On the canonical root, `RootlessPodmanAdapter::launch_at` verified isolation and the loopback mapping, then called `wait_for_readiness`. Before this lane, that function treated a successful TCP connection as sufficient readiness for both `ServiceProtocol::Tcp` and `ServiceProtocol::Http`. -The buyer-visible gap is therefore narrower than the original standalone #104 prototype: an HTTP application can accept TCP while its HTTP/login surface is still unavailable, yet the runtime can issue a ready lease. +The buyer-visible gap was therefore narrower than the original standalone #104 prototype: an HTTP application could accept TCP while its HTTP surface was still unavailable, yet the runtime could issue a ready lease. ## Rejected predecessor design @@ -20,31 +20,46 @@ Exact predecessor #104 `cd6316dc7172e97cce2630e698aa947e6df15640` created a seco The predecessor also exposed caller-controlled arbitrary `origin` and reserved `provider_terminated` / `model_communication_failed` inside the runtime. Those authorities are rejected. The runtime owns runtime-generated loopback service readiness and cleanup. Provider/model communication classification remains with the LLM/orchestration owner. -## Current RED contract +## Causal RED and minimum behavior repair -`tests/application_service_http_readiness_red.rs` uses the existing application-service request, isolation policy, and Podman ACL. It supplies otherwise-positive backend/isolation evidence and a runtime-mapped loopback port with a listening TCP socket that deliberately produces no HTTP response. +Exact `65663052ec30bc178adbe5ff4514f5409d10971f` established the intended RED: a listening TCP socket that deliberately produced no HTTP response was nevertheless accepted as `ServiceProtocol::Http` readiness. The expected contract was `ReadinessTimeout` plus cleanup. -Required behavior: +Minimum production candidate `c05d378cfc736e4257594d69bb06871893ef2d0f` changed only readiness semantics: -- `ServiceProtocol::Http` must not become ready from TCP acceptance alone; -- readiness failure remains bounded by the operator readiness budget; -- a failed readiness attempt must stop/remove the runtime-owned container and remove its network; -- `ServiceProtocol::Tcp` semantics remain a separate capability and are not implicitly upgraded to HTTP. +- `tcp` remains successful loopback transport connection; +- `http` sends a fixed HTTP/1.1 request to `/` on the runtime-derived loopback mapping; +- a final 2xx status class is required before readiness succeeds; +- read/write I/O is bounded by the remaining operator readiness budget; +- no caller-controlled URL, origin, host, path, credential, login, or provider/model classification enters the runtime contract. -The RED intentionally does not yet prescribe Caido's `loginAsGuest`, an arbitrary caller URL, or provider/model failures. After this exact test executes for the intended cause, the minimum GREEN must introduce only the backend-neutral HTTP handshake capability needed to distinguish protocol readiness from TCP reachability. Any request/wire-shape change requires versioned schema compatibility and PRD/TRD/ADR-0006 synchronization before merge. +Focused regressions prove TCP acceptance alone is not HTTP readiness, HTTP 204 is accepted, and HTTP 503 is not readiness. + +## Broad-fixture and coverage RCA + +The first exact candidate CI `34257036516` exposed an inherited fixture mismatch: `tests/podman_application_service.rs` used `ServiceProtocol::Http` while its success fixtures were intentionally plain TCP listeners. Commit `59cc738f1428d78eaf7a7999e65cc247307b990d` preserved those process-boundary fixtures as TCP instead of weakening HTTP readiness. Exact CI `34258071256` then made verify GREEN, including the full workspace test suite, Clippy, and rustdoc; hosted negative rootless/AppArmor also passed. + +The same exact branch-coverage lane found one direct owned-production admission gap in the new helper: `src/infrastructure/podman.rs` line 889 and four short-circuit branches represented timeout/socket-configuration/write setup failures. Functions were `190/190`, while the helper left total lines `1989/1990`, canonical regions `2664/2665`, and branches `462/466`. + +Commit `236d1a67eb90f6d7c10c4714dd2d1d55faee72c6` is the minimum causal repair. It composes read-timeout, write-timeout, request-write, and response-read operations as one ordered `io::Result` chain and returns readiness only when the complete chain succeeds with an HTTP/1.1 2xx prefix. This is not a coverage-threshold exception: every setup/I/O error remains fail closed, but the implementation no longer duplicates one semantic failure outcome across four explicit short-circuit branches. Rust's standard `TcpStream` contract already rejects a zero `Duration` for both read and write timeouts, so removing the separate `timeout.is_zero()` predicate does not create an unbounded-I/O path. + +The ordinary coverage lane on `59cc738...` separately reproduced `BackendInvocationFailed { operation: "rootless_probe" }` in `root_coverage_edges`, before its intended `network_inspect` failure. That repeated process-invocation class is owned by Issue #71 / Draft #72. #104 must not hide it with retries, mutexes, environment workarounds, or weakened expectations. ## DDD and security decision `application_service` owns the consumer-neutral meaning of an HTTP-ready leased service. `sandbox_execution` owns bounded readiness/lifecycle and cleanup truth. `infrastructure` performs concrete socket/HTTP I/O against only the runtime-generated loopback endpoint. Podman, Docker/Colima-compatible OCI execution, gVisor/containerd, or Kubernetes adapters must not alter the readiness domain contract merely because the backend changes. -An arbitrary origin is not accepted. The probe target must be derived from the already-validated runtime-owned loopback endpoint. Application-specific authentication semantics remain typed Supporting-context configuration or consumer ACL data rather than Core UL. +An arbitrary origin is not accepted. The probe target is derived from the already-validated runtime-owned loopback endpoint. Application-specific authentication semantics remain typed Supporting-context configuration or consumer ACL data rather than Core UL. + +RFC 9110 defines 2xx as the successful response class. The P0 probe uses only that status class rather than embedding application-specific response bodies or login semantics. The current wire shape therefore remains unchanged. ## Evidence and release gates -This RED is not release authority. A future GREEN still requires exact-head repository validation, fmt/test/Clippy/rustdoc, 100% owned-production statement/function/source-region/branch coverage, hosted negative and real positive effective-LSM evidence where applicable, review/security gates, protected integration, SBOM/provenance/reproducibility/rollback, and immutable publication before any consumer version bump. +This lane is not release authority until its final exact head reacquires repository validation, fmt/test/Clippy/rustdoc, 100% owned-production statement/function/source-region/branch coverage, hosted negative and real positive effective-LSM evidence where applicable, review/security gates, protected integration, SBOM/provenance/reproducibility/rollback, and immutable publication before any consumer version bump. ## References -Fielding, R., Nottingham, M., & Reschke, J. (2022). *HTTP Semantics (RFC 9110).* RFC Editor. https://www.rfc-editor.org/rfc/rfc9110.html +Fielding, R., Nottingham, M., & Reschke, J. (2022). *HTTP semantics (RFC 9110).* RFC Editor. https://www.rfc-editor.org/rfc/rfc9110.html + +Rust Project Developers. (2026). *TcpStream in std::net*. Rust standard library documentation. https://doc.rust-lang.org/std/net/struct.TcpStream.html -Souppaya, M., Morello, J., & Scarfone, K. (2017). *Application Container Security Guide* (NIST Special Publication 800-190). National Institute of Standards and Technology. https://doi.org/10.6028/NIST.SP.800-190 +Souppaya, M., Morello, J., & Scarfone, K. (2017). *Application container security guide* (NIST Special Publication 800-190). National Institute of Standards and Technology. https://doi.org/10.6028/NIST.SP.800-190 From d098385045cefd8b337ba2bd0069107a01756b81 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 02:50:02 +0900 Subject: [PATCH 09/18] docs(gap): refresh live readiness and release authority --- docs/product-technical-gap-baseline.md | 63 +++++++++++++------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index bacb346f..1d87969d 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,6 +1,6 @@ # Product and Technical Gap Baseline -Last reviewed on 2026-09-08 KST against root Draft PR #1 exact `489af256d8e7cdac777f8ece5f50829f4238b35f`, focused coverage-admission Draft #94 integrated candidate `b981dbc4e28b41690fde4d233321f1dfe9eaf377`, protected/default `develop@60a85c7633e03b425b67159ec6822c8178cf87ea`, and the live open PR/Issue/security state. This ledger separates protected truth, active-PR implementation, checked-in RED, causal RED, candidate GREEN, real backend evidence, central required-workflow evidence, and release authority. Evidence from a predecessor SHA never transfers after implementation or dependency movement. +Last reviewed on 2026-09-09 KST against root Draft PR #1 exact `5c6a44bb2b35eb17d0315d72db242f4488c3c426`, application-readiness Draft #104 lineage through protocol/coverage repair `236d1a67eb90f6d7c10c4714dd2d1d55faee72c6`, protected/default `develop@60a85c7633e03b425b67159ec6822c8178cf87ea`, and the live open PR/Issue/security state. This ledger separates protected truth, active-PR implementation, checked-in RED, causal RED, candidate GREEN, real backend evidence, central required-workflow evidence, and release authority. Evidence from a predecessor SHA never transfers after implementation or dependency movement. ## Product responsibility and DDD authority @@ -11,7 +11,7 @@ Quarantine Sandbox Runtime owns reusable hostile-workload/application-service is - `application_service` is a Supporting context and owns consumer-neutral service intent plus translation to/from Core sandbox execution. - `infrastructure` owns concrete Podman/process/backend translation and observation. Podman/gVisor/containerd/Kubernetes/VM types do not become consumer domain contracts. - Wardnet retains verdict/incident/response authority. contextual-orchestrator retains LLM/Agent/tool authorization. Noema retains Agent/runtime capability authority. Keyverse retains identity authority. EgressWeave retains outbound-policy authority. -- Consumer integration is through a future immutable released contract/artifact. Sibling source imports, mutable PR-head dependencies, direct foreign runtime calls, and cross-service SQL are not integration mechanisms. +- Consumer integration is through an immutable released contract/artifact. Sibling source imports, mutable PR-head dependencies, direct foreign runtime calls, and cross-service SQL are not integration mechanisms. The runtime currently owns no durable database. Any future durable job/evidence/reaper store requires an explicit persistence ADR, 3NF schema, descriptive multiword `snake_case` objects, retention/recovery semantics, and migration/rollback evidence. @@ -19,16 +19,13 @@ The runtime currently owns no durable database. Any future durable job/evidence/ | Gap / gate | Current evidence | Status | Required action | | --- | --- | --- | --- | -| Checkout credential persistence | Predecessor root `7482108c0b74f58f447722a98330f9ad44215eec`, native CI `34089522598`, verify `101640016166` proved every checkout uses `persist-credentials: false`. | Causal GREEN preserved in root ancestry | Do not regress; predecessor GREEN does not replace current-root gates. | -| Root owned-production coverage and process-fixture stability | Root lineage includes focused reachable-edge tests plus production simplification `bd23862df508dfec8f24fe7dbc74b57d23491bac`, which removes only parser-dominated empty-token predicates while retaining reachable `unconfined`, AppArmor enforce-mode, inspect-label, and nonzero capability checks. Exact #94 child `4990976015c554df1cc485135dfa1d5e3a38e4ca`, carrying that root implementation, executed the full branch suite successfully: LLVM raw totals functions `189/189`, lines `1973/1973`, regions `2629/2630`, branches `452/452`; the canonical source-region checker reported `2630/2630`. All process-backed suites in that lane passed. | Runtime branch behavior and canonical source-region gate have hosted candidate GREEN on child ancestry; root itself moved only by formatting repair and still needs normal integration/current gates | Integrate the proven coverage-admission child normally into the root only after review/current required gates permit it, then re-prove the exact integrated root without weakening reachable security checks. | -| Root formatting defect exposed by exact child CI | Exact #94 head `499097...` verify `101905358746` passed exact checkout, dependency lock, repository policy, and all eight coverage-parser tests, then failed only because inherited `tests/root_coverage_reachable_edges.rs` was not rustfmt-clean. Root commit `489af256d8e7cdac777f8ece5f50829f4238b35f` applies the minimal rustfmt-only repair. #94 adopted it by ordinary two-parent non-force merge `b981dbc4e28b41690fde4d233321f1dfe9eaf377`. | Causal failure → canonical root repair → non-force descendant adoption | Preserve the root-owned fix; do not carry a child-only formatting workaround or force-rebase descendants. | -| LLVM source-region admission | Issue #93 / Draft #94 audited immutable root artifact `10034315670` and found all unique source coordinates executed despite raw `bounded_command.rs=440/441`. Review `5136297577` found helper-only RED coverage did not bind release admission, so test-only `31b7e7b40a374122c938089e374bd6303e7e9c4a` added `main()` admission REDs. Checker candidate `1454644358190356aa5a84080129d24a1bcac77e` gates production source-region union by filename + source start/end + region kind, fails on denominator inconsistency, and retains raw LLVM output. Exact hosted head `499097...` produced branch artifact `10037275727` (`sha256:41be203dce7d8588f2ab450cf63983a9f4053515afad35762e86bfcb466bc189`) with raw regions `2629/2630`, canonical source regions `2630/2630`, and branches `452/452`. After non-force root adoption, exact `b981dbc...` CI `34176200159` has verify `101905976342`, coverage `101905976423`, branch coverage `101905976383`, and hosted negative rootless/AppArmor `101905976218` all GREEN. | Causal RED → minimum checker → hosted exact candidate GREEN on dependency-safe child ancestry | Keep raw LLVM diagnostics and 100% canonical source-region threshold. Merge only through normal review/protection flow; positive-LSM/release authority remains independent. | -| Dedicated positive effective-LSM evidence | Exact `b981dbc...` positive-LSM job `101905976348` remains queued on `[self-hosted, linux, cwl-hostile-workload, selinux]`; hosted negative rootless/AppArmor is GREEN but is not positive confinement evidence. | Independent release/security lane blocked on dedicated runner | `.github#1590` remains canonical owner. Do not substitute hosted negative evidence or weaken the LSM gate. | -| Repeated backend spawn failure is under-classified | Earlier root verify `101852033066` failed a process-backed application-service test at `rootless_probe` before the intended `container_create` failure. Dependency-safe #92 coverage independently hit the same generic `rootless_probe` invocation class in another integration-test binary while #92 verify was GREEN. Current root still maps `BoundedCommandError::{Spawn, Wait, Capture}` to one `BackendInvocationFailed`. Issue #71 / Draft #72 already has an executed missing-executable RED and bounded `std::io::ErrorKind` classification candidate, but #72 remains on older ancestry. | Real repeated RCA evidence + canonical typed-evidence owner exists; current-root integration pending | After root coverage integration stabilizes, preserve #72's `Spawn(io::ErrorKind)` and bounded public classes by ordinary non-force integration. Do not add retry, extra mutexes, or isolation weakening before the concrete OS class is observable. | -| Repository workflow SHA-pin validation | Issue #86 / Draft #92 executed both the multi-workflow SHA-pin RED and the same-repository `$/...` false-positive RED. Minimum production `d906165bf081b3e4d8ca9d71e48bdb6888cab9e8` admits only `$/` self-repository targets before retaining the exact 40-character lowercase SHA rule for external dependencies. #92 is on predecessor root ancestry. | Two causal REDs + minimum candidate; dependency restack required after root stabilization | Non-force adopt the final current root while preserving #92's child-owned files, then reacquire exact-head repository-policy/full-gate evidence. Add no tag/branch exception. | -| Central CodeQL terminal verdict | Predecessor root CodeQL detected languages and dispatched current-head analysis but failed the central `Release runner or enforce current-head CodeQL verdict` stage. | Central required-workflow failure; current root also requires fresh result | `.github#1929` remains canonical owner. Do not reinterpret dispatch success as terminal CodeQL acceptance or patch leaf status. | -| Dependency Review availability | Predecessor root Security Scan verified exact checkout/head then failed `Check dependency review support`; actual Dependency Review was skipped while Trivy/OSV/Scorecard succeeded independently. | Central required-workflow failure; current root also requires fresh result | `.github#810` owns availability. Do not substitute other scanners for Dependency Review. | -| SAST | Predecessor root SAST Semgrep was GREEN; current root `489af...` has a new SAST execution and must be judged on its exact result. | Reacquire after source movement | Do not treat one security gate or predecessor result as full release authority. | +| Checkout credential persistence | Root ancestry retains the causal GREEN proving every checkout uses `persist-credentials: false`; current root exact is `5c6a44bb2b35eb17d0315d72db242f4488c3c426`. | Integrated into current Draft root | Do not regress; predecessor GREEN does not replace current-head gates. | +| Root owned-production coverage / canonical source-region admission | Issue #93 / PR #94 was normally integrated into root. Current root native CI `34176680115` has verify `101907363084`, coverage `101907363224`, branch coverage `101907363185`, and hosted negative rootless/AppArmor `101907363162` GREEN. The checker preserves raw LLVM diagnostics while gating canonical owned-production source coordinates. | Exact current-root hosted GREEN | Preserve 100% line/function/canonical-source-region/branch admission and raw LLVM traceability. Positive-LSM and central security gates remain independent. | +| Dedicated positive effective-LSM evidence | Current root positive-LSM job `101907363137` remains queued on `[self-hosted, linux, cwl-hostile-workload, selinux]`. Hosted negative rootless/AppArmor is GREEN but is not positive confinement evidence. | Independent release/security lane pending dedicated runner | `.github#1590` remains canonical owner. Do not substitute hosted negative evidence or weaken the LSM gate. | +| Repeated backend spawn/invocation failure | Earlier root/process lanes and #92 reproduced generic `BackendInvocationFailed { operation: "rootless_probe" }` before their intended backend failures. Issue #71 / Draft #72 causally executed missing-executable/provider-neutral REDs and now carries exact `900d0273625115f7bcc602d888baadded0caeb4f`; native CI `34231220456` has verify `102077568988`, coverage `102077568604`, branch `102077568933`, hosted negative `102077569052` GREEN, while positive-LSM `102077569145` remains queued and no qualifying approval exists. #104 ordinary coverage at `59cc738...` independently reproduced the same `rootless_probe` class in `root_coverage_edges`. | Canonical typed-error repair hosted GREEN; independent positive-LSM/review gates still open | Preserve #72 as owner. Do not add retry, mutex, environment workaround, or weakened expectations in consumer children. Integrate non-force only after its remaining gates. | +| Repository workflow SHA-pin validation | Issue #86 / Draft #92 owns external full-SHA versus same-repository `$/...` policy. Current exact `30c5de5c73d729a1695609325d867a456eaf2dda`; policy validator tests, coverage-parser tests, formatting, complete production coverage, branch coverage and hosted negative are GREEN. Broader verify fails only in inherited `rootless_probe` process/backend invocation before the intended cleanup failure. | Policy-specific candidate GREEN; inherited #71/#72 runtime prerequisite blocks full verify | Do not mutate the validator or use retry. Adopt the canonical runtime prerequisite non-force, then rerun exact full gates. | +| Central CodeQL terminal verdict | Current root CodeQL `34176680113` dispatched exact-head analysis but python `101907470730` and actions `101907470755` failed `Release runner or enforce current-head CodeQL verdict`. | Central required-workflow failure | `.github#1929` remains canonical owner. Dispatch is not a terminal CodeQL verdict. | +| Dependency Review availability | Current root Security Scan `34176680132` verified exact head; OSV, Scorecard and Trivy succeeded, while dependency-review `101907475098` failed `Check dependency review support` and actual Dependency Review was skipped. | Central required-workflow failure | `.github#810` owns availability. Do not substitute sibling scanners. | GitHub's immutable-action guidance requires full-length commit SHA pinning for external actions, while its `$/` self-repository syntax is already bound to the exact running workflow commit and must not carry an `@ref` suffix. Organization/reusable CI/review/security/release policy remains canonical in `ContextualWisdomLab/.github`; repository validation is defense in depth and must preserve that distinction without duplicating central workflow implementation. @@ -36,13 +33,14 @@ GitHub's immutable-action guidance requires full-length commit SHA pinning for e | Capability | Current evidence | Status | Required action | | --- | --- | --- | --- | -| Immutable application identity | Public request requires lower-case SHA-256 digest identity and launch uses `--pull=never`; mutable tags/alternate host-backed transports are rejected on root lineage. | Implemented contract; exact current runtime evidence still required on protected integration | Keep registry/import/admission outside launch and bind effective image identity before release. | -| Rootless / read-only / capability / namespace / seccomp / LSM boundary | Root lineage requests restrictive Podman controls and performs effective checks. Exact child coverage/hosted-negative lanes prove reachable fail-closed behavior including SELinux `unconfined` rejection after the parser-dominated simplification. Positive acceptance remains separate on the dedicated SELinux runner. | Hosted negative and unit/process evidence GREEN on dependency-safe child; positive confinement still pending | Never convert unavailable/unconfined evidence to Verified. Preserve all required controls and obtain real positive effective-LSM evidence. | -| Missing Podman security fields | Issue #73 / Draft #89 causally proved that missing `EffectiveCaps`, `BoundingCaps`, or `dns_enabled` was defaulted to apparently secure values. Minimum production `80e6fd19ab5ef11eab3d18b3e80b43f880ce6058` removes only those defaults; the child remains on predecessor root ancestry. | Causal RED + minimum candidate, not current-root-integrated GREEN | Restack non-force after root stabilizes; explicit empty/false values remain observed values, while field absence is malformed evidence. | -| Podman option/data boundary | Issue #90 / Draft #91 requires literal `--` immediately before consumer-controlled digest-pinned image while preserving command argv. | RED-only child | After causal RED, add only the option terminator before the image operand. Do not shell-join or sanitize argv data. | -| Public lease deserialization | Issue #84 / Draft #85 tests that public Serde construction can bypass runtime-owned lease/endpoint/attestation invariants and closed schema constraints. | RED-only child | After causal RED, remove public deserialization if evidence is serialization-only or reconstruct through strict wire DTO + validated domain constructors. | -| Public cleanup-receipt deserialization | Issue #87 / Draft #88 tests that cleanup evidence can be caller-constructed outside its invariant-establishing path. | RED-only child | Enforce strict wire admission or serialization-only evidence; deserialized evidence never becomes destructive authority. | -| Effective network binding | Draft #23 retains exact network-attachment, acquired-network-identity, foreign-safe cleanup, and negative-egress REDs. | P0 REDs staged; production truth incomplete | Execute on current root ancestry; require exact acquired network identity, exclusive deny-by-default attachment, non-force foreign-safe cleanup, and real negative-egress evidence. | +| Immutable application identity | Public request requires lower-case SHA-256 digest identity and launch uses `--pull=never`; mutable tags/alternate host-backed transports are rejected on root lineage. | Implemented contract; exact protected integration still required | Keep registry/import/admission outside launch and bind effective image identity before release. | +| Rootless / read-only / capability / namespace / seccomp / LSM boundary | Root lineage requests restrictive Podman controls and performs effective checks. Current root hosted lanes are GREEN; positive acceptance remains separate on the dedicated SELinux runner. | Hosted negative and unit/process evidence GREEN; positive confinement pending | Never convert unavailable/unconfined evidence to Verified. Preserve all required controls and obtain real positive effective-LSM evidence. | +| Protocol-aware HTTP readiness / security-consumer bootstrap | Consumer `contextual-orchestrator#1094` run `34238690594` / job `102107934422` failed before Strix model review because Caido never became usable at `127.0.0.1:48080`. Issue #103 / Draft #104 exact RED `65663052ec30bc178adbe5ff4514f5409d10971f` proved that TCP acceptance alone could issue an HTTP lease. Candidate `c05d378cfc736e4257594d69bb06871893ef2d0f` added a bounded loopback HTTP/1.1 `/` probe requiring 2xx while retaining TCP connect semantics. Exact `59cc738f1428d78eaf7a7999e65cc247307b990d` corrected inherited plain-TCP process fixtures and made verify + hosted negative GREEN; its branch coverage then isolated one direct owned helper gap (`1989/1990` lines, `2664/2665` canonical regions, `462/466` branches). Commit `236d1a67eb90f6d7c10c4714dd2d1d55faee72c6` composes bounded socket setup/write/read errors through one fail-closed `io::Result` chain without changing the wire contract or accepting caller URLs. | Causal RED → behavioral candidate GREEN in full verify → exact coverage repair awaiting fresh final-head evidence | Reacquire final-head verify + 100% coverage/branch + hosted negative; keep application login/auth consumer-owned. Positive-LSM, qualifying review and central release gates remain independent. Release immutable runtime before consumer version/digest bump. | +| Missing Podman security fields | Issue #73 / Draft #89 causally proved that missing `EffectiveCaps`, `BoundingCaps`, or `dns_enabled` was defaulted to apparently secure values. Minimum production `80e6fd19ab5ef11eab3d18b3e80b43f880ce6058` removes only those defaults; the child remains separate. | Causal RED + minimum candidate, not protected-root integrated | Restack/adopt non-force when prerequisites permit; explicit empty/false values remain observed values, while field absence is malformed evidence. | +| Podman option/data boundary | Issue #90 / Draft #91 exact `1b1026a92b7d9ef23a709564c1e22aa0421179ff` causally proved missing option termination; production delta is one literal `--` before `request.image_reference`. Exact verify, complete production coverage, branch coverage and hosted negative are GREEN; positive-LSM and qualifying approval remain open. | Hosted exact candidate GREEN; release gates open | Preserve exact argv data and do not shell-join/sanitize. Merge only after positive confinement/review/security gates. | +| Public lease deserialization | Issue #84 / Draft #85 exact `e0040d748a8076e17f5103a8a238d2fbd878efbf` preserves private closed wire DTO + validated reconstruction. Verify, complete coverage, branch coverage and hosted negative are GREEN; positive-LSM and qualifying approval remain open. | Hosted exact candidate GREEN; release gates open | Keep strict admission and validated domain construction; do not promote mutable head to consumer authority. | +| Public cleanup-receipt deserialization | Issue #87 / Draft #88 exact `08fed96e0193d80f380aed07f6bcf4b2c29ca397` has verify, complete coverage, branch coverage and hosted negative GREEN. Stale review findings based on obsolete root ancestry were answered and resolved; positive-LSM and qualifying approval remain open. | Hosted exact candidate GREEN; release gates open | Preserve serialization/admission boundary; deserialized evidence never becomes destructive authority. | +| Effective network binding | Draft #23 retains exact network-attachment, acquired-network-identity, foreign-safe cleanup, and negative-egress REDs. | P0 REDs staged; production truth incomplete | Require exact acquired network identity, exclusive deny-by-default attachment, non-force foreign-safe cleanup, and real negative-egress evidence. | | CPU/RAM/PID/tmpfs/wall time | Applied Podman configuration exists, but inspect/configuration is not equivalent to authoritative cgroup/mount/termination evidence. Draft #19 owns resource/namespace/image/argv applied-state REDs. | Backend-applied intent; live proof incomplete | Bind inspect state only after causal RED, then verify live cgroup-v2, tmpfs mount restrictions/size, and runtime-owned wall-time termination. | | Runtime/lifecycle ownership | Draft #21 retains independent invocation collision, exact acquired container ID, malformed create-ID, and destructive-authority REDs. | REDs staged | Preserve consumer `request_id` as correlation; use collision-resistant invocation identity and exact acquired backend IDs for lifecycle authority. | | Pre-attestation command execution | Command path can start hostile payload before positive effective isolation is established; issue #25 remains the hold/attest/release boundary owner. | Security gap; RED staged downstream | Require a trusted hold/attest/release primitive or equivalent. Cleanup after execution does not undo pre-attestation code execution. | @@ -53,11 +51,15 @@ GitHub's immutable-action guidance requires full-length commit SHA pinning for e | Capability | Current evidence | Status | Required action | | --- | --- | --- | --- | | Static foundation | Bounded ingestion, SHA-256 artifact identity, non-executing format detection, deterministic evidence ordering, analyzer interface, and failure attribution exist on active foundation #18. | Active foundation, unmerged | Preserve `artifact_analysis` ownership and exact-head verification. | -| Analyzer worker Core ownership | Issue #69 / Draft #70 executed a Core-boundary E0432 RED, then a seven-required-control semantic RED. Minimum production requires rootless/read-only/cap-drop/NNP/userns/seccomp/LSM `Verified`; the current parent candidate remains separate from root repair. | Causal RED + minimum Core candidate | Obtain exact candidate GREEN with applicable isolation evidence before parent integration. | +| Analyzer worker Core ownership | Issue #69 / Draft #70 executed a Core-boundary E0432 RED, then a seven-required-control semantic RED. Minimum production requires rootless/read-only/cap-drop/NNP/userns/seccomp/LSM `Verified`. | Causal RED + minimum Core candidate | Obtain exact candidate GREEN with applicable isolation evidence before parent integration. | +| Bounded source-context semantic presence | Issue #95 / Draft #96 executed the all-null schema/domain mismatch RED. Minimum schema candidate adds only semantic-presence `anyOf` branches while retaining optional top-level/per-field nullability. | Executed RED + minimum schema candidate | Reacquire exact candidate gates and preserve published/Rust acceptance-set parity. | +| Artifact ingestion name invariant | Issue #97 / Draft #98 owns policy/name admission parity with `ArtifactDescriptor`; formatter prerequisite was repaired without production change. | RED lane executing | Require configured maximum within canonical 255-byte descriptor bound and reject non-leaf names without basename coercion/truncation. | +| Analyzer producer namespace authority | Issue #99 / Draft #100 executed reserved `runtime_core` producer collision RED; minimum candidate introduces one private runtime-core producer identifier and rejects analyzer collision through the existing error class. | Causal RED + minimum candidate | Reacquire exact gates; keep evidence taxonomy ownership in `artifact_analysis`, not Core. | +| UTF-8 byte-bound publication | Issue #101 / Draft #102 owns Rust-byte versus JSON-Schema-character acceptance mismatch; formatter prerequisite repaired. | RED lane executing | Make byte semantics executable/fail-closed without relaxing Rust byte limits or pretending an unsupported extension keyword is a standard assertion. | | Analyzer evidence producer authority | Issue #77 / Draft #78 rejects untrusted worker claims to controller/runtime-owned evidence kinds while retaining analyzer-owned static evidence. | RED-only child of #70 | After causal RED, add the smallest Supporting-context producer-authority predicate; do not move taxonomy ownership into Core. | | Worker process exit vs completed outcome | Issue #79 / Draft #80 requires `Completed` only with exact worker `Exited { exit_code: 0 }`. | RED-only child of #70 | Add one cross-field ACL only after causal execution; semantic analyzer failure remains distinct from process failure. | | Worker cleanup identity | Issue #81 / Draft #82 requires cleanup evidence to identify the exact worker rather than expose an unscoped completion boolean. | RED-only child of #70 | Introduce backend-neutral Core cleanup evidence with exact worker identity after causal RED, then consume it from `artifact_analysis`. | -| Evidence-bundle unknown fields | Issue #75 / Draft #76 tests that Rust deserialization must match the published Draft 2020-12 `additionalProperties: false` contract. | RED-only | After causal RED, add strict unknown-field rejection to published evidence wire structures without changing schema version/shape. | +| Evidence-bundle unknown fields | Issue #75 / Draft #76 tests Rust deserialization against the published Draft 2020-12 `additionalProperties: false` contract. Current focused strict-deserialization controls pass, while broader workspace execution is blocked by the inherited #71/#72 backend invocation class. | Focused candidate GREEN; inherited prerequisite open | Adopt canonical runtime prerequisite non-force and rerun full exact gates; do not mutate evidence ACL for the unrelated backend failure. | | Dynamic analysis truthfulness | #49 containment and #50/#52/#54/#56/#58/#60/#62/#64/#66 evidence/resource/provenance/cardinality/identity families remain independent focused owners above #18. | Multiple staged REDs; no release-grade detonation worker | Do not execute hostile bytes in the Rust controller. Worker execution must consume Core isolation and preserve attributable provenance/completeness. | | YARA-X / capa / Ghidra / LIEF adapters | No release-grade production adapters yet. | Missing | Add one bounded evidence producer at a time with immutable tool/version/digest/config provenance and hostile fixtures. | @@ -73,7 +75,7 @@ Configured intent, backend-applied inspection, and live effective enforcement ar - Wall-time claims require behavioral termination plus cleanup evidence. - Request/receipt equality proves contradiction resistance, not confinement. - Static analysis may not assert observed runtime behavior. -- Fake Podman/process tests may establish parser/ACL/cleanup behavior but are never release-grade positive isolation evidence. +- Fake Podman/process tests may establish parser/ACL/cleanup/readiness behavior but are never release-grade positive isolation evidence. - Canonical source-region coverage and compiler-instantiation coverage are distinct measurement targets; raw LLVM summaries remain traceable even when canonical source-region admission reconciles duplicate instances. ## Protected integration and release authority @@ -95,12 +97,11 @@ The first release remains blocked until one exact integrated protected head has, ## Next bounded slices -1. Preserve Draft #94's current dependency-safe ancestry and obtain the remaining review/current exact-head gates; its hosted verify, coverage, branch coverage, and hosted-negative lanes are GREEN at `b981dbc...`, while positive-LSM remains an independent queued release lane. -2. Normally integrate #94 into root #83 only when protection/review allows it, then execute the exact integrated root and require canonical source regions and branches to remain 100% while reachable Podman security checks remain fail closed. -3. Once root coverage/process fixtures are stable, non-force integrate #71/#72's typed spawn-failure evidence onto current root ancestry and classify any real `rootless_probe` OS failure before considering retry policy. -4. Non-force restack issue #86 / Draft #92 onto the stabilized root, preserving its executed `$/` self-reference RED and minimum external-SHA/self-reference repair. Re-run repository policy and full gates on the restacked exact head. -5. Keep CodeQL terminal-verdict failures on `.github#1929`, Dependency Review availability on `.github#810`, and positive-LSM capacity/evidence on `.github#1590`; no leaf bypass or substitute gate. -6. Execute #70's current minimum Core worker candidate; only after exact GREEN may #78/#80/#82 proceed independently with their own causal RED→minimum GREEN loops. -7. Restack root children #89/#85/#88/#91 independently after root stabilization; preserve every child delta by ordinary non-force integration and do not fold unrelated repairs together. -8. After root exact GREEN, reconcile the ordered command/release path `#1 -> #6 -> #9 -> #10 -> #13 -> #14 -> #18` dependency-first without force, then execute resource/network/runtime-identity/pre-attestation RED owners on current ancestry. -9. Publish no release until a normal merge produces an exact protected `develop` head and that exact integrated SHA reacquires every release gate above; only then publish immutable version/artifacts and hand released version/digest pinning to consumer owner paths. +1. Finish Issue #103 / Draft #104 on its final exact head: require verify, 100% owned production coverage/branch/source-region admission, hosted negative confinement, review, and dedicated positive-LSM evidence. Keep Caido login/consumer bootstrap outside the runtime and publish an immutable runtime artifact before contextual-orchestrator changes its consumed version/digest. +2. Preserve #71/#72 as the canonical repeated `rootless_probe` process-invocation RCA; integrate its typed error evidence only through ordinary non-force ancestry after remaining positive-LSM/review gates. +3. Re-run children blocked by that prerequisite (#92 and focused artifact-evidence lanes) after canonical adoption rather than adding leaf retries/workarounds. +4. Keep CodeQL terminal-verdict failures on `.github#1929`, Dependency Review availability on `.github#810`, and positive-LSM capacity/evidence on `.github#1590`; no leaf bypass or substitute gate. +5. Execute #70's current minimum Core worker candidate; only after exact GREEN may #78/#80/#82 proceed independently with their own causal RED→minimum GREEN loops. +6. Preserve #85/#88/#91 complete deltas and exact hosted evidence; do not merge while qualifying review/positive confinement remains open. +7. Reconcile the ordered command/release path `#1 -> #6 -> #9 -> #10 -> #13 -> #14 -> #18` dependency-first without force, then execute resource/network/runtime-identity/pre-attestation RED owners on current ancestry. +8. Publish no release until a normal merge produces an exact protected `develop` head and that exact integrated SHA reacquires every release gate above; only then publish immutable version/artifacts and hand released version/digest pinning to consumer owner paths. From 32972162bfee95112b2f79a3427ebe4815e24b39 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 04:09:38 +0900 Subject: [PATCH 10/18] test(application-service): harden HTTP readiness authority RED --- .../application_service_http_readiness_red.rs | 87 ++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/tests/application_service_http_readiness_red.rs b/tests/application_service_http_readiness_red.rs index a53611ad..f0b0e46b 100644 --- a/tests/application_service_http_readiness_red.rs +++ b/tests/application_service_http_readiness_red.rs @@ -4,7 +4,7 @@ use std::{ fs, - io::Write, + io::{Read, Write}, net::TcpListener, os::unix::fs::PermissionsExt, path::PathBuf, @@ -106,6 +106,49 @@ fn spawn_http_response(listener: TcpListener, response: &'static [u8]) -> thread }) } +fn spawn_http_response_capturing_request( + listener: TcpListener, + response: &'static [u8], +) -> thread::JoinHandle> { + thread::spawn(move || { + let (mut stream, _) = listener.accept().expect("readiness probe should connect"); + let mut request = Vec::new(); + let mut chunk = [0_u8; 128]; + while !request.ends_with(b"\r\n\r\n") { + let bytes_read = stream + .read(&mut chunk) + .expect("HTTP readiness request should be readable"); + if bytes_read == 0 { + break; + } + request.extend_from_slice(&chunk[..bytes_read]); + assert!(request.len() <= 1_024, "HTTP readiness request must stay bounded"); + } + stream + .write_all(response) + .expect("HTTP response should be writable"); + request + }) +} + +fn assert_malformed_http_status_is_not_ready(response: &'static [u8]) { + let listener = TcpListener::bind(("127.0.0.1", 0)).expect("loopback listener should bind"); + let ready_port = listener + .local_addr() + .expect("listener should expose its address") + .port(); + let responder = spawn_http_response(listener, response); + let (program, log) = write_fake_podman(ready_port); + let adapter = RootlessPodmanAdapter::new(program.clone()); + + assert_eq!( + adapter.launch_at(&request(), &policy(), 1_780_000_000), + Err(ApplicationServiceError::ReadinessTimeout) + ); + responder.join().expect("HTTP responder should finish"); + remove_fixture(program, log); +} + #[test] fn http_service_does_not_become_ready_from_tcp_acceptance_alone() { let listener = TcpListener::bind(("127.0.0.1", 0)).expect("loopback listener should bind"); @@ -155,6 +198,48 @@ fn http_service_becomes_ready_after_a_bounded_success_response() { remove_fixture(program, log); } +#[test] +fn http_readiness_request_uses_the_selected_loopback_authority() { + let listener = TcpListener::bind(("127.0.0.1", 0)).expect("loopback listener should bind"); + let ready_port = listener + .local_addr() + .expect("listener should expose its address") + .port(); + let responder = spawn_http_response_capturing_request( + listener, + b"HTTP/1.1 204 No Content\r\nContent-Length: 0\r\nConnection: close\r\n\r\n", + ); + let (program, log) = write_fake_podman(ready_port); + let adapter = RootlessPodmanAdapter::new(program.clone()); + + let lease = adapter + .launch_at(&request(), &policy(), 1_780_000_000) + .expect("valid HTTP response should establish protocol readiness"); + let captured_request = responder.join().expect("HTTP responder should finish"); + let captured_request = std::str::from_utf8(&captured_request) + .expect("HTTP readiness request should use ASCII header syntax"); + assert!(captured_request.contains(&format!("\r\nHost: 127.0.0.1:{ready_port}\r\n"))); + + adapter + .terminate_at(&lease, 1_780_000_001) + .expect("successful HTTP lease should remain cleanable"); + remove_fixture(program, log); +} + +#[test] +fn malformed_http_status_with_invalid_second_digit_is_not_readiness() { + assert_malformed_http_status_is_not_ready( + b"HTTP/1.1 2x0 Invalid\r\nContent-Length: 0\r\nConnection: close\r\n\r\n", + ); +} + +#[test] +fn malformed_http_status_with_invalid_third_digit_is_not_readiness() { + assert_malformed_http_status_is_not_ready( + b"HTTP/1.1 20x Invalid\r\nContent-Length: 0\r\nConnection: close\r\n\r\n", + ); +} + #[test] fn http_server_error_is_not_readiness() { let listener = TcpListener::bind(("127.0.0.1", 0)).expect("loopback listener should bind"); From b481086cbd13a1e94cf8df09d49efb9fa3200e85 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 04:16:28 +0900 Subject: [PATCH 11/18] fix(application-service): validate HTTP readiness authority --- src/infrastructure/podman.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/infrastructure/podman.rs b/src/infrastructure/podman.rs index 19f17c5b..d34c0cbd 100644 --- a/src/infrastructure/podman.rs +++ b/src/infrastructure/podman.rs @@ -21,8 +21,6 @@ use crate::{ const PODMAN_BACKEND_ID: &str = "rootless_podman"; const DEFAULT_COMMAND_TIMEOUT: Duration = Duration::from_secs(30); const DEFAULT_COMMAND_OUTPUT_LIMIT_BYTES: usize = 64 * 1024; -const HTTP_READINESS_REQUEST: &[u8] = - b"GET / HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n"; #[derive(Clone, Debug, Deserialize, PartialEq, Eq)] struct PodmanInfo { @@ -871,7 +869,7 @@ fn wait_for_readiness( return Ok(()); } let probe_timeout = poll.min(deadline.saturating_duration_since(Instant::now())); - if http_response_is_ready(&mut stream, probe_timeout) { + if http_response_is_ready(&mut stream, host_port, probe_timeout) { return Ok(()); } } @@ -880,12 +878,21 @@ fn wait_for_readiness( } } -fn http_response_is_ready(stream: &mut TcpStream, timeout: Duration) -> bool { - let mut status_prefix = [0_u8; 10]; +fn http_response_is_ready(stream: &mut TcpStream, host_port: u16, timeout: Duration) -> bool { + let request = format!( + "GET / HTTP/1.1\r\nHost: 127.0.0.1:{host_port}\r\nConnection: close\r\n\r\n" + ); + let mut status_prefix = [0_u8; 13]; stream .set_read_timeout(Some(timeout)) .and_then(|()| stream.set_write_timeout(Some(timeout))) - .and_then(|()| stream.write_all(HTTP_READINESS_REQUEST)) + .and_then(|()| stream.write_all(request.as_bytes())) .and_then(|()| stream.read_exact(&mut status_prefix)) - .is_ok_and(|()| status_prefix == *b"HTTP/1.1 2") + .is_ok_and(|()| { + status_prefix.starts_with(b"HTTP/1.1 ") + && status_prefix[9] == b'2' + && status_prefix[10].is_ascii_digit() + && status_prefix[11].is_ascii_digit() + && status_prefix[12] == b' ' + }) } From 0819b66d9699e3c44cb7dd4656f881fa1615474c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 04:19:25 +0900 Subject: [PATCH 12/18] docs(application-service): trace readiness review repair --- ...TION_SERVICE_HTTP_READINESS_TRACEABILITY.md | 18 +++++++++++++++++- docs/product-technical-gap-baseline.md | 8 ++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/doctoring/APPLICATION_SERVICE_HTTP_READINESS_TRACEABILITY.md b/docs/doctoring/APPLICATION_SERVICE_HTTP_READINESS_TRACEABILITY.md index 57a120c4..44428728 100644 --- a/docs/doctoring/APPLICATION_SERVICE_HTTP_READINESS_TRACEABILITY.md +++ b/docs/doctoring/APPLICATION_SERVICE_HTTP_READINESS_TRACEABILITY.md @@ -44,6 +44,20 @@ Commit `236d1a67eb90f6d7c10c4714dd2d1d55faee72c6` is the minimum causal repair. The ordinary coverage lane on `59cc738...` separately reproduced `BackendInvocationFailed { operation: "rootless_probe" }` in `root_coverage_edges`, before its intended `network_inspect` failure. That repeated process-invocation class is owned by Issue #71 / Draft #72. #104 must not hide it with retries, mutexes, environment workarounds, or weakened expectations. +## Review-driven protocol-integrity RED and minimum repair + +Code review of hosted-GREEN predecessor `d098385045cefd8b337ba2bd0069107a01756b81` found two acceptance defects in the probe itself. The request used a fixed `Host: 127.0.0.1` even though the actual runtime authority is the dynamically selected `127.0.0.1:`. The response check read only `HTTP/1.1 2`, so malformed status codes whose first character was `2` could be promoted to readiness. + +Test-only exact `32972162bfee95112b2f79a3427ebe4815e24b39` made both findings causal. Native CI `34267331198`, branch-coverage job `102199888859`, reached `tests/application_service_http_readiness_red.rs` and failed exactly three focused controls: the captured request omitted the selected port from `Host`, and malformed `HTTP/1.1 2x0 ...` / `HTTP/1.1 20x ...` responses both produced `Ok(ApplicationServiceLease)` instead of `ReadinessTimeout`. Existing TCP-only timeout, valid HTTP 204, and HTTP 503 controls remained GREEN in the same test binary. + +Minimum descendant `b481086cbd13a1e94cf8df09d49efb9fa3200e85` changes only `src/infrastructure/podman.rs`: + +- the fixed request constant is replaced by a request generated from the already-validated runtime-selected loopback port, producing `Host: 127.0.0.1:`; +- the probe reads a bounded 13-byte HTTP/1.1 status prefix and requires `HTTP/1.1 `, a literal `2`, two ASCII decimal digits, and the required following space; +- existing bounded read/write timeout chaining, fixed `/` path, TCP connect-only readiness, cleanup, and consumer-neutral ownership remain unchanged. + +RFC 9110 section 7.2 defines `Host = uri-host [ ":" port ]` as target authority information. RFC 9112 requires an HTTP/1.1 `Host` field consistent with the target URI authority and defines the status line as HTTP-version, space, a three-digit status code, space, and optional reason phrase. The repair therefore tightens the probe to the protocol grammar without adding application authentication semantics. + ## DDD and security decision `application_service` owns the consumer-neutral meaning of an HTTP-ready leased service. `sandbox_execution` owns bounded readiness/lifecycle and cleanup truth. `infrastructure` performs concrete socket/HTTP I/O against only the runtime-generated loopback endpoint. Podman, Docker/Colima-compatible OCI execution, gVisor/containerd, or Kubernetes adapters must not alter the readiness domain contract merely because the backend changes. @@ -60,6 +74,8 @@ This lane is not release authority until its final exact head reacquires reposit Fielding, R., Nottingham, M., & Reschke, J. (2022). *HTTP semantics (RFC 9110).* RFC Editor. https://www.rfc-editor.org/rfc/rfc9110.html +Thomson, M., & Nottingham, M. (2022). *HTTP/1.1 (RFC 9112).* RFC Editor. https://www.rfc-editor.org/rfc/rfc9112.html + Rust Project Developers. (2026). *TcpStream in std::net*. Rust standard library documentation. https://doc.rust-lang.org/std/net/struct.TcpStream.html -Souppaya, M., Morello, J., & Scarfone, K. (2017). *Application container security guide* (NIST Special Publication 800-190). National Institute of Standards and Technology. https://doi.org/10.6028/NIST.SP.800-190 +Souppaya, M., Morello, J., & Scarfone, K. (2017). *Application container security guide* (NIST Special Publication 800-190). National Institute of Standards and Technology. https://doi.org/10.6028/NIST.SP.800-190 \ No newline at end of file diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 1d87969d..1e6e6965 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,6 +1,6 @@ # Product and Technical Gap Baseline -Last reviewed on 2026-09-09 KST against root Draft PR #1 exact `5c6a44bb2b35eb17d0315d72db242f4488c3c426`, application-readiness Draft #104 lineage through protocol/coverage repair `236d1a67eb90f6d7c10c4714dd2d1d55faee72c6`, protected/default `develop@60a85c7633e03b425b67159ec6822c8178cf87ea`, and the live open PR/Issue/security state. This ledger separates protected truth, active-PR implementation, checked-in RED, causal RED, candidate GREEN, real backend evidence, central required-workflow evidence, and release authority. Evidence from a predecessor SHA never transfers after implementation or dependency movement. +Last reviewed on 2026-09-09 KST against root Draft PR #1 exact `5c6a44bb2b35eb17d0315d72db242f4488c3c426`, application-readiness Draft #104 lineage through review-driven causal RED `32972162bfee95112b2f79a3427ebe4815e24b39` and minimum production repair `b481086cbd13a1e94cf8df09d49efb9fa3200e85`, protected/default `develop@60a85c7633e03b425b67159ec6822c8178cf87ea`, and the live open PR/Issue/security state. This ledger separates protected truth, active-PR implementation, checked-in RED, causal RED, candidate GREEN, real backend evidence, central required-workflow evidence, and release authority. Evidence from a predecessor SHA never transfers after implementation or dependency movement. ## Product responsibility and DDD authority @@ -11,7 +11,7 @@ Quarantine Sandbox Runtime owns reusable hostile-workload/application-service is - `application_service` is a Supporting context and owns consumer-neutral service intent plus translation to/from Core sandbox execution. - `infrastructure` owns concrete Podman/process/backend translation and observation. Podman/gVisor/containerd/Kubernetes/VM types do not become consumer domain contracts. - Wardnet retains verdict/incident/response authority. contextual-orchestrator retains LLM/Agent/tool authorization. Noema retains Agent/runtime capability authority. Keyverse retains identity authority. EgressWeave retains outbound-policy authority. -- Consumer integration is through an immutable released contract/artifact. Sibling source imports, mutable PR-head dependencies, direct foreign runtime calls, and cross-service SQL are not integration mechanisms. +- Consumer integration must use an immutable released contract/artifact. Sibling source imports, mutable PR-head dependencies, direct foreign runtime calls, and cross-service SQL are not integration mechanisms. The runtime currently owns no durable database. Any future durable job/evidence/reaper store requires an explicit persistence ADR, 3NF schema, descriptive multiword `snake_case` objects, retention/recovery semantics, and migration/rollback evidence. @@ -35,7 +35,7 @@ GitHub's immutable-action guidance requires full-length commit SHA pinning for e | --- | --- | --- | --- | | Immutable application identity | Public request requires lower-case SHA-256 digest identity and launch uses `--pull=never`; mutable tags/alternate host-backed transports are rejected on root lineage. | Implemented contract; exact protected integration still required | Keep registry/import/admission outside launch and bind effective image identity before release. | | Rootless / read-only / capability / namespace / seccomp / LSM boundary | Root lineage requests restrictive Podman controls and performs effective checks. Current root hosted lanes are GREEN; positive acceptance remains separate on the dedicated SELinux runner. | Hosted negative and unit/process evidence GREEN; positive confinement pending | Never convert unavailable/unconfined evidence to Verified. Preserve all required controls and obtain real positive effective-LSM evidence. | -| Protocol-aware HTTP readiness / security-consumer bootstrap | Consumer `contextual-orchestrator#1094` run `34238690594` / job `102107934422` failed before Strix model review because Caido never became usable at `127.0.0.1:48080`. Issue #103 / Draft #104 exact RED `65663052ec30bc178adbe5ff4514f5409d10971f` proved that TCP acceptance alone could issue an HTTP lease. Candidate `c05d378cfc736e4257594d69bb06871893ef2d0f` added a bounded loopback HTTP/1.1 `/` probe requiring 2xx while retaining TCP connect semantics. Exact `59cc738f1428d78eaf7a7999e65cc247307b990d` corrected inherited plain-TCP process fixtures and made verify + hosted negative GREEN; its branch coverage then isolated one direct owned helper gap (`1989/1990` lines, `2664/2665` canonical regions, `462/466` branches). Commit `236d1a67eb90f6d7c10c4714dd2d1d55faee72c6` composes bounded socket setup/write/read errors through one fail-closed `io::Result` chain without changing the wire contract or accepting caller URLs. | Causal RED → behavioral candidate GREEN in full verify → exact coverage repair awaiting fresh final-head evidence | Reacquire final-head verify + 100% coverage/branch + hosted negative; keep application login/auth consumer-owned. Positive-LSM, qualifying review and central release gates remain independent. Release immutable runtime before consumer version/digest bump. | +| Protocol-aware HTTP readiness / security-consumer bootstrap | Consumer `contextual-orchestrator#1094` run `34238690594` / job `102107934422` failed before Strix model review because Caido never became usable at `127.0.0.1:48080`. Issue #103 / Draft #104 first causal RED `65663052ec30bc178adbe5ff4514f5409d10971f` proved that TCP acceptance alone could issue an HTTP lease. After the initial protocol and coverage repair reached hosted GREEN at `d098385045cefd8b337ba2bd0069107a01756b81`, review found two narrower protocol defects. Test-only exact `32972162bfee95112b2f79a3427ebe4815e24b39`, CI `34267331198` / branch job `102199888859`, causally failed because the request omitted the dynamically mapped port from `Host`, while malformed `HTTP/1.1 2x0 ...` and `HTTP/1.1 20x ...` responses both returned ready leases. Minimum production `b481086cbd13a1e94cf8df09d49efb9fa3200e85` derives `Host: 127.0.0.1:` from runtime authority and validates a bounded HTTP/1.1 three-digit 2xx status prefix. | Review RED causal; minimum production repair committed, final exact-head validation pending | Reacquire verify + 100% owned-production coverage/branch/source-region + hosted negative on the exact descendant containing code-current docs. Keep login/auth consumer-owned. Positive-LSM, qualifying review and central release gates remain independent; release immutable runtime before consumer version/digest bump. | | Missing Podman security fields | Issue #73 / Draft #89 causally proved that missing `EffectiveCaps`, `BoundingCaps`, or `dns_enabled` was defaulted to apparently secure values. Minimum production `80e6fd19ab5ef11eab3d18b3e80b43f880ce6058` removes only those defaults; the child remains separate. | Causal RED + minimum candidate, not protected-root integrated | Restack/adopt non-force when prerequisites permit; explicit empty/false values remain observed values, while field absence is malformed evidence. | | Podman option/data boundary | Issue #90 / Draft #91 exact `1b1026a92b7d9ef23a709564c1e22aa0421179ff` causally proved missing option termination; production delta is one literal `--` before `request.image_reference`. Exact verify, complete production coverage, branch coverage and hosted negative are GREEN; positive-LSM and qualifying approval remain open. | Hosted exact candidate GREEN; release gates open | Preserve exact argv data and do not shell-join/sanitize. Merge only after positive confinement/review/security gates. | | Public lease deserialization | Issue #84 / Draft #85 exact `e0040d748a8076e17f5103a8a238d2fbd878efbf` preserves private closed wire DTO + validated reconstruction. Verify, complete coverage, branch coverage and hosted negative are GREEN; positive-LSM and qualifying approval remain open. | Hosted exact candidate GREEN; release gates open | Keep strict admission and validated domain construction; do not promote mutable head to consumer authority. | @@ -97,7 +97,7 @@ The first release remains blocked until one exact integrated protected head has, ## Next bounded slices -1. Finish Issue #103 / Draft #104 on its final exact head: require verify, 100% owned production coverage/branch/source-region admission, hosted negative confinement, review, and dedicated positive-LSM evidence. Keep Caido login/consumer bootstrap outside the runtime and publish an immutable runtime artifact before contextual-orchestrator changes its consumed version/digest. +1. Finish Issue #103 / Draft #104 from causal review RED `32972162bfee95112b2f79a3427ebe4815e24b39` through minimum repair `b481086cbd13a1e94cf8df09d49efb9fa3200e85`: require exact-descendant verify, 100% owned production coverage/branch/source-region admission, hosted negative confinement, review, and dedicated positive-LSM evidence. Keep Caido login/consumer bootstrap outside the runtime and publish an immutable runtime artifact before contextual-orchestrator changes its consumed version/digest. 2. Preserve #71/#72 as the canonical repeated `rootless_probe` process-invocation RCA; integrate its typed error evidence only through ordinary non-force ancestry after remaining positive-LSM/review gates. 3. Re-run children blocked by that prerequisite (#92 and focused artifact-evidence lanes) after canonical adoption rather than adding leaf retries/workarounds. 4. Keep CodeQL terminal-verdict failures on `.github#1929`, Dependency Review availability on `.github#810`, and positive-LSM capacity/evidence on `.github#1590`; no leaf bypass or substitute gate. From 153293b480a58d91f929eb32c376eaa9a4ddf358 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 04:26:00 +0900 Subject: [PATCH 13/18] style(application-service): apply rustfmt to HTTP readiness --- src/infrastructure/podman.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/infrastructure/podman.rs b/src/infrastructure/podman.rs index d34c0cbd..b162eae9 100644 --- a/src/infrastructure/podman.rs +++ b/src/infrastructure/podman.rs @@ -879,9 +879,8 @@ fn wait_for_readiness( } fn http_response_is_ready(stream: &mut TcpStream, host_port: u16, timeout: Duration) -> bool { - let request = format!( - "GET / HTTP/1.1\r\nHost: 127.0.0.1:{host_port}\r\nConnection: close\r\n\r\n" - ); + let request = + format!("GET / HTTP/1.1\r\nHost: 127.0.0.1:{host_port}\r\nConnection: close\r\n\r\n"); let mut status_prefix = [0_u8; 13]; stream .set_read_timeout(Some(timeout)) From dc1f1be9ce64dae7eb5d3a91b2beb7fa1f63b86a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 04:26:41 +0900 Subject: [PATCH 14/18] style(application-service): apply rustfmt to readiness tests --- tests/application_service_http_readiness_red.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/application_service_http_readiness_red.rs b/tests/application_service_http_readiness_red.rs index f0b0e46b..ec4c0ab4 100644 --- a/tests/application_service_http_readiness_red.rs +++ b/tests/application_service_http_readiness_red.rs @@ -122,7 +122,10 @@ fn spawn_http_response_capturing_request( break; } request.extend_from_slice(&chunk[..bytes_read]); - assert!(request.len() <= 1_024, "HTTP readiness request must stay bounded"); + assert!( + request.len() <= 1_024, + "HTTP readiness request must stay bounded" + ); } stream .write_all(response) From 84216db4bc4241db01ec9d50958d99bc2368bc67 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 04:32:22 +0900 Subject: [PATCH 15/18] test(application-service): cover malformed HTTP prefix --- tests/application_service_http_readiness_red.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/application_service_http_readiness_red.rs b/tests/application_service_http_readiness_red.rs index ec4c0ab4..78923cfc 100644 --- a/tests/application_service_http_readiness_red.rs +++ b/tests/application_service_http_readiness_red.rs @@ -229,6 +229,13 @@ fn http_readiness_request_uses_the_selected_loopback_authority() { remove_fixture(program, log); } +#[test] +fn malformed_http_protocol_prefix_is_not_readiness() { + assert_malformed_http_status_is_not_ready( + b"NOTP/1.1 204 Invalid\r\nContent-Length: 0\r\nConnection: close\r\n\r\n", + ); +} + #[test] fn malformed_http_status_with_invalid_second_digit_is_not_readiness() { assert_malformed_http_status_is_not_ready( @@ -267,4 +274,4 @@ fn http_server_error_is_not_readiness() { assert!(calls.contains("rm --force")); assert!(calls.contains("network rm --force")); remove_fixture(program, log); -} +} \ No newline at end of file From 4d738ccc52a3acb3d6ea621e306f628074c96122 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 04:37:28 +0900 Subject: [PATCH 16/18] test(application-service): restore rustfmt newline --- tests/application_service_http_readiness_red.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/application_service_http_readiness_red.rs b/tests/application_service_http_readiness_red.rs index 78923cfc..8d4de9c3 100644 --- a/tests/application_service_http_readiness_red.rs +++ b/tests/application_service_http_readiness_red.rs @@ -274,4 +274,4 @@ fn http_server_error_is_not_readiness() { assert!(calls.contains("rm --force")); assert!(calls.contains("network rm --force")); remove_fixture(program, log); -} \ No newline at end of file +} From 61ccfcdbc1ed982704198a66da5c6616e803183c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 17 Sep 2026 10:08:14 +0900 Subject: [PATCH 17/18] docs(readiness): preserve gap evidence before owner repair --- ...SERVICE_HTTP_READINESS_GAP_OWNER_REPAIR.md | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 docs/doctoring/APPLICATION_SERVICE_HTTP_READINESS_GAP_OWNER_REPAIR.md diff --git a/docs/doctoring/APPLICATION_SERVICE_HTTP_READINESS_GAP_OWNER_REPAIR.md b/docs/doctoring/APPLICATION_SERVICE_HTTP_READINESS_GAP_OWNER_REPAIR.md new file mode 100644 index 00000000..2c036a41 --- /dev/null +++ b/docs/doctoring/APPLICATION_SERVICE_HTTP_READINESS_GAP_OWNER_REPAIR.md @@ -0,0 +1,35 @@ +# Application-service HTTP-readiness Gap owner repair + +Reviewed 2026-09-17 KST against Draft PR #104 exact `4d738ccc52a3acb3d6ea621e306f628074c96122`, exact base `5c6a44bb2b35eb17d0315d72db242f4488c3c426`, and repository-wide Gap owner PR #121. + +## Owner-boundary finding + +Issue #103 is a focused application-service readiness contract. It owns protocol-aware readiness semantics, the Podman readiness adapter, its focused tests, and the corresponding PRD/TRD/ADR/TRACEABILITY updates. It does not own the repository-wide live product/technical Gap ledger. Review `5229919282` found that the branch still changed `docs/product-technical-gap-baseline.md`; carrying that file forward could replay a 2026-09-09 repository snapshot over #121's newer owner graph. + +The repair is migration-first. The protocol evidence and consumer-release rule that had also appeared in the global ledger are retained here and in the existing local PRD/TRD/ADR/TRACEABILITY before the global file is restored byte-for-byte to this PR's exact-base blob `bacb346f2ce4259a4f55bd3bece5e871b06d69db`. + +## Retained causal and review evidence + +The repaired causal RED `65663052ec30bc178adbe5ff4514f5409d10971f`, native CI `34255729573`, verify `102160927453`, proved that a runtime-owned loopback socket accepting TCP without an HTTP response could incorrectly satisfy `ServiceProtocol::Http`. The first production repair `c05d378cfc736e4257594d69bb06871893ef2d0f` split protocol semantics: `Tcp` remains connect-only; `Http` sends one bounded fixed-path request to the runtime-owned loopback mapping and requires a successful HTTP response class. + +Legacy success fixtures that were plain TCP listeners were corrected to `Tcp` in `59cc738f1428d78eaf7a7999e65cc247307b990d`. A focused helper-coverage repair followed without widening readiness semantics. Exact predecessor `d098385045cefd8b337ba2bd0069107a01756b81`, CI `34259463376`, reached hosted GREEN for verify, coverage, branch coverage, and hosted negative rootless/AppArmor; its branch artifact and coverage details remain historical predecessor evidence only. + +Code review then identified two narrower protocol-integrity defects: the fixed `Host: 127.0.0.1` omitted the runtime-selected non-default port, and status acceptance treated a prefix beginning with `2` as sufficient. Test-only `32972162bfee95112b2f79a3427ebe4815e24b39`, CI `34267331198`, branch job `102199888859`, executed the causal review RED: the captured request omitted `:`, while malformed `HTTP/1.1 2x0 ...` and `HTTP/1.1 20x ...` were incorrectly accepted. The same focused binary retained TCP/no-response timeout, HTTP 204 success, and HTTP 503 non-readiness controls. + +Minimum production `b481086cbd13a1e94cf8df09d49efb9fa3200e85` derives `Host: 127.0.0.1:` and validates a bounded HTTP/1.1 three-digit 2xx status prefix. It does not add caller-controlled origin/path, authentication, credential handling, egress policy, model/provider semantics, or new wire fields. RFC 9110/9112 basis and the exact contract distinction remain in `APPLICATION_SERVICE_HTTP_READINESS_TRACEABILITY.md`. + +Exact predecessor `4d738ccc52a3acb3d6ea621e306f628074c96122`, native CI `34270054863`, had verify `102209085115`, production coverage `102209084826`, branch coverage `102209084792`, and hosted negative rootless/AppArmor `102209084617` GREEN. Dedicated positive-LSM `102209085130` was still queued and no qualifying approval existed. This hosted GREEN must not transfer after the ownership-only head movement. + +## Consumer and release contract retained locally + +The PR's PRD/TRD/ADR changes make the ownership boundary normative: consumers must consume an immutable released runtime contract/artifact; mutable PR heads, sibling source copies, direct foreign runtime calls, and cross-service SQL are not integration mechanisms. No GitHub Release is claimed by this branch. Protected integration and immutable version/package/SBOM/provenance/reproducibility/rollback publication remain prerequisites before a consumer version/digest bump. + +Caido login/authentication and consumer bootstrap remain consumer-owned. The runtime readiness contract only attests the selected backend-neutral service protocol against the exact runtime-owned loopback mapping. + +## DDD and decision + +`application_service` owns protocol intent and lease/readiness semantics. `infrastructure::podman` owns the concrete loopback probe translation. `sandbox_execution` remains the reusable isolation owner. Requested protocol intent, successful readiness observation, and positive sandbox confinement are independent evidence dimensions. + +Selected repair: preserve all focused source/test/PRD/TRD/ADR/TRACEABILITY changes, add this owner-repair record, and restore only the global Gap file to the exact base. Rejected alternatives are copying #121's latest ledger into this leaf, discarding review/causal history, retaining a second live Gap writer, or force-rebasing merely to remove the file. + +After the docs-only movement, all current-head gates must be reacquired. Historical exact-head GREEN remains useful lineage evidence but is not merge/release authority for the moved head. From 16b5df4db81d854aeee49c4ba28beb5745bbb44b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 17 Sep 2026 10:08:29 +0900 Subject: [PATCH 18/18] docs(readiness): restore global gap owner boundary --- docs/product-technical-gap-baseline.md | 63 +++++++++++++------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 1e6e6965..bacb346f 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,6 +1,6 @@ # Product and Technical Gap Baseline -Last reviewed on 2026-09-09 KST against root Draft PR #1 exact `5c6a44bb2b35eb17d0315d72db242f4488c3c426`, application-readiness Draft #104 lineage through review-driven causal RED `32972162bfee95112b2f79a3427ebe4815e24b39` and minimum production repair `b481086cbd13a1e94cf8df09d49efb9fa3200e85`, protected/default `develop@60a85c7633e03b425b67159ec6822c8178cf87ea`, and the live open PR/Issue/security state. This ledger separates protected truth, active-PR implementation, checked-in RED, causal RED, candidate GREEN, real backend evidence, central required-workflow evidence, and release authority. Evidence from a predecessor SHA never transfers after implementation or dependency movement. +Last reviewed on 2026-09-08 KST against root Draft PR #1 exact `489af256d8e7cdac777f8ece5f50829f4238b35f`, focused coverage-admission Draft #94 integrated candidate `b981dbc4e28b41690fde4d233321f1dfe9eaf377`, protected/default `develop@60a85c7633e03b425b67159ec6822c8178cf87ea`, and the live open PR/Issue/security state. This ledger separates protected truth, active-PR implementation, checked-in RED, causal RED, candidate GREEN, real backend evidence, central required-workflow evidence, and release authority. Evidence from a predecessor SHA never transfers after implementation or dependency movement. ## Product responsibility and DDD authority @@ -11,7 +11,7 @@ Quarantine Sandbox Runtime owns reusable hostile-workload/application-service is - `application_service` is a Supporting context and owns consumer-neutral service intent plus translation to/from Core sandbox execution. - `infrastructure` owns concrete Podman/process/backend translation and observation. Podman/gVisor/containerd/Kubernetes/VM types do not become consumer domain contracts. - Wardnet retains verdict/incident/response authority. contextual-orchestrator retains LLM/Agent/tool authorization. Noema retains Agent/runtime capability authority. Keyverse retains identity authority. EgressWeave retains outbound-policy authority. -- Consumer integration must use an immutable released contract/artifact. Sibling source imports, mutable PR-head dependencies, direct foreign runtime calls, and cross-service SQL are not integration mechanisms. +- Consumer integration is through a future immutable released contract/artifact. Sibling source imports, mutable PR-head dependencies, direct foreign runtime calls, and cross-service SQL are not integration mechanisms. The runtime currently owns no durable database. Any future durable job/evidence/reaper store requires an explicit persistence ADR, 3NF schema, descriptive multiword `snake_case` objects, retention/recovery semantics, and migration/rollback evidence. @@ -19,13 +19,16 @@ The runtime currently owns no durable database. Any future durable job/evidence/ | Gap / gate | Current evidence | Status | Required action | | --- | --- | --- | --- | -| Checkout credential persistence | Root ancestry retains the causal GREEN proving every checkout uses `persist-credentials: false`; current root exact is `5c6a44bb2b35eb17d0315d72db242f4488c3c426`. | Integrated into current Draft root | Do not regress; predecessor GREEN does not replace current-head gates. | -| Root owned-production coverage / canonical source-region admission | Issue #93 / PR #94 was normally integrated into root. Current root native CI `34176680115` has verify `101907363084`, coverage `101907363224`, branch coverage `101907363185`, and hosted negative rootless/AppArmor `101907363162` GREEN. The checker preserves raw LLVM diagnostics while gating canonical owned-production source coordinates. | Exact current-root hosted GREEN | Preserve 100% line/function/canonical-source-region/branch admission and raw LLVM traceability. Positive-LSM and central security gates remain independent. | -| Dedicated positive effective-LSM evidence | Current root positive-LSM job `101907363137` remains queued on `[self-hosted, linux, cwl-hostile-workload, selinux]`. Hosted negative rootless/AppArmor is GREEN but is not positive confinement evidence. | Independent release/security lane pending dedicated runner | `.github#1590` remains canonical owner. Do not substitute hosted negative evidence or weaken the LSM gate. | -| Repeated backend spawn/invocation failure | Earlier root/process lanes and #92 reproduced generic `BackendInvocationFailed { operation: "rootless_probe" }` before their intended backend failures. Issue #71 / Draft #72 causally executed missing-executable/provider-neutral REDs and now carries exact `900d0273625115f7bcc602d888baadded0caeb4f`; native CI `34231220456` has verify `102077568988`, coverage `102077568604`, branch `102077568933`, hosted negative `102077569052` GREEN, while positive-LSM `102077569145` remains queued and no qualifying approval exists. #104 ordinary coverage at `59cc738...` independently reproduced the same `rootless_probe` class in `root_coverage_edges`. | Canonical typed-error repair hosted GREEN; independent positive-LSM/review gates still open | Preserve #72 as owner. Do not add retry, mutex, environment workaround, or weakened expectations in consumer children. Integrate non-force only after its remaining gates. | -| Repository workflow SHA-pin validation | Issue #86 / Draft #92 owns external full-SHA versus same-repository `$/...` policy. Current exact `30c5de5c73d729a1695609325d867a456eaf2dda`; policy validator tests, coverage-parser tests, formatting, complete production coverage, branch coverage and hosted negative are GREEN. Broader verify fails only in inherited `rootless_probe` process/backend invocation before the intended cleanup failure. | Policy-specific candidate GREEN; inherited #71/#72 runtime prerequisite blocks full verify | Do not mutate the validator or use retry. Adopt the canonical runtime prerequisite non-force, then rerun exact full gates. | -| Central CodeQL terminal verdict | Current root CodeQL `34176680113` dispatched exact-head analysis but python `101907470730` and actions `101907470755` failed `Release runner or enforce current-head CodeQL verdict`. | Central required-workflow failure | `.github#1929` remains canonical owner. Dispatch is not a terminal CodeQL verdict. | -| Dependency Review availability | Current root Security Scan `34176680132` verified exact head; OSV, Scorecard and Trivy succeeded, while dependency-review `101907475098` failed `Check dependency review support` and actual Dependency Review was skipped. | Central required-workflow failure | `.github#810` owns availability. Do not substitute sibling scanners. | +| Checkout credential persistence | Predecessor root `7482108c0b74f58f447722a98330f9ad44215eec`, native CI `34089522598`, verify `101640016166` proved every checkout uses `persist-credentials: false`. | Causal GREEN preserved in root ancestry | Do not regress; predecessor GREEN does not replace current-root gates. | +| Root owned-production coverage and process-fixture stability | Root lineage includes focused reachable-edge tests plus production simplification `bd23862df508dfec8f24fe7dbc74b57d23491bac`, which removes only parser-dominated empty-token predicates while retaining reachable `unconfined`, AppArmor enforce-mode, inspect-label, and nonzero capability checks. Exact #94 child `4990976015c554df1cc485135dfa1d5e3a38e4ca`, carrying that root implementation, executed the full branch suite successfully: LLVM raw totals functions `189/189`, lines `1973/1973`, regions `2629/2630`, branches `452/452`; the canonical source-region checker reported `2630/2630`. All process-backed suites in that lane passed. | Runtime branch behavior and canonical source-region gate have hosted candidate GREEN on child ancestry; root itself moved only by formatting repair and still needs normal integration/current gates | Integrate the proven coverage-admission child normally into the root only after review/current required gates permit it, then re-prove the exact integrated root without weakening reachable security checks. | +| Root formatting defect exposed by exact child CI | Exact #94 head `499097...` verify `101905358746` passed exact checkout, dependency lock, repository policy, and all eight coverage-parser tests, then failed only because inherited `tests/root_coverage_reachable_edges.rs` was not rustfmt-clean. Root commit `489af256d8e7cdac777f8ece5f50829f4238b35f` applies the minimal rustfmt-only repair. #94 adopted it by ordinary two-parent non-force merge `b981dbc4e28b41690fde4d233321f1dfe9eaf377`. | Causal failure → canonical root repair → non-force descendant adoption | Preserve the root-owned fix; do not carry a child-only formatting workaround or force-rebase descendants. | +| LLVM source-region admission | Issue #93 / Draft #94 audited immutable root artifact `10034315670` and found all unique source coordinates executed despite raw `bounded_command.rs=440/441`. Review `5136297577` found helper-only RED coverage did not bind release admission, so test-only `31b7e7b40a374122c938089e374bd6303e7e9c4a` added `main()` admission REDs. Checker candidate `1454644358190356aa5a84080129d24a1bcac77e` gates production source-region union by filename + source start/end + region kind, fails on denominator inconsistency, and retains raw LLVM output. Exact hosted head `499097...` produced branch artifact `10037275727` (`sha256:41be203dce7d8588f2ab450cf63983a9f4053515afad35762e86bfcb466bc189`) with raw regions `2629/2630`, canonical source regions `2630/2630`, and branches `452/452`. After non-force root adoption, exact `b981dbc...` CI `34176200159` has verify `101905976342`, coverage `101905976423`, branch coverage `101905976383`, and hosted negative rootless/AppArmor `101905976218` all GREEN. | Causal RED → minimum checker → hosted exact candidate GREEN on dependency-safe child ancestry | Keep raw LLVM diagnostics and 100% canonical source-region threshold. Merge only through normal review/protection flow; positive-LSM/release authority remains independent. | +| Dedicated positive effective-LSM evidence | Exact `b981dbc...` positive-LSM job `101905976348` remains queued on `[self-hosted, linux, cwl-hostile-workload, selinux]`; hosted negative rootless/AppArmor is GREEN but is not positive confinement evidence. | Independent release/security lane blocked on dedicated runner | `.github#1590` remains canonical owner. Do not substitute hosted negative evidence or weaken the LSM gate. | +| Repeated backend spawn failure is under-classified | Earlier root verify `101852033066` failed a process-backed application-service test at `rootless_probe` before the intended `container_create` failure. Dependency-safe #92 coverage independently hit the same generic `rootless_probe` invocation class in another integration-test binary while #92 verify was GREEN. Current root still maps `BoundedCommandError::{Spawn, Wait, Capture}` to one `BackendInvocationFailed`. Issue #71 / Draft #72 already has an executed missing-executable RED and bounded `std::io::ErrorKind` classification candidate, but #72 remains on older ancestry. | Real repeated RCA evidence + canonical typed-evidence owner exists; current-root integration pending | After root coverage integration stabilizes, preserve #72's `Spawn(io::ErrorKind)` and bounded public classes by ordinary non-force integration. Do not add retry, extra mutexes, or isolation weakening before the concrete OS class is observable. | +| Repository workflow SHA-pin validation | Issue #86 / Draft #92 executed both the multi-workflow SHA-pin RED and the same-repository `$/...` false-positive RED. Minimum production `d906165bf081b3e4d8ca9d71e48bdb6888cab9e8` admits only `$/` self-repository targets before retaining the exact 40-character lowercase SHA rule for external dependencies. #92 is on predecessor root ancestry. | Two causal REDs + minimum candidate; dependency restack required after root stabilization | Non-force adopt the final current root while preserving #92's child-owned files, then reacquire exact-head repository-policy/full-gate evidence. Add no tag/branch exception. | +| Central CodeQL terminal verdict | Predecessor root CodeQL detected languages and dispatched current-head analysis but failed the central `Release runner or enforce current-head CodeQL verdict` stage. | Central required-workflow failure; current root also requires fresh result | `.github#1929` remains canonical owner. Do not reinterpret dispatch success as terminal CodeQL acceptance or patch leaf status. | +| Dependency Review availability | Predecessor root Security Scan verified exact checkout/head then failed `Check dependency review support`; actual Dependency Review was skipped while Trivy/OSV/Scorecard succeeded independently. | Central required-workflow failure; current root also requires fresh result | `.github#810` owns availability. Do not substitute other scanners for Dependency Review. | +| SAST | Predecessor root SAST Semgrep was GREEN; current root `489af...` has a new SAST execution and must be judged on its exact result. | Reacquire after source movement | Do not treat one security gate or predecessor result as full release authority. | GitHub's immutable-action guidance requires full-length commit SHA pinning for external actions, while its `$/` self-repository syntax is already bound to the exact running workflow commit and must not carry an `@ref` suffix. Organization/reusable CI/review/security/release policy remains canonical in `ContextualWisdomLab/.github`; repository validation is defense in depth and must preserve that distinction without duplicating central workflow implementation. @@ -33,14 +36,13 @@ GitHub's immutable-action guidance requires full-length commit SHA pinning for e | Capability | Current evidence | Status | Required action | | --- | --- | --- | --- | -| Immutable application identity | Public request requires lower-case SHA-256 digest identity and launch uses `--pull=never`; mutable tags/alternate host-backed transports are rejected on root lineage. | Implemented contract; exact protected integration still required | Keep registry/import/admission outside launch and bind effective image identity before release. | -| Rootless / read-only / capability / namespace / seccomp / LSM boundary | Root lineage requests restrictive Podman controls and performs effective checks. Current root hosted lanes are GREEN; positive acceptance remains separate on the dedicated SELinux runner. | Hosted negative and unit/process evidence GREEN; positive confinement pending | Never convert unavailable/unconfined evidence to Verified. Preserve all required controls and obtain real positive effective-LSM evidence. | -| Protocol-aware HTTP readiness / security-consumer bootstrap | Consumer `contextual-orchestrator#1094` run `34238690594` / job `102107934422` failed before Strix model review because Caido never became usable at `127.0.0.1:48080`. Issue #103 / Draft #104 first causal RED `65663052ec30bc178adbe5ff4514f5409d10971f` proved that TCP acceptance alone could issue an HTTP lease. After the initial protocol and coverage repair reached hosted GREEN at `d098385045cefd8b337ba2bd0069107a01756b81`, review found two narrower protocol defects. Test-only exact `32972162bfee95112b2f79a3427ebe4815e24b39`, CI `34267331198` / branch job `102199888859`, causally failed because the request omitted the dynamically mapped port from `Host`, while malformed `HTTP/1.1 2x0 ...` and `HTTP/1.1 20x ...` responses both returned ready leases. Minimum production `b481086cbd13a1e94cf8df09d49efb9fa3200e85` derives `Host: 127.0.0.1:` from runtime authority and validates a bounded HTTP/1.1 three-digit 2xx status prefix. | Review RED causal; minimum production repair committed, final exact-head validation pending | Reacquire verify + 100% owned-production coverage/branch/source-region + hosted negative on the exact descendant containing code-current docs. Keep login/auth consumer-owned. Positive-LSM, qualifying review and central release gates remain independent; release immutable runtime before consumer version/digest bump. | -| Missing Podman security fields | Issue #73 / Draft #89 causally proved that missing `EffectiveCaps`, `BoundingCaps`, or `dns_enabled` was defaulted to apparently secure values. Minimum production `80e6fd19ab5ef11eab3d18b3e80b43f880ce6058` removes only those defaults; the child remains separate. | Causal RED + minimum candidate, not protected-root integrated | Restack/adopt non-force when prerequisites permit; explicit empty/false values remain observed values, while field absence is malformed evidence. | -| Podman option/data boundary | Issue #90 / Draft #91 exact `1b1026a92b7d9ef23a709564c1e22aa0421179ff` causally proved missing option termination; production delta is one literal `--` before `request.image_reference`. Exact verify, complete production coverage, branch coverage and hosted negative are GREEN; positive-LSM and qualifying approval remain open. | Hosted exact candidate GREEN; release gates open | Preserve exact argv data and do not shell-join/sanitize. Merge only after positive confinement/review/security gates. | -| Public lease deserialization | Issue #84 / Draft #85 exact `e0040d748a8076e17f5103a8a238d2fbd878efbf` preserves private closed wire DTO + validated reconstruction. Verify, complete coverage, branch coverage and hosted negative are GREEN; positive-LSM and qualifying approval remain open. | Hosted exact candidate GREEN; release gates open | Keep strict admission and validated domain construction; do not promote mutable head to consumer authority. | -| Public cleanup-receipt deserialization | Issue #87 / Draft #88 exact `08fed96e0193d80f380aed07f6bcf4b2c29ca397` has verify, complete coverage, branch coverage and hosted negative GREEN. Stale review findings based on obsolete root ancestry were answered and resolved; positive-LSM and qualifying approval remain open. | Hosted exact candidate GREEN; release gates open | Preserve serialization/admission boundary; deserialized evidence never becomes destructive authority. | -| Effective network binding | Draft #23 retains exact network-attachment, acquired-network-identity, foreign-safe cleanup, and negative-egress REDs. | P0 REDs staged; production truth incomplete | Require exact acquired network identity, exclusive deny-by-default attachment, non-force foreign-safe cleanup, and real negative-egress evidence. | +| Immutable application identity | Public request requires lower-case SHA-256 digest identity and launch uses `--pull=never`; mutable tags/alternate host-backed transports are rejected on root lineage. | Implemented contract; exact current runtime evidence still required on protected integration | Keep registry/import/admission outside launch and bind effective image identity before release. | +| Rootless / read-only / capability / namespace / seccomp / LSM boundary | Root lineage requests restrictive Podman controls and performs effective checks. Exact child coverage/hosted-negative lanes prove reachable fail-closed behavior including SELinux `unconfined` rejection after the parser-dominated simplification. Positive acceptance remains separate on the dedicated SELinux runner. | Hosted negative and unit/process evidence GREEN on dependency-safe child; positive confinement still pending | Never convert unavailable/unconfined evidence to Verified. Preserve all required controls and obtain real positive effective-LSM evidence. | +| Missing Podman security fields | Issue #73 / Draft #89 causally proved that missing `EffectiveCaps`, `BoundingCaps`, or `dns_enabled` was defaulted to apparently secure values. Minimum production `80e6fd19ab5ef11eab3d18b3e80b43f880ce6058` removes only those defaults; the child remains on predecessor root ancestry. | Causal RED + minimum candidate, not current-root-integrated GREEN | Restack non-force after root stabilizes; explicit empty/false values remain observed values, while field absence is malformed evidence. | +| Podman option/data boundary | Issue #90 / Draft #91 requires literal `--` immediately before consumer-controlled digest-pinned image while preserving command argv. | RED-only child | After causal RED, add only the option terminator before the image operand. Do not shell-join or sanitize argv data. | +| Public lease deserialization | Issue #84 / Draft #85 tests that public Serde construction can bypass runtime-owned lease/endpoint/attestation invariants and closed schema constraints. | RED-only child | After causal RED, remove public deserialization if evidence is serialization-only or reconstruct through strict wire DTO + validated domain constructors. | +| Public cleanup-receipt deserialization | Issue #87 / Draft #88 tests that cleanup evidence can be caller-constructed outside its invariant-establishing path. | RED-only child | Enforce strict wire admission or serialization-only evidence; deserialized evidence never becomes destructive authority. | +| Effective network binding | Draft #23 retains exact network-attachment, acquired-network-identity, foreign-safe cleanup, and negative-egress REDs. | P0 REDs staged; production truth incomplete | Execute on current root ancestry; require exact acquired network identity, exclusive deny-by-default attachment, non-force foreign-safe cleanup, and real negative-egress evidence. | | CPU/RAM/PID/tmpfs/wall time | Applied Podman configuration exists, but inspect/configuration is not equivalent to authoritative cgroup/mount/termination evidence. Draft #19 owns resource/namespace/image/argv applied-state REDs. | Backend-applied intent; live proof incomplete | Bind inspect state only after causal RED, then verify live cgroup-v2, tmpfs mount restrictions/size, and runtime-owned wall-time termination. | | Runtime/lifecycle ownership | Draft #21 retains independent invocation collision, exact acquired container ID, malformed create-ID, and destructive-authority REDs. | REDs staged | Preserve consumer `request_id` as correlation; use collision-resistant invocation identity and exact acquired backend IDs for lifecycle authority. | | Pre-attestation command execution | Command path can start hostile payload before positive effective isolation is established; issue #25 remains the hold/attest/release boundary owner. | Security gap; RED staged downstream | Require a trusted hold/attest/release primitive or equivalent. Cleanup after execution does not undo pre-attestation code execution. | @@ -51,15 +53,11 @@ GitHub's immutable-action guidance requires full-length commit SHA pinning for e | Capability | Current evidence | Status | Required action | | --- | --- | --- | --- | | Static foundation | Bounded ingestion, SHA-256 artifact identity, non-executing format detection, deterministic evidence ordering, analyzer interface, and failure attribution exist on active foundation #18. | Active foundation, unmerged | Preserve `artifact_analysis` ownership and exact-head verification. | -| Analyzer worker Core ownership | Issue #69 / Draft #70 executed a Core-boundary E0432 RED, then a seven-required-control semantic RED. Minimum production requires rootless/read-only/cap-drop/NNP/userns/seccomp/LSM `Verified`. | Causal RED + minimum Core candidate | Obtain exact candidate GREEN with applicable isolation evidence before parent integration. | -| Bounded source-context semantic presence | Issue #95 / Draft #96 executed the all-null schema/domain mismatch RED. Minimum schema candidate adds only semantic-presence `anyOf` branches while retaining optional top-level/per-field nullability. | Executed RED + minimum schema candidate | Reacquire exact candidate gates and preserve published/Rust acceptance-set parity. | -| Artifact ingestion name invariant | Issue #97 / Draft #98 owns policy/name admission parity with `ArtifactDescriptor`; formatter prerequisite was repaired without production change. | RED lane executing | Require configured maximum within canonical 255-byte descriptor bound and reject non-leaf names without basename coercion/truncation. | -| Analyzer producer namespace authority | Issue #99 / Draft #100 executed reserved `runtime_core` producer collision RED; minimum candidate introduces one private runtime-core producer identifier and rejects analyzer collision through the existing error class. | Causal RED + minimum candidate | Reacquire exact gates; keep evidence taxonomy ownership in `artifact_analysis`, not Core. | -| UTF-8 byte-bound publication | Issue #101 / Draft #102 owns Rust-byte versus JSON-Schema-character acceptance mismatch; formatter prerequisite repaired. | RED lane executing | Make byte semantics executable/fail-closed without relaxing Rust byte limits or pretending an unsupported extension keyword is a standard assertion. | +| Analyzer worker Core ownership | Issue #69 / Draft #70 executed a Core-boundary E0432 RED, then a seven-required-control semantic RED. Minimum production requires rootless/read-only/cap-drop/NNP/userns/seccomp/LSM `Verified`; the current parent candidate remains separate from root repair. | Causal RED + minimum Core candidate | Obtain exact candidate GREEN with applicable isolation evidence before parent integration. | | Analyzer evidence producer authority | Issue #77 / Draft #78 rejects untrusted worker claims to controller/runtime-owned evidence kinds while retaining analyzer-owned static evidence. | RED-only child of #70 | After causal RED, add the smallest Supporting-context producer-authority predicate; do not move taxonomy ownership into Core. | | Worker process exit vs completed outcome | Issue #79 / Draft #80 requires `Completed` only with exact worker `Exited { exit_code: 0 }`. | RED-only child of #70 | Add one cross-field ACL only after causal execution; semantic analyzer failure remains distinct from process failure. | | Worker cleanup identity | Issue #81 / Draft #82 requires cleanup evidence to identify the exact worker rather than expose an unscoped completion boolean. | RED-only child of #70 | Introduce backend-neutral Core cleanup evidence with exact worker identity after causal RED, then consume it from `artifact_analysis`. | -| Evidence-bundle unknown fields | Issue #75 / Draft #76 tests Rust deserialization against the published Draft 2020-12 `additionalProperties: false` contract. Current focused strict-deserialization controls pass, while broader workspace execution is blocked by the inherited #71/#72 backend invocation class. | Focused candidate GREEN; inherited prerequisite open | Adopt canonical runtime prerequisite non-force and rerun full exact gates; do not mutate evidence ACL for the unrelated backend failure. | +| Evidence-bundle unknown fields | Issue #75 / Draft #76 tests that Rust deserialization must match the published Draft 2020-12 `additionalProperties: false` contract. | RED-only | After causal RED, add strict unknown-field rejection to published evidence wire structures without changing schema version/shape. | | Dynamic analysis truthfulness | #49 containment and #50/#52/#54/#56/#58/#60/#62/#64/#66 evidence/resource/provenance/cardinality/identity families remain independent focused owners above #18. | Multiple staged REDs; no release-grade detonation worker | Do not execute hostile bytes in the Rust controller. Worker execution must consume Core isolation and preserve attributable provenance/completeness. | | YARA-X / capa / Ghidra / LIEF adapters | No release-grade production adapters yet. | Missing | Add one bounded evidence producer at a time with immutable tool/version/digest/config provenance and hostile fixtures. | @@ -75,7 +73,7 @@ Configured intent, backend-applied inspection, and live effective enforcement ar - Wall-time claims require behavioral termination plus cleanup evidence. - Request/receipt equality proves contradiction resistance, not confinement. - Static analysis may not assert observed runtime behavior. -- Fake Podman/process tests may establish parser/ACL/cleanup/readiness behavior but are never release-grade positive isolation evidence. +- Fake Podman/process tests may establish parser/ACL/cleanup behavior but are never release-grade positive isolation evidence. - Canonical source-region coverage and compiler-instantiation coverage are distinct measurement targets; raw LLVM summaries remain traceable even when canonical source-region admission reconciles duplicate instances. ## Protected integration and release authority @@ -97,11 +95,12 @@ The first release remains blocked until one exact integrated protected head has, ## Next bounded slices -1. Finish Issue #103 / Draft #104 from causal review RED `32972162bfee95112b2f79a3427ebe4815e24b39` through minimum repair `b481086cbd13a1e94cf8df09d49efb9fa3200e85`: require exact-descendant verify, 100% owned production coverage/branch/source-region admission, hosted negative confinement, review, and dedicated positive-LSM evidence. Keep Caido login/consumer bootstrap outside the runtime and publish an immutable runtime artifact before contextual-orchestrator changes its consumed version/digest. -2. Preserve #71/#72 as the canonical repeated `rootless_probe` process-invocation RCA; integrate its typed error evidence only through ordinary non-force ancestry after remaining positive-LSM/review gates. -3. Re-run children blocked by that prerequisite (#92 and focused artifact-evidence lanes) after canonical adoption rather than adding leaf retries/workarounds. -4. Keep CodeQL terminal-verdict failures on `.github#1929`, Dependency Review availability on `.github#810`, and positive-LSM capacity/evidence on `.github#1590`; no leaf bypass or substitute gate. -5. Execute #70's current minimum Core worker candidate; only after exact GREEN may #78/#80/#82 proceed independently with their own causal RED→minimum GREEN loops. -6. Preserve #85/#88/#91 complete deltas and exact hosted evidence; do not merge while qualifying review/positive confinement remains open. -7. Reconcile the ordered command/release path `#1 -> #6 -> #9 -> #10 -> #13 -> #14 -> #18` dependency-first without force, then execute resource/network/runtime-identity/pre-attestation RED owners on current ancestry. -8. Publish no release until a normal merge produces an exact protected `develop` head and that exact integrated SHA reacquires every release gate above; only then publish immutable version/artifacts and hand released version/digest pinning to consumer owner paths. +1. Preserve Draft #94's current dependency-safe ancestry and obtain the remaining review/current exact-head gates; its hosted verify, coverage, branch coverage, and hosted-negative lanes are GREEN at `b981dbc...`, while positive-LSM remains an independent queued release lane. +2. Normally integrate #94 into root #83 only when protection/review allows it, then execute the exact integrated root and require canonical source regions and branches to remain 100% while reachable Podman security checks remain fail closed. +3. Once root coverage/process fixtures are stable, non-force integrate #71/#72's typed spawn-failure evidence onto current root ancestry and classify any real `rootless_probe` OS failure before considering retry policy. +4. Non-force restack issue #86 / Draft #92 onto the stabilized root, preserving its executed `$/` self-reference RED and minimum external-SHA/self-reference repair. Re-run repository policy and full gates on the restacked exact head. +5. Keep CodeQL terminal-verdict failures on `.github#1929`, Dependency Review availability on `.github#810`, and positive-LSM capacity/evidence on `.github#1590`; no leaf bypass or substitute gate. +6. Execute #70's current minimum Core worker candidate; only after exact GREEN may #78/#80/#82 proceed independently with their own causal RED→minimum GREEN loops. +7. Restack root children #89/#85/#88/#91 independently after root stabilization; preserve every child delta by ordinary non-force integration and do not fold unrelated repairs together. +8. After root exact GREEN, reconcile the ordered command/release path `#1 -> #6 -> #9 -> #10 -> #13 -> #14 -> #18` dependency-first without force, then execute resource/network/runtime-identity/pre-attestation RED owners on current ancestry. +9. Publish no release until a normal merge produces an exact protected `develop` head and that exact integrated SHA reacquires every release gate above; only then publish immutable version/artifacts and hand released version/digest pinning to consumer owner paths.