dcipc

    Local daemon control channel for MoonBit: length-prefixed frames, token auth over loopback TCP, contact files in the state dir

    ipc
    daemon
    frame
    token
    tcp
    Download zip
    Author
    Version
    0.1.0
    License
    MIT
    Last updated
    6 hours ago
    Downloads
    1

    Dependencies

    #chensuiyi/dcipc

    Local daemon control channel · 本地守护进程控制通道

    本地守护进程控制通道:长度前缀帧协议、共享令牌认证(loopback TCP)、状态目录接触文件(port / token)。异步(async 运行时)。

    • 认证先行:token 帧后服务端回单字节确认,拒绝 = 直接断连——错误 token 呈现为 EOF 而非协议错误
    • 常量时间 token 比较,时序不泄露前缀
    • 帧上限 4MB;握手与静默客户端均有 5s 超时
    • 协议与传输解耦:Unix socket 等传输可作为后续扩展点
    • 依赖:moonbitlang/async + chensuiyi/fsx

    Control channel for local daemons: length-prefixed frames, shared-token authentication over loopback TCP, and contact files (port / token) in the state dir. Asynchronous (async runtime).

    • Auth first: the server acks the token frame with one byte; rejection closes the connection, so a wrong token surfaces as EOF rather than a protocol error
    • Constant-time token comparison — timing does not reveal a matching prefix
    • 4 MB frame ceiling; 5 s timeouts on the handshake and silent clients
    • Protocol is transport-agnostic: Unix sockets and other transports are future extension points
    • Depends on moonbitlang/async + chensuiyi/fsx

    #API

    接口 Interface说明 Description
    generate_token()内核 RNG 256-bit;256 bits from the kernel RNG
    dial(state_dir)按 port 文件连接;Connect via the port file
    accept_authed(server, token)接受并认证;Accept + authenticate
    round_trip_bytes(conn, token, payload, timeout_ms)认证往返;Authenticated round trip
    read_frame_timeout(conn, timeout_ms)读一帧(静默容错);Read one frame, silence-tolerant
    read_token / read_daemon_port接触文件;Contact files

    License: MIT

    IpcError

    pub suberror IpcError {
    Timeout
    AuthFailed
    Closed(String)
    TooLarge(size~ : Int, limit~ : Int)
    } derive(
    Debug
    )

    Control-channel failures. AuthFailed closes the connection without a response, so a wrong token looks to its presenter like a closed port.

    IpcError::to_repr

    AUTH_TIMEOUT_MS

    let AUTH_TIMEOUT_MS : Int

    How long either side waits for the auth handshake (and the daemon for a silent client) before dropping the connection.

    MAX_MSG_BYTES

    let MAX_MSG_BYTES : Int

    Hard ceiling for one control message. The receiver allocates exactly the announced length, so this is not a limit on how many instances may be listed — it only stops a hostile peer from forcing an absurd allocation.

    PORT_FILE

    let PORT_FILE : String

    Files under the state dir that publish the daemon's contact point.

    TOKEN_FILE

    let TOKEN_FILE : String

    accept_authed

    async fn accept_authed(server :
    TcpServer
    , token : String) ->
    Tcp
    ?

    Accept one connection and authenticate it. Returns the open connection when the peer presented the right token (the ack byte is already sent), None for wrong token / silence / garbage — those connections are closed here without ever reaching the dispatcher. Cancellation propagates so a shutting-down daemon stops accepting.

    dial

    async fn dial(state_dir : String) ->
    Tcp

    Dial the daemon over loopback TCP using the recorded port. Raises Closed when there is no readable port file (no daemon, or a stale one).

    generate_token

    fn generate_token() -> String raise

    256 bits from the kernel RNG, hex-encoded: 64 characters, no ambiguity on the wire or in a file.

    hex_encode

    fn hex_encode(bytes : Bytes) -> String

    read_daemon_port

    fn read_daemon_port(state_dir : String) -> Int?

    Read the port file the daemon wrote after binding. None when there is nothing readable (no daemon, or a stale file from a dead one).

    read_frame_timeout

    async fn read_frame_timeout(conn :
    Tcp
    , timeout_ms : Int) -> Bytes?

    Read one framed message from an authenticated connection, tolerating a client that connects and goes silent: None means the connection yielded nothing usable and has been closed.

    read_token

    fn read_token(state_dir : String) -> String?

    Read the shared token. None when missing/unreadable — callers report "no daemon" rather than leaking the reason.

    round_trip_bytes

    async fn round_trip_bytes(conn :
    Tcp
    , token : String, payload : Bytes, timeout_ms : Int) -> Bytes

    One authenticated round trip on a fresh connection, over raw bytes: the token frame goes first, the server answers with a single ack byte before the payload frame is accepted (a rejected token therefore surfaces as EOF — AuthFailed — rather than a protocol error), then the payload frame is written and the response frame read. The whole exchange is bounded by timeout_ms; silence raises Timeout.

    write_frame

    async fn write_frame(conn :
    Tcp
    , payload : Bytes) -> Unit

    4-byte big-endian length header + payload, over the connection.

    Source Files