Sign in

    tun-poc-server

    Download zip
    Version
    0.1.4
    License
    Apache-2.0
    Last updated
    5 months ago
    Downloads
    128

    #tun-poc-server

    The server agent for tun-poc. Runs on the machine you want to expose, opens a single outbound WebSocket to a relay, and spawns a PTY per incoming session so clients get a real shell.

    No inbound ports are required — it works behind home routers, corporate firewalls, and carrier-grade NATs.

    Responsibilities:

    • Register with the relay and accept sessions keyed by ServerId.
    • Fork a PTY per session (pty/, tty/) and stream I/O both ways.
    • Keep the local shell alive across relay disconnects and reattach on reconnect.
    • Resolve XDG paths for scoped credential storage (xdg/).

    #Run

    # connect to the hosted relay moon run server --target native # expose a specific program instead of the default shell moon run server --target native -- /bin/bash moon run server --target native -- -- htop # point at a self-hosted relay moon run server --target native -- --relay your.relay.example.com

    The agent prints the ServerId it registered under — keep the process running for the session to stay reachable.

    #Build And Publish Artifacts

    Server release binaries are built from the repo root:

    make build-server make build-server-linux

    The outputs are written under downloads/ as tun-server-<platform> files with .sha256 sidecars. The current manual publishing workflow bundles those files into the relay image. See ../docs/publishing.md for the current steps and ../docs/plans/2026-04-27-structured-publishing-workflow.md for the plan to publish server binaries independently.

    The server keeps a fixed 4 MiB PTY replay tail in memory for reconnect catch-up. That replay is a bounded byte suffix, not a terminal snapshot: once the tail trims, a fresh client or a client reconnecting with an older cursor can receive output from the middle of the PTY stream and cannot rely on replay alone to reconstruct the full screen state.

    #Terminal backend — virtual package

    The PTY is spawned at a fixed 80×24 grid (see bridge.mbt). The server's local stdout goes through a @terminal virtual package (server/terminal/) so the rendering backend stays a build-time choice, not a runtime one.

    The current default implementation is server/terminal/default.mbt: raw passthrough. PTY bytes are written straight to host stdout, host stdin is forwarded straight to the PTY, and shell queries (DSR/DA1/...) are handled by the host terminal's own stdin-to-PTY loop.

    #Adding another backend

    1. Create server/terminal_<name>/ with moon.pkg declaring options(implement: "tonyfettes/tun-poc-server/terminal") and any deps that backend needs.
    2. Provide pub fn new() -> Terminal raise, pub fn pty_to_host(Bytes, Terminal) -> Array[Bytes], pub fn host_to_pty(Bytes, Terminal) -> Bytes, pub fn host_setup(Terminal) -> Unit, and pub fn host_teardown(Terminal) -> Unit to match server/terminal/pkg.mbti.
    3. Add that package to overrides in server/moon.pkg to select it at build time.

    No change is needed in the rest of server/ library code. server/bridge.mbt and server/bridge_state.mbt already talk only to the virtual @terminal interface.

    GetcwdFailed

    pub suberror GetcwdFailed derive(
    Debug
    )

    Raised by getcwd when @env.current_dir() returns None. In practice a process without a valid cwd cannot run, so this is effectively unreachable — the suberror exists so callers can depend on a String rather than String?.

    BridgeState

    type BridgeState

    Process-lifetime mutable state that bridge tasks share. run_bridge constructs one and passes it to pty_reader, local_stdin_task, and run_retry_loop. Replaces the pty_ref + shell_exited_ref + log + next_id + send_queue_ref + clients tuple that used to be threaded around.

    current collapses send_queue_ref and the peer-level clients map — both were always set or cleared together, so a single Ref[RelaySession?] makes "a relay session is active" a single None/Some check. terminal holds whichever implementation the virtual @terminal package resolves to at link time.

    CliArgs

    type CliArgs

    Parsed command-line arguments. CliArgs::parse() returns None when help was printed or the user supplied a malformed --relay (the argparse / URL error is already logged — main should just return).

    ConnectConfig

    type ConnectConfig

    Immutable relay-identity bundle. Built once in main after the token is acquired and the uuid + hostname + cwd are known. Every function that talks to the relay threads this through instead of five separate params.

    CredentialStore

    type CredentialStore

    Credential file + device-flow facet. for_relay derives the on-disk path and the HTTP base URL from the relay WebSocket URL; acquire reads the file or runs the device flow (writing on success); remove_stale deletes the file after the relay has rejected the token with 401/403.

    RelaySession

    type RelaySession

    Per-WebSocket state. Exists only while a relay session is connected — BridgeState.current holds Some(self) during a session and None between sessions. Collapses today's peer-level send_queue_ref and clients refs into one struct so "connected or not" is a single check.

    ReplayEntry

    type ReplayEntry

    Each entry pairs the dense id with created (ms since epoch at append time) and the raw bytes. created is stored so replay frames carry the same timestamp as the live broadcast did — a reconnecting client can tell how stale catch-up output is instead of guessing.

    ReplayLog

    type ReplayLog

    getcwd

    fn getcwd() -> String raise GetcwdFailed

    Returns the server process's current working directory. Thin wrapper around @env.current_dir(), which reads the kernel's cwd via POSIX getcwd(2). The wrapper exists so callers can depend on a String rather than String?.