# shell2http: Run Shell Commands Through an HTTP API

> shell2http maps HTTP paths to shell commands and returns command output to the caller. It suits developers prototyping shell-based integrations, with explicit controls for forms, CGI behavior, authentication, timeouts, and error handling.

**msoap/shell2http** — Executing shell commands via HTTP server

- Repository: https://github.com/msoap/shell2http
- Stars: 1,514 · Forks: 150
- Language: Go
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/msoap-shell2http

## Where shell2http Fits in a Development Stack

shell2http solves a narrow but specific problem: it turns a configured shell command into an HTTP endpoint. A command-line argument associates a URL path with a shell command, and an HTTP request for that path receives the command's standard output. The README describes the contract in unusually direct terms: return stdout, with no application framework in between. That makes the project suitable for development, prototyping, remote control, and rapid iteration around shell-based functionality.

The intended users are developers or operators who already know which commands they want to expose and need a small HTTP adapter instead of writing a conventional service. A shell script can become reachable as `/date`, `/top`, or `/env`, while a request can also supply form data or uploaded files under controlled variable names. The project is not a general workflow engine. It does not document queues, job persistence, retries, result storage, or a database. Its role is to execute a configured command when a request arrives.

That boundary matters. shell2http turns network access into process execution on the host running the server. The README does not provide an allowlist for arbitrary shell text in requests. Instead, the administrator chooses commands at startup. Even so, environment variables, filesystem permissions, authentication, and the account running the server determine what those commands can actually do.

## Path Mapping, Processes, and HTTP Responses

The basic configuration follows a repeated path-and-command structure. shell2http receives route definitions on its command line, listens on an HTTP port, matches an incoming request, executes the associated shell command, and returns stdout. By default it binds to all interfaces and listens on port 8080. The `-port` option can request a random port by using `0`, while `-host` changes the bind address.

The repository layout supports this interpretation. `shell2http.go`, `config.go`, and `http_middlewares.go` separate server execution, option handling, and request processing. The `go.mod` file declares the Go module as `github.com/msoap/shell2http`, requires Go 1.18, and includes `github.com/mattn/go-shellwords` plus `github.com/msoap/raphanus`. That is a compact Go service rather than a shell script wrapped around another web framework.

Several switches change the request-to-process boundary. `-shell=""` disables shell execution according to the usage text. `-one-thread` serializes each shell command in one thread. `-timeout=N` applies a command timeout in seconds. By default, output is stdout only. `-include-stderr` adds standard error, while `-show-errors` retains output from commands that exit unsuccessfully. The `-500` switch converts a nonzero command exit into an HTTP 500 response. These are explicit choices for environments where command completion, stream handling, or HTTP status matters.

For CGI mode, `-cgi` places HTTP request information in environment variables, writes POST, PUT, or PATCH data to standard input when `-form` is not set, and parses script-generated headers. A command can therefore emit `Location:` or `Status:` lines, although the README does not promise a complete CGI implementation.

## Installing shell2http and Running the First Endpoint

For a Go installation, the README gives a source installation command. The README states a minimum Go version of 1.12, while the repository's `go.mod` declares Go 1.18. The command installs the current module path into the Go binary directory.

```bash
go install github.com/msoap/shell2http@latest
```

If the installed binary is not already on `PATH`, the README documents adding a link in `~/bin`. Create the target location first if it does not exist.

```bash
ln -s $(go env GOPATH)/bin/shell2http ~/bin/shell2http
```

Start a real endpoint by mapping `/date` to the system `date` command. This example uses `GET:/date`, which the README presents as a path-specific method declaration.

```bash
shell2http GET:/date date
```

With the default port, a request to `http://localhost:8080/date` reaches the mapped command and returns its standard output. The date text comes from the host system. The project does not document a health endpoint or a required response schema beyond returning stdout.

Docker is also supported. The published image entrypoint is `/app/shell2http`, and its default command is `-help`, so arguments such as paths and commands need to be supplied when a container is run. The repository's Dockerfile builds the Go binary in an Alpine builder stage and copies it into a final Alpine image.

## Forms, Uploads, and Restricted Variables

The `-form` mode turns request data into shell variables. Query parameter names become `$v_NNN`, so `from=10` becomes `$v_from`. Uploaded files receive both a temporary path, `$filepath_ID`, and the browser-provided name, `$filename_ID`. The README states that the temporary uploaded file is deleted automatically. A route can declare its accepted method with a `GET:` or `POST:` prefix, which is useful when the same server serves a form and its submission target.

The following example copies an upload into a file named `uploaded_file.dat` and then prints `Ok`.

```bash
shell2http -form \
    GET:/form 'echo "<html><body><form method=POST action=/file enctype=multipart/form-data><input type=file name=uplfile><input type=submit></form>"' \
    POST:/file 'cat $filepath_uplfile > uploaded_file.dat; echo Ok'
```

The README then uses `curl` to submit a local file. This command sends one multipart field named `uplfile` to the `/file` endpoint.

```bash
curl -i -F uplfile=@some/file/path 'http://localhost:8080/file'
```

Input restriction deserves attention. `-form-check` accepts a regular expression and only passes matching form fields, documented with `^[0-9]+$` for digits. Without that control, the feature makes request values available to the shell. The expression should therefore match the exact parameter names and value shape accepted by each command. The README documents filtering, but it does not describe stronger controls such as per-field file-size limits or a command sandbox.

## Authentication, TLS, Logging, and Operational Boundaries

shell2http offers controls relevant to an HTTP command interface. `-basic-auth="user_name:password"` configures HTTP Basic Authentication and can be repeated for multiple users. The README also names `SH_BASIC_AUTH` as an environment-variable alternative for credentials. Basic Authentication does not encrypt traffic by itself, so the HTTPS switches matter when credentials cross a network. `-cert=cert.pem` and `-key=key.pem` select certificate and private-key files; specifying both starts the HTTPS server.

Logging can go to standard output or a file selected with `-log=filename`. Entries include timestamps unless `-no-log-timestamp` is set. The `-no-index` option removes the generated index page, while `-add-exit` adds an `/exit` route. `-cache=N` caches command output for a specified number of seconds. The README does not explain cache invalidation, concurrency semantics, or whether every route shares one cache mechanism, so those details should not be inferred.

Environment exposure is configurable. By default, shell2http exports `PATH`, `HOME`, `LANG`, `USER`, and `TMPDIR`. `-export-vars=var` selects named variables, and `-export-all-vars` exports the current environment. This is a meaningful privilege boundary. A command may inherit more credentials, paths, or service configuration when all variables are exported. The server should run with an account whose filesystem and environment permissions match the smallest set of commands it needs.

The Snap package introduces another documented boundary. Its sandbox has `/bin` and `/usr/bin` directories that differ from system-wide `PATH`, so commands may behave differently or fail. The release documentation also includes Linux, macOS, Windows, and Raspberry Pi binaries. The project is under the MIT license, which permits use, modification, distribution, and sublicensing subject to the license terms, but operators remain responsible for reviewing those terms in their own context.

## Failure Modes and the Cost of Exposing Commands

The largest limitation is structural rather than incidental. A configured route crosses two trust boundaries at once: HTTP input reaches a shell environment, and command output returns to the caller. A malformed command, unavailable executable, permission failure, timeout, or nonzero exit can all produce behavior that differs depending on the selected flags. With default settings, stderr is excluded and a nonzero exit does not automatically become HTTP 500. A client may therefore receive an empty or partial success response even when the command failed. `-include-stderr`, `-show-errors`, and `-500` address different parts of that ambiguity, but operators must choose them deliberately.

Timeouts also need interpretation. The README says `-timeout=N` sets a timeout for shell command execution in seconds. It does not specify whether the route remains blocked, how child processes are terminated, or whether output produced before termination is retained. It also does not document rollback or transactional behavior. A command that changes files or contacts external services should not be assumed to reverse itself when a timeout occurs.

The project is the wrong tool when a workflow needs durable job state, a queue, structured API responses, centralized policy, or independent user-level authorization. It is also a poor fit for accepting arbitrary commands from callers. A service designed around a fixed set of Go handlers can validate input inside application code and expose a narrower interface. That alternative trades shell2http's small command-line setup for more control over validation, authorization, persistence, and response handling.

Maintenance status should be recorded precisely. The repository is not archived, and its last push was on 2026-10-11. Release `v1.18.0` was published on 2026-10-11, after `v1.17.0` from 2024-03-23 and `v1.16.0` from 2023-03-11. The repository includes `go.mod`, `go.sum`, tests, a Makefile, and release-oriented build files, but upgrade cost still depends on reviewing changes between the installed version and the selected release.

## Conclusion

Adopt shell2http for local development, prototypes, and controlled integrations where HTTP-to-shell mapping is intentional. Do not expose unrestricted commands to untrusted networks. Verify first that every mapped command, exported environment variable, uploaded-file path, authentication rule, and timeout matches the deployment boundary.

## FAQ

### How do I install shell2http?

The README documents Homebrew, release binaries, Docker, Snap, and a Go installation. For source installation with Go, use `go install github.com/msoap/shell2http@latest`; the repository's `go.mod` declares Go 1.18.

### How do I expose a shell command with shell2http?

Start the server with a path followed by a shell command, for example `shell2http /date date`. A request to the configured host and port then returns the command's standard output.

### Can shell2http receive uploaded files?

Yes. With `-form`, an uploaded file is available through `$filepath_ID`, where `ID` matches the multipart field name, and the temporary file is deleted automatically. The README also documents `$filename_ID` for the uploaded file name.

### How does shell2http protect HTTP endpoints?

The README documents `-basic-auth`, repeated user configuration, and `SH_BASIC_AUTH` for credentials. HTTPS can be enabled with `-cert` and `-key`; the project does not document application-level authorization beyond these HTTP controls.

## Sources

- [Issues](https://github.com/msoap/shell2http/issues)
- [License: MIT](https://github.com/msoap/shell2http/blob/master/LICENSE)
- [msoap/shell2http on GitHub](https://github.com/msoap/shell2http)
- [README](https://github.com/msoap/shell2http/blob/master/README.md)
- [Releases](https://github.com/msoap/shell2http/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/msoap-shell2http
