Silk

Native socket connections

Acquire owned or scoped TCP and pathname-Unix ByteDuplex connections with bounded cooperative polling.

silk.native_socket is the ordinary-source native transport for numeric TCP endpoints and local pathname-Unix sockets. connectResolvedOwned and connectUnixOwned return the authoritative affine Connection; their scoped counterparts consume that owner and lend it to one callback. The module is selected only for aarch64 Apple Darwin with system libc and aarch64 or x86-64 GNU/Linux with GNU libc. Portable, no-libc, musl, and Windows profiles do not expose it or its foreign imports.

Open a local pathname connection

import silk.byte_duplex {ByteDuplex, ByteIoError}
import silk.effect {Effect}
import silk.monotonic_clock {MonotonicClock}
import silk.native_socket {ConnectOptions, Connection, NativeSocketError, connectUnix}
import silk.option {Option}
import silk.system_clock {Instant}

effect fn sendRequest(connection: &mut Connection) -> ()
! ByteIoError
? &mut MonotonicClock {
  let accepted = run ByteDuplex.writeSome(
    b"status\n",
    Option.none<Instant>(),
  ) |> Effect.provideMut<ByteDuplex>(&mut connection.*)
  drop accepted
  return ()
}

pub effect fn notifyLocalAgent() -> ()
! NativeSocketError | ByteIoError
? &mut MonotonicClock {
  return run connectUnix(
    b"/tmp/local-agent.sock",
    ConnectOptions.defaults(),
    Option.none<Instant>(),
    sendRequest,
  )
}

The callback receives a higher-ranked affine Connection; it cannot return the connection or keep its ByteDuplex loan. Structured success, typed failure, and cancellation all run terminal release. Use the owned constructors when another actor must retain or transfer the connection, and call Connection.close when that ownership ends. Acquisition failures and structured cancellation before publication close the descriptor once; publication transfers that responsibility to the caller. Fatal traps remain outside structured cleanup guarantees. Unix paths are copied during acquisition, must be absolute and NUL-free, and must fit the target sun_path including its terminator (104 bytes on Darwin, 108 on GNU). The transport never creates, removes, or owns the filesystem pathname.

Deadlines, polling, and throughput

ConnectOptions.defaults() retains Nagle, permits 64 counted attempts, and checks readiness every one millisecond. ConnectOptions.make admits intervals from 1 through 1,000,000,000 nanoseconds and attempt counts from 1 through 1024. connectResolved receives already-resolved numeric endpoints; DNS is deliberately outside its deadline. One supplied absolute deadline covers setup and every candidate without renewal. Option.none<Instant>() permits a pending operation to wait indefinitely, subject to the active MonotonicClock provider.

Readiness uses zero-time poll followed by a positive clock wait, so a local scheduler can run peer tasks and cancel its timer before the socket finalizer. The interval therefore trades latency for a bounded idle poll rate; this first provider is not an event reactor and does not promise high-connection-count throughput.

Transfer and error boundaries

Reads and writes acknowledge only an exact positive prefix. A zero-byte receive is sticky end of input; hangup does not discard already-readable bytes. flush is intentionally a no-op: a successful send means kernel acceptance, not peer delivery. shutdownWrite flushes that boundary, issues one directional shutdown, remains readable, and is idempotent.

Acquisition reports rich NativeSocketError values for invalid input, deadline arithmetic, timeout, attempt exhaustion, policy failures, and the responsible native operation/errno. Once installed as ByteDuplex, timeout, invalid count, and closure use the canonical ByteIoError variants; other native failures retain the exact ByteIoOperation and errno as Provider.code. A checked wake-time overflow during byte I/O uses provider code -1, because ByteIoError has no time-range variant.

On this page