Skip to content

feat: opt-in interactive bash + one-shot installer (v1.1.0)#11

Merged
sebyx07 merged 3 commits into
mainfrom
feat/interactive-shell-and-installer
Jun 27, 2026
Merged

feat: opt-in interactive bash + one-shot installer (v1.1.0)#11
sebyx07 merged 3 commits into
mainfrom
feat/interactive-shell-and-installer

Conversation

@sebyx07

@sebyx07 sebyx07 commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

What

  • bash gains an interactive param (like bg). Default stays the fast bare sh -c; interactive=true runs through bash -ic, sourcing the service user's ~/.bashrc so aliases and version managers (mise/nvm/rbenv) resolve. Startup job-control warnings (no controlling TTY under systemd) are discarded by redirecting the child's stdio to /dev/null and having the command re-point stdout+stderr at the log after startup — output stays clean.
  • Shell is injected into JobStore (production = interactive bash, tests = hermetic sh -c) so test output never depends on the host's shell config. New test proves alias expansion against a controlled rcfile.
  • deploy/install.sh: curl … | sudo bash one-liner — downloads the latest release .deb, prompts for username/password, picks the run-as user, writes a systemd drop-in, starts the service.
  • Docs: README, deploy, usage, architecture updated with the installer, a curl verify/debug section, and the new param. Version bumped to 1.1.0.

Verified

  • bin/check green (fmt + clippy -D warnings + 71 tests).
  • Installed the .deb as a systemd service running as a real user; curl end-to-end: discovery JSON, /mcp 401 unauth, bad creds → 401 / good → 302, interactive=falsell: not found, interactive=truell is aliased to `ls -alF' (clean, no job-control noise).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a one-liner install option using an interactive installer.
    • Added an optional interactive mode for Bash tool commands to load shell startup settings (aliases/version managers).
    • Added local post-install verification and curl-based debug steps.
  • Bug Fixes
    • Improved job command execution and per-job log capture to reduce noisy startup output and make logging more reliable.
  • Documentation
    • Updated README and deploy/usage/architecture docs to cover the new install flow and interactive Bash option.
  • Chores
    • Bumped release version to 1.1.0.

bash gains an `interactive` param (like `bg`): default stays the fast bare
`sh -c`; `interactive=true` runs the command through `bash -ic`, sourcing the
service user's ~/.bashrc so aliases and version managers (mise/nvm/rbenv)
resolve. Startup job-control warnings (no controlling TTY under systemd) are
discarded by redirecting the child's stdio to /dev/null and having the command
re-point stdout+stderr at the log after startup — output stays clean.

The shell is injected into JobStore (production = interactive bash, tests =
hermetic sh -c) so test output never depends on the host's shell config; a new
test proves alias expansion against a controlled rcfile.

Also adds deploy/install.sh: a curl|sudo bash one-liner that downloads the
latest release .deb, prompts for username/password, picks the run-as user, and
starts the service. Docs (README, deploy, usage, architecture) updated with the
installer, a curl verify/debug section, and the new param.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds interactive bash execution, threads the new flag through bash, updates job tests and deployment docs, and adds a one-shot installer script. The crate version is bumped to 1.1.0.

Changes

Interactive bash execution and installer flow

Layer / File(s) Summary
JobStore shell selection
src/jobs/mod.rs, src/main.rs, src/app_tests.rs, docs/architecture.md
JobStore now stores a shell choice, switches command execution between sh and interactive bash, and updates constructor wiring plus the execution-model docs to use the new path.
bash tool interactive flag
CLAUDE.md, docs/usage.md, src/tools/mod.rs
BashArgs gains interactive, the bash handler forwards it to job execution, and the tool guidance/docs describe bash -ic and ~/.bashrc sourcing.
Job execution tests
src/jobs/mod.rs
Interactive-bash rcfile coverage is added, and existing job lifecycle tests now pass the new interactive argument.
Installer script
Cargo.toml, deploy/install.sh
Adds deploy/install.sh for release download, config creation, systemd override creation, and loopback readiness checks, and bumps the package version to 1.1.0.
Install docs
README.md, docs/deploy.md
Adds curl-based one-liner install steps and loopback OAuth/MCP verification commands, plus deployment troubleshooting notes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • developerz-ai/mcp-ssh#7: Both PRs modify the bash MCP tool implementation in src/tools/mod.rs, overlapping in the same dispatch path.
  • developerz-ai/mcp-ssh#10: Both PRs touch the deploy-time OAuth/token verification flow used by the installation and debug docs.

Suggested labels

claudetm

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main changes: opt-in interactive bash, the one-shot installer, and the version bump.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/interactive-shell-and-installer

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/tools/mod.rs (1)

117-142: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Cover interactive=true at the tool boundary.

This new public parameter is only exercised in JobStore tests. A regression in BashArgs deserialization/defaulting or the interactive.unwrap_or(false) forwarding would still leave the suite green. Add one Tools::bash(...) or HTTP-level test that proves interactive=true reaches the interactive shell path. As per path instructions, **/*: "Flag missing tests for new public behaviour."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/mod.rs` around lines 117 - 142, Add a tool-boundary test for the
new public interactive behavior in Tools::bash, since current coverage only
verifies JobStore internals. Create a test that invokes Tools::bash (or the HTTP
endpoint that routes to it) with interactive=true and asserts it reaches the
interactive shell path, so failures in BashArgs deserialization/defaulting or
interactive.unwrap_or(false) forwarding are caught.

Source: Path instructions

src/jobs/mod.rs (1)

153-200: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Don't hold the jobs mutex across blocking create/spawn.

run() keeps the map lock from id reservation through std::fs::File::create() and Command::spawn(). Those are synchronous OS calls on the async request path, so slow disk or fork/exec stalls can block a Tokio worker and also freeze unrelated poll/list/kill calls behind the same mutex. Reserve the id first, then do file setup/spawn without the lock and offload the blocking file create. As per path instructions, src/**/*.rs: "Async end-to-end... spawn_blocking for blocking I/O" and "keep critical sections tiny."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/jobs/mod.rs` around lines 153 - 200, The run() path holds the jobs mutex
across synchronous File::create and Command::spawn, which can block the Tokio
worker and delay unrelated requests. Update the job reservation flow around
self.jobs.lock(), JobId::generate, and log_path creation so the lock is only
used to reserve and insert the id, then release it before any blocking work.
Move the log file creation to spawn_blocking or another blocking-safe path, and
spawn the child without holding the mutex so the critical section stays tiny.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@deploy/install.sh`:
- Around line 56-59: The service user selection in the install flow is
defaulting to SUDO_USER, which can widen privileges beyond the intended trust
boundary. Update the user prompt logic in the install script around RUN_USER so
the default is the dedicated mcp-ssh account, and only allow a broader account
when the operator explicitly enters it. Keep the existing user-existence check,
and make sure the default path for the service setup remains tied to the
low-privilege service user rather than the invoking admin account.
- Around line 75-79: The install script’s PUBLIC_HOST handling in the ENV_FILE
write path leaves stale MCP_SSH_ALLOWED_HOSTS values behind when the hostname is
blank. Update the install.sh logic around the PUBLIC_HOST check so a blank value
removes or clears the existing ENV_FILE instead of skipping it, ensuring the
rerun falls back to localhost-only behavior. Add a rerun smoke test covering the
blank-hostname path to prove the env file is cleared and the old allowlist does
not persist.
- Around line 68-71: Raw interpolation of USER_NAME and PASS1 into the TOML
output can break config.toml when values contain quotes, backslashes, or
newlines; update the install.sh config-writing logic around the CONF heredoc to
escape TOML string values before writing them. Use the existing credential setup
flow with mcp-ssh set-auth instead of manually embedding the password in the
config, and keep the generated user/pass entries valid for TOML parsing.

In `@docs/deploy.md`:
- Around line 51-52: The bearer-token step references bin/mcp-token, which is
unavailable in a .deb installation and breaks the packaged-install verification
flow. Update the deploy docs to make this step explicitly source-checkout only,
or replace it with a curl-based OAuth PKCE token flow that works after a package
install; keep the surrounding verify steps consistent with the install path
described in the same section.

---

Outside diff comments:
In `@src/jobs/mod.rs`:
- Around line 153-200: The run() path holds the jobs mutex across synchronous
File::create and Command::spawn, which can block the Tokio worker and delay
unrelated requests. Update the job reservation flow around self.jobs.lock(),
JobId::generate, and log_path creation so the lock is only used to reserve and
insert the id, then release it before any blocking work. Move the log file
creation to spawn_blocking or another blocking-safe path, and spawn the child
without holding the mutex so the critical section stays tiny.

In `@src/tools/mod.rs`:
- Around line 117-142: Add a tool-boundary test for the new public interactive
behavior in Tools::bash, since current coverage only verifies JobStore
internals. Create a test that invokes Tools::bash (or the HTTP endpoint that
routes to it) with interactive=true and asserts it reaches the interactive shell
path, so failures in BashArgs deserialization/defaulting or
interactive.unwrap_or(false) forwarding are caught.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 516cb816-313d-4137-a6c8-405e57fafba5

📥 Commits

Reviewing files that changed from the base of the PR and between bd81369 and 78b3dad.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • CLAUDE.md
  • Cargo.toml
  • README.md
  • deploy/install.sh
  • docs/architecture.md
  • docs/deploy.md
  • docs/usage.md
  • src/app_tests.rs
  • src/jobs/mod.rs
  • src/main.rs
  • src/tools/mod.rs

Comment thread deploy/install.sh Outdated
Comment thread deploy/install.sh
Comment thread deploy/install.sh
Comment thread docs/deploy.md Outdated
- Default service user to dedicated mcp-ssh, not $SUDO_USER; auto-provision
  it when missing (the .deb assumes but never creates it).
- Escape username/password before writing config.toml (toml_escape).
- Clear $ENV_FILE on blank hostname so reruns drop a stale allowlist.
- docs: mark bin/mcp-token verify step as source-checkout-only (.deb lacks it).

Co-Authored-By: Claude <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/deploy.md`:
- Around line 51-54: The `bin/mcp-token` example is missing the credential
source, so update the deploy docs to show that it reads `MCP_SSH_USER` and
`MCP_SSH_PASS` from the environment or `.env`, not `/etc/mcp-ssh/config.toml`.
In the section around `TOKEN=$(bin/mcp-token)`, add a brief rule-first note and
an example showing those variables being set before invoking `bin/mcp-token`, so
the one-liner works as written.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: bc43591f-2953-45d8-b3a2-f7f0d768e639

📥 Commits

Reviewing files that changed from the base of the PR and between 78b3dad and 37e7020.

📒 Files selected for processing (2)
  • deploy/install.sh
  • docs/deploy.md

Comment thread docs/deploy.md Outdated
- bin/mcp-token requires MCP_SSH_USER/MCP_SSH_PASS from env or .env;
  the prior `TOKEN=$(bin/mcp-token)` one-liner failed as written.
- Prompt for both via read (password via read -rsp, unset after) so
  the verify snippet is self-contained and accurate.

Co-Authored-By: Claude <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/deploy.md`:
- Around line 53-58: The token prompt flow in the deploy script is mangling
passwords containing backslashes because the hidden password read is not using
raw input. Update the two read prompts in the token bootstrap flow so both the
MCP_SSH_USER and MCP_SSH_PASS reads use raw mode, keeping the existing
identifiers MCP_SSH_USER, MCP_SSH_PASS, and bin/mcp-token path intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 943acbb7-6397-44bb-ab1c-983214b99096

📥 Commits

Reviewing files that changed from the base of the PR and between 37e7020 and b5362c7.

📒 Files selected for processing (1)
  • docs/deploy.md

Comment thread docs/deploy.md
@sebyx07 sebyx07 merged commit 022b137 into main Jun 27, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant