A persistent shell daemon written in C++. Shelld maintains shell sessions that persist across client connections, enabling command execution via API without spawning new shell processes for each command.
- Persistent sessions: Maintains context (environment variables, current directory) between commands
- Multiple sessions: Several independent shells running in parallel
- PTY support: Compatible with interactive programs (vim, less, htop, etc.)
- Async job execution: Run long-duration commands without blocking (NEW ✨)
- Pseudo-streaming: Poll job output for real-time progress updates (NEW ✨)
- Signal handling: Send SIGINT to running processes
- Configurable timeout: Execution time limit per command (10 min default)
┌─────────────┐ ┌──────────────────┐ ┌─────────────┐
│ Client │────▶│ Daemon (Shell) │────▶│ PTY/Bash │
│ (CLI/API) │◀────│ - Queue cmds │◀────│ │
└─────────────┘ │ - I/O Handling │ └─────────────┘
└──────────────────┘
- Automation: Execute scripts/commands from other applications
- CI/CD Integration: Entry point for automation pipelines
- Remote execution: Execute commands remotely via API
- IDE/Editors: Backend for integrated terminals
From source:
git clone https://github.com/hansipie/shelld.git
cd shelld/src
# With CMake
mkdir build && cd build
cmake ..
make
# Or direct compilation
g++ -std=c++17 -Wall -Wextra -Wpedantic -o shelld \
main.cpp Daemon.cpp Session.cpp UnixSocket.cpp -pthread./shelld
# Listening on /tmp/shelld.sock# Show available commands
echo "HELP" | nc -U /tmp/shelld.sock
# Create a session
echo "CREATE mysession" | nc -U /tmp/shelld.sock
# List sessions
echo "LIST" | nc -U /tmp/shelld.sock
# Execute a command (blocks until done, 30s timeout)
echo "EXEC mysession ls -la" | nc -U /tmp/shelld.sock
# Destroy a session
echo "DESTROY mysession" | nc -U /tmp/shelld.sock
# Stop the daemon
echo "EXIT" | nc -U /tmp/shelld.sockFor long-running commands, use the async API:
# Start an async job (returns immediately with job ID)
echo "EXEC_ASYNC mysession 'npm run build'" | nc -U /tmp/shelld.sock
# Output: OK: Job 1
# Check job status
echo "JOB_STATUS mysession 1" | nc -U /tmp/shelld.sock
# Output: RUNNING elapsed_ms=5234
# Get partial output (while running)
echo "JOB_OUTPUT mysession 1 partial" | nc -U /tmp/shelld.sock
# Get final output (after completion)
echo "JOB_OUTPUT mysession 1" | nc -U /tmp/shelld.sock
# List all jobs
echo "JOB_LIST mysession" | nc -U /tmp/shelld.sock
# Cancel a running job
echo "JOB_CANCEL mysession 1" | nc -U /tmp/shelld.sock
# Cleanup finished job
echo "JOB_CLEANUP mysession 1" | nc -U /tmp/shelld.sockPseudo-streaming pattern:
# Use the helper script for automatic polling
./examples/stream_job.sh mysession "npm run build" 2See examples/ for ready-to-use scripts!
Simple text-based protocol over Unix socket:
| Command | Description |
|---|---|
CREATE <name> |
Create a new session |
LIST |
List all active sessions |
DESTROY <name> |
Destroy a session |
HELP |
Show available commands |
EXIT |
Stop the daemon |
| Command | Description |
|---|---|
EXEC <name> <cmd> |
Execute command (blocks, 30s timeout) |
INTERRUPT <name> |
Send SIGINT (Ctrl+C) to session |
SETENV <name> <key> <value> |
Set environment variable |
| Command | Description |
|---|---|
EXEC_ASYNC <name> <cmd> |
Start async job, returns job ID |
JOB_STATUS <name> <id> |
Get job status (RUNNING/COMPLETED/etc) |
JOB_OUTPUT <name> <id> [partial] |
Get job output |
JOB_LIST <name> |
List all jobs in session |
JOB_CANCEL <name> <id> |
Cancel running job |
JOB_CLEANUP <name> <id> |
Remove finished job |
Job States: RUNNING, COMPLETED, TIMEOUT, CANCELLED, FAILED
📚 Full documentation: See docs/ASYNC_COMMANDS.md for complete guide with diagrams and examples.
| Option | Description | Default |
|---|---|---|
| Socket path | Unix socket location | /tmp/shelld.sock |
- Unix socket: Filesystem permissions control access
- Client UID verification: Planned feature
| Project | Description |
|---|---|
| shpool | Lightweight tmux alternative in Rust |
| shellmgr | Multi-shell REST API in Go |
| tmux | Terminal multiplexer |
MIT