Generated every 4 hours from main · rolling 7d · raw metrics
An intelligent workspace for continuous work across devices, environments, and compute nodes.
Nession connects local machines, remote servers, cloud environments, terminals, coding agents, and other execution contexts into one continuous working experience. The infrastructure may be distributed and complex; the product should remain coherent, quiet, and extensible.
Product direction and design decisions are governed by two repository-level documents:
VISION.md— what problem Nession exists to solve and where the product is going.PRINCIPLE.md— the durable design principles used to decide how Nession should grow.
In short: Nession should absorb infrastructure complexity instead of exposing it, organize the experience around the work rather than a feature catalog, let relevant capabilities emerge from context, and reveal deeper complexity progressively. The two documents above are the canonical source when this summary and lower-level design documents diverge.
Today, Nession provides distributed tmux session management across many machines. A central server tracks every agent (one per node), and clients attach to live sessions from the Web UI or CLI — over a relay through the server, or peer-to-peer directly to an agent for lower latency.
Browser / CLI
│ ws:// (or wss://)
▼
nession-server ── SQLite ── registry (agents, sessions)
│ ws:// ▲
▼ │ heartbeat + session sync
nession-agent ── tmux ─────┘
│
▼
tmux sessions (per-node)
Connection modes
- Relay — Browser/CLI → Server → Agent. Works anywhere the server is reachable.
- P2P — Browser/CLI → Agent directly, using the agent address returned by the attach response. Lower latency.
- Central registry — agents register on connect and heartbeat; the server persists agents and sessions in SQLite.
- Web dashboard — React + xterm.js UI to browse agents, create/kill sessions, and open a live terminal (light, on the same ground as the chrome).
- Interactive CLI — attach to a session in a raw terminal, list agents/sessions, create and kill sessions.
- Two transports — relay through the server or P2P directly to the agent.
- Container-ready — multi-arch (amd64/arm64) Docker images and Kubernetes manifests (kustomize base + staging/production overlays).
- Prebuilt binaries — released for Linux and macOS on every version bump.
Downloads the release binaries matching your OS and architecture:
curl -fsSL https://github.com/BestNathan/nession/releases/latest/download/install.sh | shOptions:
# specific version, custom dir, subset of binaries
./scripts/install.sh --version 0.3.8 --dir ~/.local/bin --bins nession
# -v, --version <ver> version to install (default: latest)
# -d, --dir <path> install dir (default: /usr/local/bin → ~/.local/bin fallback)
# -b, --bins <list> comma-separated: nession,nession-agent,nession-serverEnvironment overrides: NESSION_VERSION, NESSION_INSTALL_DIR, NESSION_BINS, and GITHUB_TOKEN (to avoid API rate limits).
Requires Rust 1.96+ (pinned in rust-toolchain.toml) and Node.js 20+ for the web UI.
git clone https://github.com/BestNathan/nession.git
cd nession
cargo build --release # binaries in target/release/Produces three binaries: nession (CLI), nession-agent, nession-server.
nession-server # reads ./config.toml if present, else uses defaultsWithout a config it listens on 127.0.0.1:8080 with no auth and stores its DB under ~/.nession/. The Vite dev server proxies /ws to 19090, not 8080 — so a config-less server is reachable by the CLI but not by the UI. Either pass a config that sets listen_address = "127.0.0.1:19090", or expect to log in and see "disconnected" with no other clue.
To customize, create a config.toml:
listen_address = "0.0.0.0:8080"
tls_cert_path = "" # set both to enable wss://
tls_key_path = ""
auth_token = "your-secret-token"
heartbeat_interval_secs = 10
heartbeat_timeout_secs = 30
# db_path defaults to ~/.nession/server/nession.dbCopy and edit agent-config.toml:
agent_id = "agent-local"
server_url = "ws://127.0.0.1:8080/ws"
auth_token = "your-secret-token"
listen_address = "0.0.0.0:8080" # internal WS server for P2P
heartbeat_interval_secs = 10
session_poll_interval_secs = 5
# advertise_address / connect_url — optional, for how clients reach this agent (P2P)nession agent start --config agent-config.toml --foregroundThe nession CLI can also manage the agent/server lifecycle as background processes:
nession agent start # background, writes a PID file under ~/.nession/
nession agent status
nession agent stop
nession server start|status|stopCLI (targets the central server; override with --server-url / --auth-token or NENSION_SERVER_URL / NESSION_AUTH_TOKEN):
nession agents list
nession sessions list [--agent-id <id>]
nession sessions create --agent-id <id> --name dev [--width 120 --height 40]
nession sessions attach --session-id <agent_id>:<session_name> [--mode p2p|relay]
nession sessions kill --session-id <agent_id>:<session_name> [--force]Web UI — see Web dashboard below.
cd web
npm install
npm run dev # Vite dev server on http://localhost:13000, proxies /ws → :19090Open http://localhost:13000, connect to the server, then browse agents and open terminals. For production, npm run build emits static assets to web/dist/ (served by nginx in the Docker/K8s images).
The web terminal supports zoom controls for better readability on different devices:
- Auto-scaling: Terminal automatically scales based on device type (mobile: 60%, tablet: 80%, desktop: 100%)
- Manual zoom: Use the +/- buttons in the terminal toolbar to adjust zoom level (30%-300%)
- Reset: Click the reset button to restore default zoom for your device
- Scrolling: When terminal size exceeds viewport, use scrollbars or touch gestures to navigate
Zoom level is session-specific and resets on page refresh. A Session's history is
xterm's own scrollback — the wheel stays in the browser and never enters tmux
copy mode — and attaching (or reloading) fills it from the Session, so the
context is there the moment the terminal is. That holds on the default attach
transport; attach_mode = "plain" is a fallback, and
docs/design/terminal/scrollback-bootstrap.md
states exactly what it does and does not give you.
nession/
├── crates/ # Rust workspace (5 crates)
│ ├── nession-common/ # shared protocol, config, paths, errors
│ ├── nession-server/ # broker, registry, SQLite persistence, WS server + TLS
│ ├── nession-agent/ # per-node agent: tmux management, server connection, P2P server
│ ├── nession-cli/ # CLI: attach, list, create/kill, lifecycle
│ └── nession-claude-code/# Claude Code config browser extension
├── web/ # React + Vite + TypeScript + shadcn/ui + xterm.js
├── deploy/ # docker-compose + entrypoints + nginx template
├── design/ # design tokens + executable UI contracts (`design/generated/` is codegen output)
├── e2e/ # Playwright specs + canonical visual baselines
├── scripts/ # gates, coverage, gitops writer, install.sh
├── Dockerfile.* # server/agent/ui build variants
└── Cargo.toml # workspace root (version lives here, and in web/package.json)
Deployment desired state — the kustomize base, per-environment overlays and the
ArgoCD app-of-apps — lives on the gitops orphan branch, not on main
(issue #592). See "Deploying to Kubernetes" in the root CLAUDE.md.
# Rust
cargo build
cargo test
cargo fmt --all -- --check
cargo clippy -- -D warnings # must pass with 0 warnings; #[allow(clippy::*)] is forbidden
# Web (in web/)
npm run dev # dev server :13000
npm run build # production build → dist/
npm run lint # ESLint (--max-warnings 0; eslint-disable is forbidden)
npm test # Vitest
npx tsc --noEmit # type checkSee CLAUDE.md for the full contributor workflow, coding conventions, and branch/PR rules.
Never develop on
main. Create a feature branch:git checkout -b feat/<slug>.
# full builds (Rust + UI)
docker build -f Dockerfile.server -t nession-server .
docker build -f Dockerfile.agent -t nession-agent .Published multi-arch images (on version bumps via CI):
ghcr.io/bestnathan/nession:server-<version>ghcr.io/bestnathan/nession:agent-<version>ghcr.io/bestnathan/nession:ui-<web-version>
deploy/docker-compose.yml wires server + agent + UI together for local runs.
Kustomize overlays under k8s/:
kubectl apply -k k8s/overlays/production # or overlays/staging| Service | Port | Purpose |
|---|---|---|
| nession-server | 19090 | WebSocket (agents + clients) |
| nession-agent | 19090 | WebSocket (P2P terminal) |
| nession-ui | 80 | nginx serving web/dist/ |
10080 is the container's nginx, not a Rust listener: neither binary opens
an HTTP port. nginx serves /health and the UI and proxies /ws to 19090.
CI publishes multi-arch images and updates the production overlay's image tags automatically on every version change (see .github/workflows/release.yml).
Pushing a version bump (Cargo.toml / web/package.json) to main triggers CI to:
- Lint + test (Rust and web).
- Build & push multi-arch Docker images.
- Build native binaries for Linux (amd64/arm64) and macOS (Intel/Apple Silicon).
- Publish a GitHub Release
v<version>with tarballs namednession-<version>-<os>-<arch>.tar.gz, each containingnession,nession-agent, andnession-server.
The installer consumes these release assets.