registry  /  @fny/joy-tmux  /  1.5.0

@fny/joy-tmux@1.5.0

joy-tmux daemon + CLI — run Claude Code sessions in tmux, bridged to the joy/Happy relay.

AI Security Review

scanned 23h ago · by lpm-firewall-ai

Review flagged AI-agent configuration or capability changes. This remains warn-only unless evidence shows foreign-agent hijack through preinstall/install/postinstall, hidden persistence, exfiltration, remote code execution, or other concrete malicious behavior.

Static reason
One or more suspicious static signals were detected.; previous stored version diff introduced dangerous source
Trigger
User runs joy start/install/new/run or uses the Joy/Happy relay to control sessions
Impact
Authenticated clients can run commands and read/write files within configured session workspaces; explicit install can persist the daemon as a user service.
Mechanism
Authenticated local daemon and relay RPCs drive Claude/tmux, shell, file, and persistence operations
Policy narrative
No hidden npm install hook or import-time payload was found. The main risk is deliberate product functionality: a user-started daemon connects to a Joy/Happy relay, starts Claude in tmux, often with bypass permissions, and exposes authenticated file/shell/session operations plus optional user-service persistence.
Rationale
Source inspection supports a warning for dangerous agent automation capability, but not a publish block: activation is explicit, routes are token-gated, file access is workspace-scoped, and the Claude hook/settings files are package-owned rather than unconsented foreign control-surface mutation. Scanner persistence and shell signals align with the CLI daemon product behavior.
Evidence
package.jsonbin/joy.mjssrc/cli.tssrc/server.tssrc/domain/registry.tssrc/domain/operations.tssrc/domain/fileOps.tssrc/relay/relay.tssrc/transports/http.tssrc/claude/hooks.tssrc/domain/commands.ts~/.happy/access.key~/.happy/settings.json~/.happy/joy-tmux-state/daemon.json~/.happy/joy-tmux-state/daemon.log~/.happy/joy-tmux-state/claude-settings.json~/.happy/joy-tmux-state/joy-hook.mjs~/.config/systemd/user/joy-tmux.service~/Library/LaunchAgents/vip.voltai.joy-tmux.plist~/.claude/commands~/.claude/skills~/.claude/plugins<session cwd>
Network endpoints2
api.cluster-fluster.com127.0.0.1:<PORT>

Decision evidence

public snapshot
AI called this Suspicious at 88.0% confidence as Dangerous Capability with medium false-positive risk.
Evidence for warning
  • src/domain/registry.ts launches Claude in tmux and defaults sessions to --dangerously-skip-permissions unless opted out
  • src/domain/operations.ts exposes authenticated session RPC/HTTP ops for bash, readFile, writeFile, sendKeys, mode changes, and session creation
  • src/relay/relay.ts connects to https://api.cluster-fluster.com using ~/.happy/access.key credentials and registers machine/session RPC handlers
  • src/cli.ts user-invoked install writes systemd/launchd service files for a persistent joy-tmux daemon
Evidence against
  • package.json has no npm lifecycle hooks, so no install-time execution
  • Persistence is behind explicit joy install CLI command, not npm install/import
  • src/transports/http.ts binds localhost and requires X-Joy-Token for POST/DELETE routes
  • src/domain/fileOps.ts validates file paths to session cwd plus scoped per-session media root
  • src/claude/hooks.ts writes package-owned hook/settings files under ~/.happy/joy-tmux-state and passes them via --settings for joy-managed Claude sessions
  • No credential harvesting or arbitrary exfiltration beyond package-aligned Happy/Joy relay credentials and encrypted relay traffic
Behavioral surface
Source
ChildProcessCryptoDynamicRequireEnvironmentVarsFilesystemNetworkShellWebSocket
Supply chain
HighEntropyStringsUrlStrings
Manifest
NoLicense
scanned 40 file(s), 547 KB of source, external domains: 0.0.0.0, 127.0.0.1, api.cluster-fluster.com, claude.com, example.com, exp.host, github.com, www.apple.com

Source & flagged code

7 flagged · loading source
src/cli.tsView file
10import { homedir, platform as osPlatform } from "os"; L11: import { spawn, spawnSync } from "child_process"; L12: import { moduleDir } from "./esm";
High
Child Process

Package source references child process execution.

src/cli.tsView on unpkg · L10
10import { homedir, platform as osPlatform } from "os"; L11: import { spawn, spawnSync } from "child_process"; L12: import { moduleDir } from "./esm"; ... L14: L15: const PORT = parseInt(process.env.PORT ?? "4997"); L16: const BASE = `http://127.0.0.1:${PORT}`; L17: const HAPPY_HOME = happyHomeDir();
High
Same File Env Network Execution

A single source file combines environment access, network access, and code or shell execution; review context before blocking.

src/cli.tsView on unpkg · L10
10import { homedir, platform as osPlatform } from "os"; L11: import { spawn, spawnSync } from "child_process"; L12: import { moduleDir } from "./esm"; ... L22: // store (…/.pnpm/@fny+joy-tmux@1.0.15_…/node_modules/@fny/joy-tmux). Baking THAT into a L23: // launchd/systemd service breaks on the next `pnpm add -g`: pnpm makes a fresh store dir L24: // for the new version and deletes the old one, so the service's server.ts path vanishes
High
Runtime Package Install

Package source invokes a package manager install command at runtime.

src/cli.tsView on unpkg · L10
10import { homedir, platform as osPlatform } from "os"; L11: import { spawn, spawnSync } from "child_process"; L12: import { moduleDir } from "./esm"; ... L14: L15: const PORT = parseInt(process.env.PORT ?? "4997"); L16: const BASE = `http://127.0.0.1:${PORT}`; L17: const HAPPY_HOME = happyHomeDir(); L18: const STATE_DIR = joyStateDir(); ... L47: function readState(): DaemonState | null { L48: try { return JSON.parse(readFileSync(STATE_FILE, "utf8")) as DaemonState; } catch { return null; } L49: } ... L61: headers: authHeaders(),
Medium
Install Persistence

Source writes installer persistence such as shell profile or service configuration.

src/cli.tsView on unpkg · L10
src/domain/operations.tsView file
48// like `--import tsx` + the script path) so the replacement runs the same way. L49: const argv = [process.execPath, ...process.execArgv, ...process.argv.slice(1)]; L50: const cmd = argv.map(a => `'${a.replace(/'/g, "'\\''")}'`).join(" ");
High
Shell

Package source references shell execution.

src/domain/operations.tsView on unpkg · L48
bin/joy.mjsView file
15L16: await import(new URL('../src/cli.ts', import.meta.url).href);
Medium
Dynamic Require

Package source references dynamic require/import behavior.

bin/joy.mjsView on unpkg · L15
src/domain/fileOps.tsView file
matchType = previous_version_dangerous_delta matchedPackage = @fny/joy-tmux@1.3.0 matchedIdentity = npm:QGZueS9qb3ktdG11eA:1.3.0 similarity = 0.744 summary = stored previous version shares package body but lacks this dangerous source file
Critical
Previous Version Dangerous Delta

This package version adds a dangerous source file absent from the previous stored version; route for source-aware review.

src/domain/fileOps.tsView on unpkg

Findings

1 Critical4 High5 Medium5 Low
CriticalPrevious Version Dangerous Deltasrc/domain/fileOps.ts
HighChild Processsrc/cli.ts
HighShellsrc/domain/operations.ts
HighSame File Env Network Executionsrc/cli.ts
HighRuntime Package Installsrc/cli.ts
MediumDynamic Requirebin/joy.mjs
MediumNetwork
MediumEnvironment Vars
MediumInstall Persistencesrc/cli.ts
MediumStructural Risk Force Deep Review
LowScripts Present
LowFilesystem
LowHigh Entropy Strings
LowUrl Strings
LowNo License