share: publish a running tmux session as a URL - #12
Conversation
Allow a listener to map access codes to control or view roles and advertise the granted role during session setup. Restricted shell handlers now pin argv, environment, and cwd, while view-only handlers reject input at the transport boundary. Make connection teardown reliable before a data channel opens, bound incomplete handshakes, and return useful client errors when verification ends early. Ephemeral URL flags keep short-lived share credentials out of the device table. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Add target discovery, role-specific tmux attach commands, ephemeral credentials, peer admission, TTL and source-session watches, and credential-protected state files. Run each peer through its own shell handler and delivery goroutine. Lifecycle probes use successful server-wide tmux listings, and target locks keep cleanup from racing a replacement share. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Add share, status, stop, and rotate commands with per-target locking, detached tmux supervision, exact target matching, startup diagnostics, and bounded cleanup of stranded state. Hosting requires tmux 3.2 or newer on Unix or WSL. Other platforms retain client support and report that hosting is unavailable. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Add share usage, security boundaries, lifecycle behavior, platform requirements, and command flags to the README and CLI help. Install tmux in Linux CI so the real integration suite runs, and include command packages in the local unit-test script.
main gained TCP forwarding (connect -L, streamtype/tcp) and refactored connect's flag parsing into connectOptions; the only textual conflict was cmd/bitbang/connect.go. Resolution keeps both features: - ephemeral-URL detection and the -name skip move onto opts.name - the view-only mode switch adapts to the renamed shellOpts - the forwarder-only path is preserved before shell setup One semantic conflict: main's e2e harness wired handler teardown through the removed Connection.OnClose field; it now uses SetOnClose and closes both CloseAll() and Close() handlers, mirroring serve.go.
|
Thanks @kodareef5 -- this is a chunk of work and is carefully built. I went The finding: view-only peers can resize the controller's terminal
controlArgv := append(..., "attach-session", "-t", cfg.SessionID)
viewArgv := append(..., "attach-session", "-r", "-t", cfg.SessionID)tmux sizes a shared window from its attached clients, so a viewer's resize propagates to I could not verify this -- tmux is not installed on my machine, so this is read from Not a regression -- nothing behaved differently before, this is a new feature -- so it
If you take option 1, the doc comment on What I checked and found cleanListing this because "no findings" is not very informative on a PR this size.
Forcing env alongside argv, for the right reason. The comment nails it: pinned argv Slots reserved at authorization, not at terminal open. The comment explains that a
Also confirmed: the two codes are independently generated with distinctness enforced; Build, vet, and the full suite pass. Race detector clean on Roughly half this diff is tests, and |
|
Thanks for the detailed pass, the clean-bill section especially. I ran your two-client check on tmux 3.6a and it doesn't reproduce. What I did: attached a read-write control client at 200x50, recorded the window, then The reason -r does more than gate keystrokes now: ▎ tmux 3.2: attach -r ... set or toggle both flags together Since 3.2, -r is an alias for -f read-only,ignore-size, and ignore-size means "the This is already pinned in the integration tests, which is the easy bit to miss without Additionally, and the reason that test is scoped "WhileControlAttached": the 3.1 Net: I don't think it needs a code change. That ViewOnly "resize remains enabled" |
|
You're right and I was wrong. Thanks for running it properly. The control condition is what makes it convincing -- rerunning that same second client And the mechanism explains my error precisely: I was reasoning from pre-3.1 tmux, where I checked the version floor rather than take it on faith, since the whole argument rests The "unless all clients are read-only" clause is a good catch to have surfaced, and I agree Yes to both offers, please:
Neither blocks. Push them and I will merge. For what it is worth, this is the ideal outcome of a review finding: it was a real question |
Review of richlegrand#12 asked whether a view-only peer can resize the controller's terminal, since both roles attach to the same tmux session and tmux sizes a shared window from its attached clients. It cannot. `tmux attach -r` has been an alias for read-only,ignore-size since tmux 3.2 (the geometry half landed in 3.1), and ignore-size means the client does not affect the size of other clients. The >= 3.2 floor is enforced by share.CheckVersion, so -r always carries ignore-size for any tmux this can run against. Verified by hand on tmux 3.6a under window-size latest, smallest, and largest; a read-write client of the same small size does shrink the window, which confirms the check is live rather than dead. - Rewrite the ViewOnly doc comment to state the guarantee, name ignore-size and the version floor, and point at the tests, instead of reading as "we allow resize and rely on tmux." - Extend TestViewerCannotResizeWhileControlAttached to run under window-size smallest as well as latest; only latest was pinned in CI, and smallest is a common configuration. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
main gained SWSP v4 (per-stream flow control) and the proxy auth-ordering
fixes while this branch was open. Five conflicts, all where both sides
added something to the same place:
protocol/swsp.go Access type vs the v4 control messages -- both kept
session/control.go `access` vs `negotiated_version` on ready -- both kept
cmd/serve.go teardown lists merged: deadline.Done, sess.Close,
release, forget. deadline first so it cannot fire
during teardown; release stays after the session
closes, matching the order OnReady already used
client/session.go setNegotiatedVersion owns ServerVersion and the v2
default now, so the branch's manual assignment goes
and only ServerAccess is added alongside it
e2e_harness_test.go main assigned the OnClose field this branch replaced
with SetOnClose; moved to the new API, and
newSession.Close() now always runs rather than only
when closeHandlers is non-empty
One semantic fixup beyond the conflicts: access_test.go built a Session
with streamHandler and reasm, both of which v4 replaced with the
streams map, so it now matches what session_test.go constructs.
The PIN gate reordering from main survived the automatic merge -- handler
setup still happens in notifyHandlers after auth, and this branch's
Authorize hook is unaffected by it.
|
Thanks for the doc line and the I've pushed a merge of Green, and merging. Goes out in 0.5.0 alongside Windows and macOS support, TCP forwarding, |
share: publish a running tmux session as a URL
Closes #5.
Summary
bitbang sharepublishes a tmux session that is already in progress. Itreturns a control URL and a separate view-only URL, or only the view URL when
started with
--read-only.The command returns after publication, so suspending a task, sharing it, and
resuming it works without changing how the task was started.
Design
tmux attach-sessionprocess. Control peersattach read-write; viewers attach with
-r.also drops viewer stdin, signals, and EOF before tmux sees them.
maps each code to a control or view role without signaling-server changes.
--max-viewersslots.Reservations begin at authorization and last for the connection lifetime,
including peers that never open a terminal.
_bbshare_*tmux session supervises the worker. There is nodaemonization or PID file.
--ttl,share stop, source-session removal, or signalingpreemption. The source session is never stopped by share cleanup.
Share URLs carry
!ephemeralin the fragment. Browser parsing already ignoresunknown fragment flags, and CLI clients use the flag to avoid saving an expired
share credential to
devices.json.Lifecycle safety
The command changes no tmux options. It reads the effective
window-sizeandwarns when it is not
latest, but preserves the user's configuration.State contains bearer credentials and is written with mode 0600 inside a 0700
directory. A per-target advisory lock spans classification, cleanup, worker
creation, and state acceptance. Cleanup only acts after a successful
server-wide tmux listing proves the management pane is gone, or after the
socket proves that no server remains. Failed or ambiguous probes preserve
state.
Connection teardown now also covers peers whose data channel never opens, and
both normal listeners and share workers bound incomplete handshakes. This
prevents abandoned WebRTC requests from permanently consuming admission
slots.
Platform behavior
Hosting requires tmux 3.2 or newer on Unix or WSL. Native Windows clients can
open control and view URLs; attempting to host reports the platform limitation
before looking for tmux. The changes preserve the current ConPTY shell path and
compile as Windows test binaries.
Verification
go build ./...go vet ./...go test -count=1 ./...go test -race -count=1 ./...TERM=dumbPATHresize isolation, source loss, exact targeting, lifecycle locks, retained
dead panes, and stale-state cleanup
bitba.ngsmoke test: existing output rendered, view input was blocked,control input executed, share URLs were not persisted, status recovered the
same URLs, and stop left the source session running
Linux CI now installs tmux, so the tmux integration tests execute in pull
requests instead of silently skipping.
Review order
peer: add role-aware terminal accessshare: add the tmux publication workershare: add tmux session lifecycle commandsdocs: document tmux session sharing