Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ Changelog tracking starts with 0.2.0. Prior versions were not tracked.
refusal or eviction always falls back to verified reads. Cold reopen tests
corrupt a neighbour frame after recovery and prove that no process-local
evidence can suppress its checksum failure. Closes #1399.
- **Capsule inbound TCP bind (`bind_tcp`).** A capsule can bind an inbound TCP port and accept connections from its run loop via the WIT `bind-tcp(host, port) -> tcp-listener` fn (`accept` / `poll-accept` / `local-addr`), gated by the per-capsule `net_bind = ["host:port", "host:*"]` manifest capability (new fail-closed `check_net_tcp_bind` gate reusing the `net_connect` host:port matcher). A post-gate loopback airlock refuses non-loopback binds; accepted connections reuse the existing `NetStream::Tcp` read/write/close plumbing. The runtime primitive the srouter capsule needs (loopback ingress on 127.0.0.1:8788). Closes #1230.
- **Windows local transport uses authenticated per-user named pipes.** A pipe
name derived only from the caller's operating-system SID replaces
filesystem endpoint naming on Windows. Local-only byte-mode instances use a
Expand Down
14 changes: 14 additions & 0 deletions crates/astrid-audit/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,14 @@ pub enum AuditAction {
addr: String,
},

/// Inbound TCP connection accepted by a capsule listener.
NetAccept {
/// Host-observed local listener endpoint.
local_addr: String,
/// Host-observed remote peer endpoint.
peer_addr: String,
},

/// Child-process spawn by a capsule host call (`astrid:process` spawn).
///
/// Recorded for every spawn attempt — allowed, failed, or denied — so a
Expand Down Expand Up @@ -540,6 +548,12 @@ impl AuditAction {
Self::NetBind { addr } => {
format!("Bound socket {addr}")
},
Self::NetAccept {
local_addr,
peer_addr,
} => {
format!("Accepted connection from {peer_addr} on {local_addr}")
},
Self::ProcessSpawn { command } => {
format!("Spawned process {command}")
},
Expand Down
7 changes: 7 additions & 0 deletions crates/astrid-capsule/src/audit_sink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ pub enum HostAuditEvent<'a> {
/// The bind address.
addr: &'a str,
},
/// An inbound TCP connection accepted by a capsule listener.
NetAccept {
/// Host-observed local listener endpoint.
local_addr: &'a str,
/// Host-observed remote peer endpoint.
peer_addr: &'a str,
},
/// A child-process spawn.
ProcessSpawn {
/// The command being executed.
Expand Down
27 changes: 27 additions & 0 deletions crates/astrid-capsule/src/engine/wasm/host/audit_sink_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ enum CapturedEvent {
FileDelete(String),
NetConnect(String, u16),
NetBind(String),
NetAccept(String, String),
ProcessSpawn(String),
}

Expand All @@ -33,6 +34,10 @@ impl CapturedEvent {
HostAuditEvent::FileDelete { path } => Self::FileDelete(path.to_owned()),
HostAuditEvent::NetConnect { host, port } => Self::NetConnect(host.to_owned(), port),
HostAuditEvent::NetBind { addr } => Self::NetBind(addr.to_owned()),
HostAuditEvent::NetAccept {
local_addr,
peer_addr,
} => Self::NetAccept(local_addr.to_owned(), peer_addr.to_owned()),
HostAuditEvent::ProcessSpawn { command } => Self::ProcessSpawn(command.to_owned()),
}
}
Expand Down Expand Up @@ -167,6 +172,28 @@ async fn audit_net_reports_connect() {
);
}

#[tokio::test]
async fn audit_net_accept_carries_host_observed_endpoints() {
let (state, sink) = state_with_sink(tokio::runtime::Handle::current());
let alice = PrincipalId::new("alice").unwrap();

super::net::audit_net_accept(
&state,
"127.0.0.1:8788",
"127.0.0.1:49152",
&Ok::<(), ()>(()),
);

assert_eq!(
sink.snapshot(),
vec![(
alice,
CapturedEvent::NetAccept("127.0.0.1:8788".into(), "127.0.0.1:49152".into()),
CapturedOutcome::Allowed,
)]
);
}

#[tokio::test]
async fn audit_net_reports_bind_denied() {
// A denied socket bind (capsule lacks `net_bind`) currently leaves no
Expand Down
Loading
Loading