운영 브리프
tmux-web-manager는 중앙 hub(main)와 여러 agent(sub)를 중심으로 동작하는 분산형 tmux 제어 도구입니다.
여러 머신의 tmux 세션과 pane을 한곳에서 모아 보여주고, xterm.js로 붙어서 조작하며, relay/orchestration API와 얇은 CLI까지 제공합니다.
- raw PTY 기반
tmux attach-sessionxterm.js 터미널 뷰 - 여러 backend 서버 등록 및 영속 저장
- 좌측 sidebar 기반 backend / session 관리
- backend, 작업 경로, optional session 이름으로 세션 생성
- HTTP + WebSocket 기반 backend API
- 기본적으로 host의 기본 tmux server를 사용하고, 필요하면 dedicated socket mode 사용 가능
main/sub실행 모드 지원
HOST:main모드 중앙 UI bind host, 기본값0.0.0.0PORT:main모드 중앙 UI 포트, 기본값8787BASE_URL: 중앙 UI public URL, 기본값http://localhost:8787DATA_DIR: 데이터 루트 디렉터리, 기본값~/.tmux-web-managerALLOWED_PROJECT_ROOTS: 허용할 절대 경로 root 목록(콤마 구분)BACKEND_HOST: backend bind host, 기본값0.0.0.0BACKEND_PORT: backend 포트, 기본값8788BACKEND_PUBLIC_URL: 중앙 서비스가 local backend에 접근할 때 사용할 base URLBACKEND_NAME: local backend 표시 이름BACKEND_AUTH_TOKEN: backend API / WebSocket용 bearer token. 지정하지 않으면 agent가 자동 생성해서 저장합니다.HUB_AUTH_USERNAME: 환경 변수로 미리 계정을 심을 때 사용할 bootstrap 사용자 ID, 기본값adminHUB_AUTH_PASSWORD: bootstrap 비밀번호. 비워두면 첫 실행 시 web UI가 onboarding 화면으로 전환되어 최초 ID/PW 생성을 요구합니다.HUB_API_TOKEN: hub 인증이 켜진 상태에서 CLI/자동화 접근에 사용할 API token. 비워두면 자동 생성되어 저장됩니다.HUB_SESSION_TTL_MS: hub 로그인 세션 수명(ms), 기본값43200000(12시간)HUB_SECURE_COOKIES:Secure쿠키 강제 여부 (true/false).BASE_URL이https://...면 기본적으로 활성화됩니다.TMUX_SOCKET_MODE:default또는dedicated, 기본값defaultTMUX_SOCKET_NAME:dedicated모드에서 사용할 tmux socket 이름SESSION_PREFIX: 자동 생성 세션 이름 prefixOH_MY_TMUX_CONF:dedicated모드 generated config에서 source할 oh-my-tmux config 경로
BASE_URL은 브라우저가 실제로 접속하는 외부 hub URL과 정확히 맞춰야 합니다. 예:https://tmux.example.com- HTTPS 뒤에서 운영할 때는
HUB_SECURE_COOKIES=true를 권장합니다. - TLS를 reverse proxy에서 종료한다면
X-Forwarded-Proto를 전달해 origin 검증과 secure cookie 기대값이 맞도록 해야 합니다. - 브라우저 세션 기반 write 요청은 origin + CSRF 검증을 통과해야 하므로, 같은 브라우저 세션 안에서는 loopback/LAN/public URL을 섞기보다 하나의 canonical hub origin만 쓰는 것이 좋습니다.
npm install
npm run build
npm run start:main기본적으로 HOST=0.0.0.0, BACKEND_HOST=0.0.0.0으로 bind되므로, loopback으로 제한하지 않는 한 LAN에서 접근할 수 있습니다.
기본 backend는 host의 기본 tmux server에 붙습니다. 예전처럼 격리된 동작이 필요하면 TMUX_SOCKET_MODE=dedicated를 사용하면 됩니다.
각 agent는 backend auth token이 반드시 필요합니다. BACKEND_AUTH_TOKEN이 없으면 agent가 자동으로 생성해서 다음 파일에 저장합니다.
$DATA_DIR/backend/agent-auth-tokenhub에서 backend를 등록할 때는 이 토큰을 사용해야 합니다.
hub는 이제 기본적으로 login-first 흐름을 사용합니다. 저장된 hub 인증 정보가 없는 새 설치에서는 첫 브라우저 접근 시 onboarding 화면이 열리고, 운영자가 최초 사용자 ID/PW를 만들게 됩니다. 무인 bootstrap이 필요하면 HUB_AUTH_USERNAME / HUB_AUTH_PASSWORD를 환경 변수로 미리 넣어둘 수 있습니다.
CLI/자동화용 hub API token은 다음 파일에서 확인할 수 있습니다:
$DATA_DIR/central/hub-api-tokenbackend 전용 실행:
npm run start:sub장시간 운영용 자동 재시작 실행:
nohup ./scripts/run-main-supervised.sh >/tmp/tmux-web-manager-supervised/nohup.out 2>&1 &더 안정적인 user-level 서비스가 필요하면 포함된 systemd unit을 사용할 수 있습니다.
mkdir -p ~/.config/systemd/user
cp ./scripts/systemd/tmux-web-manager.service ~/.config/systemd/user/
mkdir -p ~/.config/tmux-web-manager
cp ./scripts/systemd/tmux-web-manager.env.example ~/.config/tmux-web-manager/tmux-web-manager.env
# ~/.config/tmux-web-manager/tmux-web-manager.env 를 현재 서버 환경에 맞게 수정
systemctl --user daemon-reload
systemctl --user enable --now tmux-web-manager.serviceagent shell이나 tmux pane에서 바로 사용할 수 있는 얇은 CLI wrapper도 포함되어 있습니다.
이 wrapper는 agent host에서 실행되지만, 내부적으로는 hub relay API를 호출합니다.
예시:
npm run bridge -- panes
npm run bridge -- resolve server-b reviewer
npm run bridge -- read server-b reviewer 20
npm run bridge -- message server-b reviewer "Please review the failing test output."tmux 안에서 실행하면 $TMUX_PANE를 기본 source pane으로 활용할 수 있습니다.
유용한 환경 변수:
TWM_BASE_URL또는BASE_URLTWM_HUB_API_TOKEN또는HUB_API_TOKENTWM_SOURCE_BACKENDTWM_SOURCE_PANETWM_SOURCE_LABEL
예:
export TWM_BASE_URL=http://127.0.0.1:8787
export TWM_HUB_API_TOKEN=$(cat ~/.tmux-web-manager/central/hub-api-token)
export TWM_SOURCE_BACKEND=server-a
export TWM_SOURCE_PANE=%1
npm run bridge -- read server-b reviewer 20Docker 없이 standalone prefix에 설치:
cd tmux-web-manager
./scripts/install-native.sh \
--prefix "$HOME/.local/share/tmux-web-manager" \
--data-dir "$HOME/.local/state/tmux-web-manager" \
--allowed-root /workspace \
--tmux-socket-mode default \
--oh-my-tmux-conf "$HOME/.tmux.conf"생성물:
PREFIX/app/(dist/,node_modules/, package metadata 포함)PREFIX/etc/tmux-web-manager.envPREFIX/bin/run-main.shPREFIX/bin/run-sub.sh
설치 후 실행:
$HOME/.local/share/tmux-web-manager/bin/run-main.shdocker compose up --build기본 구성:
main:8787- local backend:
8788 - extra
subbackend:8790
이 프로젝트는 완전히 진공 상태에서 나온 것이 아니라, 몇 가지 오픈소스에서 인터랙션 방향과 웹 터미널 전달 방식, agent 지향 tmux 제어 아이디어를 많이 참고했습니다:
ShawnPana/smux— pane label/resolve,read-before-writeguard, agent-to-agent tmux 자동화 패턴에 큰 영향을 준 프로젝트입니다.tsl0922/ttyd— 가벼운 web terminal 제공 방식과 실용적인 브라우저 터미널 UX를 참고했습니다.sorenisanerd/gotty— terminal을 웹 애플리케이션처럼 노출하는 초기 아이디어에 참고가 되었습니다.butlerx/wetty— HTTP/HTTPS 기반 browser terminal UX와 remote terminal access 패턴을 참고했습니다.
이 프로젝트는 여기에 더해 hub/agent registry, tmux session + pane orchestration, relay logging, agent-local / hub-backed CLI를 한 시스템으로 결합하는 쪽으로 확장했습니다.
중앙 web server:
GET /api/stateGET /api/panesGET /api/orchestration/panesGET /api/orchestration/panes/resolve?backendName=...&label=...POST /api/backendsPUT /api/backends/:idDELETE /api/backends/:idPOST /api/sessionsPUT /api/sessions/:backendId/:sessionIdDELETE /api/sessions/:backendId/:sessionIdPOST /api/relay/send-textPOST /api/relay/send-text-no-enterPOST /api/relay/send-keysPOST /api/relay/messagePOST /api/relay/readPOST /api/relay/panes/readPOST /api/relay/panes/send-textPOST /api/relay/panes/send-text-no-enterPOST /api/relay/panes/send-keysPOST /api/relay/panes/messagePOST /api/relay/panes/labelWS /ws/terminal?backendId=...&sessionId=...
Relay 사용 예시:
curl -X POST http://127.0.0.1:8787/api/relay/send-text \
-H 'content-type: application/json' \
-d '{
"sourceBackendName": "server-a",
"sourceSessionName": "source-session",
"targetBackendName": "server-b",
"targetSessionName": "target-session",
"text": "echo hello"
}'relay 감사 로그는 다음 파일에 기록됩니다:
$DATA_DIR/central/relay-log.jsonlrelay 요청은 backend/session 이름만 사용하므로 감사 로그에서도 source/target을 사람이 읽기 쉬운 형태로 남길 수 있습니다.
Pane orchestration discovery 예시:
curl http://127.0.0.1:8787/api/orchestration/panes이 응답에는 다음이 포함됩니다:
targetIdFormat: "backendName/paneId"readBeforeWrite- relay endpoint 안내
backendName,paneId,sessionName,location,label,currentCommand,currentPath가 들어간 pane summary
Pane resolve 예시:
curl "http://127.0.0.1:8787/api/orchestration/panes/resolve?backendName=server-b&label=reviewer"Pane relay read 예시:
curl -X POST http://127.0.0.1:8787/api/relay/panes/read \
-H 'content-type: application/json' \
-d '{
"sourceBackendName": "server-a",
"sourcePaneId": "%1",
"targetBackendName": "server-b",
"targetLabel": "reviewer",
"lines": 20
}'Pane relay message 예시:
curl -X POST http://127.0.0.1:8787/api/relay/panes/message \
-H 'content-type: application/json' \
-d '{
"sourceBackendName": "server-a",
"sourcePaneId": "%1",
"targetBackendName": "server-b",
"targetLabel": "reviewer",
"text": "Please review the failing test output."
}'Pane label 설정 예시:
curl -X POST http://127.0.0.1:8787/api/relay/panes/label \
-H 'content-type: application/json' \
-d '{
"sourceBackendName": "server-a",
"sourcePaneId": "%1",
"targetBackendName": "server-b",
"targetPaneId": "%12",
"label": "reviewer"
}'명시적인 pane label이 없으면 session 이름 기반 숫자 suffix 형식(build-1, build-2)으로 자동 label이 생성되며, 이 자동 label도 discovery/resolve에서 그대로 사용할 수 있습니다.
tmux backend server:
GET /api/healthGET /api/sessionsGET /api/panesGET /api/panes/resolve/:labelPOST /api/sessionsPOST /api/sessions/by-name/:sessionName/send-textPOST /api/sessions/by-name/:sessionName/send-text-no-enterPOST /api/sessions/by-name/:sessionName/send-keysPOST /api/sessions/by-name/:sessionName/messageGET /api/sessions/by-name/:sessionName/readPOST /api/panes/by-id/:paneId/labelPOST /api/panes/by-id/:paneId/send-textPOST /api/panes/by-id/:paneId/send-text-no-enterPOST /api/panes/by-id/:paneId/send-keysPOST /api/panes/by-id/:paneId/messageGET /api/panes/by-id/:paneId/readPUT /api/sessions/:idDELETE /api/sessions/:idWS /ws/sessions/:id
- native install은 host에
node와tmux가 있어야 합니다. dedicated모드에서는 generated tmux config가OH_MY_TMUX_CONF를 source하고mouse on을 강제합니다.