Documentation
¶
Overview ¶
Package bridge implements the PTY multiplexing between the in-container tmux session and N connected API clients.
In v1 the bridge is a thin wrapper over container primitives: tmux inside, docker exec / API for PTY plumbing outside. The public API contract is the stable surface; the bridge implementation is replaceable.
Index ¶
- func HostMaxClientSize(ctx context.Context, tmuxSession string) (rows, cols uint16, ok bool)
- func MaxClientSize(ctx context.Context, dockerBin, container, tmuxSession string) (rows, cols uint16, ok bool)
- type PTYSession
- func AttachToHostTmux(ctx context.Context, tmuxSession string, rows, cols uint16, te TermEnv) (*PTYSession, error)
- func AttachToTmux(ctx context.Context, dockerBin, container, tmuxSession string, ...) (*PTYSession, error)
- func ExecPTY(ctx context.Context, dockerBin, container string, cmd []string, ...) (*PTYSession, error)
- func HostPTY(ctx context.Context, cmd []string, rows, cols uint16, te TermEnv) (*PTYSession, error)
- type TermEnv
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func HostMaxClientSize ¶
HostMaxClientSize is MaxClientSize for a tmux session on the daemon host.
func MaxClientSize ¶
func MaxClientSize(ctx context.Context, dockerBin, container, tmuxSession string) (rows, cols uint16, ok bool)
MaxClientSize returns the largest size (per axis) among the tmux clients already attached to the session, querying via a throwaway `docker exec`. It's used to size a *sizeless* attach (a client that sent no resize — automation, a status poller) so it matches the real interactive client instead of coming up at creack/pty's 0x0 default. A 0x0 client, under `window-size latest`, becomes the "latest" client and collapses the shared window to tmux's 80x24 fallback — which is exactly the resize bug. Matching the largest existing client makes a sizeless attach harmless (it can't shrink the window) and even pulls the window toward the real client's dimensions.
ok is false when there are no attached clients yet (the very first connect) or the query fails; callers should then fall back to the PTY default.
Types ¶
type PTYSession ¶
type PTYSession struct {
// contains filtered or unexported fields
}
PTYSession is one PTY-backed `docker exec` attached to the in-container tmux session. Each connected client gets its own PTYSession; tmux's native multi- attach gives them a shared screen.
func AttachToHostTmux ¶
func AttachToHostTmux(ctx context.Context, tmuxSession string, rows, cols uint16, te TermEnv) (*PTYSession, error)
AttachToHostTmux attaches (creating if absent) to a tmux session on the daemon host — the operator host-terminal equivalent of AttachToTmux. te carries the client's terminal identity; see TermEnv.
func AttachToTmux ¶
func AttachToTmux(ctx context.Context, dockerBin, container, tmuxSession string, rows, cols uint16, te TermEnv) (*PTYSession, error)
AttachToTmux starts `docker exec -it <container> tmux attach-session -t <session>` against a host PTY and returns the session. Caller should Copy() to bridge bytes between the PTY and a client transport (e.g., a websocket), and Close() when the client disconnects.
rows/cols, when non-zero, size the PTY at creation. This matters: without an initial size the docker exec PTY (and the tmux client that runs in it) come up at creack/pty's 80x24 default, the agent renders its TUI at that size, and the SIGWINCH that arrives from the client's first resize envelope races the agent's initial render. Sizing up-front eliminates the race.
te carries the client's terminal identity; see TermEnv.
func ExecPTY ¶
func ExecPTY(ctx context.Context, dockerBin, container string, cmd []string, rows, cols uint16, te TermEnv) (*PTYSession, error)
ExecPTY starts `docker exec -it <container> <cmd...>` against a host PTY and returns the session, sized to rows/cols when both are non-zero. It generalizes AttachToTmux for the SSH façade, which bridges an SSH session channel to an arbitrary in-container command (a login shell, or `bash -lc <exec>`).
func HostPTY ¶
HostPTY starts cmd directly on the daemon host against a PTY — the operator host-terminal path (no container). Mirrors ExecPTY without the docker wrapper. This runs UNCONTAINED on the daemon host; it is gated to operators only and must never be reachable by an island token (see internal/api/tokenauth.go).
func (*PTYSession) Close ¶
func (s *PTYSession) Close() error
Close terminates the underlying docker exec and releases the PTY.
func (*PTYSession) Read ¶
func (s *PTYSession) Read(p []byte) (int, error)
Read reads from the PTY (container output → client).
func (*PTYSession) Resize ¶
func (s *PTYSession) Resize(rows, cols uint16) error
Resize tells the PTY about a new window size.
func (*PTYSession) Wait ¶
func (s *PTYSession) Wait() int
Wait reaps the underlying `docker exec` and returns its exit code. Call it after the PTY hits EOF (the in-container process exited). A signal/abnormal exit reports 1. Safe on a nil session.
type TermEnv ¶ added in v0.8.62
type TermEnv struct {
Term string // client's $TERM, e.g. "xterm-256color"
ColorTerm string // client's $COLORTERM, e.g. "truecolor"
}
TermEnv carries the CLIENT terminal's identity into the container, so the in-island tmux can decide what the OUTER terminal actually supports.
Without this the island sees only the DAEMON's TERM (docker exec propagates the docker CLI's environment, not the end user's), which is identical for every connected client and says nothing about any of them. That is why image/tmux.conf's capability lines had drifted to matching `,*:` — there was no per-client signal to match on — and why a terminal that genuinely can't handle RGB/sync/extkeys was being told to emit them anyway.
Both fields are best-effort and may be empty (automation, a non-TTY client, or an older dejima client that doesn't send them); empty means "say nothing", which leaves the container's own defaults in place.