These libraries are experimental. APIs may change without notice. Generated from source with koruc 0.1.7 on 9/16/2026.
Udp
@korulang/udp@0.0.1Nonblocking UDP datagrams for Koru with phantom socket obligations
udp/index.kz · 14 tors
@korulang/udp — nonblocking UDP datagrams for Koru (v0: IPv4, posix sockets) · 21 more lines
@korulang/udp — nonblocking UDP datagrams for Koru (v0: IPv4, posix sockets)
Raw datagrams behind phantom obligations. The footguns this compiles away:
1. There is no unbound-socket state AT ALL. `bind` is the only mint: a
*Socket exists only once it owns a port, which is the earliest a UDP
socket can do anything anyway (even sendto wants a local port).
2. `close` consumes <!bound> — send-after-close is not expressible, and a
forgotten close is auto-healed or a KORU030, same as raylib's window.
3. `packets` drains the nonblocking fd up to `max` datagrams, firing
`! packet` once each. The *Packet<packet!> handle is a scoped borrow:
it aliases the shared recv buffer, valid only inside the branch body
(same shape as raylib's *Frame<frame>, which the engine brackets).
4. packet.key folds (ipv4, port) into an i64 session key — the column a
server keys per-client state on. send.key consumes the same fold, so
a client row needs one column, not three.
The socket is nonblocking by construction (O_NONBLOCK at bind): recv hits
WouldBlock, which the drain reads as | done. This is the poll-shaped v0 —
the deadline/evented recv is the named gap (kqueue exists over in Orisha's
pump and wants porting into a socket-source tor someday).
Phantom lifecycles
Derived from the phantom labels in the declarations below — state! issues an
obligation the compiler will chase, !state discharges it, a bare state holds it without moving it. Nothing here is hand-drawn.
Socket 1 state bound!Packet 1 state packet!// Lifecycle — the symmetric model: bound -> closed.
//
// Bind an IPv4 datagram socket on 0.0.0.0:port, nonblocking. port 0 asks the
// OS for an ephemeral port — read it back with socket.port.
~pub tor bind { port: u16 }
| bound *Socket<bound!>
| err string// The ephemeral port the OS picked (getsockname) — for bind(port: 0).
~pub tor socket.port { sock: *Socket<bound> } -> u16// SO_RCVBUF — the kernel's per-socket backlog. A server that drains once
// per tick lives or dies by this number: bursts deeper than the buffer are
// discarded by the kernel before recvfrom ever sees them (netstat's
// "dropped due to full socket buffers" counter). The granted size may be
// clamped below the request (kern.ipc.maxsockbuf) — read it back with
// socket.rcvsize and log what you actually got.
~pub tor socket.rcvbuf { sock: *Socket<bound>, bytes: i32 }
| ok
| err string// The granted receive-buffer size (getsockopt) — what rcvbuf actually got.
~pub tor socket.rcvsize { sock: *Socket<bound> } -> i32// Branchless: a datagram close cannot meaningfully fail, and a `| err` arm
// here would only fork the obligation graph (a second socket's close nested
// behind it leaves the first undisposed — measured, KORU030).
~pub tor close { sock: *Socket<!bound> }// Receiving — the drain. `packets` is the source combinator (the `! packet`
// sibling of raylib's `! frame`): fire once per datagram currently queued,
// stop at `max` or at WouldBlock, whichever first.
~pub tor packets { sock: *Socket<bound>, max: i32 }
! packet *Packet<packet!>
| done
| err string// The datagram's payload as a byte string — valid only inside the branch.
~pub tor packet.data { p: *Packet<packet> } -> string// Session key: addr << 16 | port. One i64 column keys a clients store.
~pub tor packet.key { p: *Packet<packet> } -> i64// Sender's ipv4 + port, host order — for logging/dedup, not for send.to
// (which wants them; send.key wants the fold instead).
~pub tor packet.addr { p: *Packet<packet> } -> u32~pub tor packet.port { p: *Packet<packet> } -> u16// No-op consumer of the per-packet borrow — auto-inserted at the branch
// boundary, user never calls it. Mirror of raylib's release.frame.
~pub tor release.packet { p: *Packet<!packet> }// Sending — three spellings of the destination: dotted ip for clients dialing
// a server, addr+port for rows that stored them, key for rows that stored the
// packet.key fold.
~pub tor send.ip { sock: *Socket<bound>, ip: string, port: u16, data: string }
| sent
| err string~pub tor send.to { sock: *Socket<bound>, addr: u32, port: u16, data: string }
| sent
| err string~pub tor send.key { sock: *Socket<bound>, key: i64, data: string }
| sent
| err string