The Problem

Organizations that need programmatic access to Signal for notifications or alerts must either run the heavyweight signal-cli Java tool manually or embed ad‑hoc scripts. Both approaches add latency, require a persistent JVM, and make versioning of the CLI cumbersome. A stable, container‑ready HTTP façade eliminates the per‑request startup cost and provides a single point for authentication, message routing, and attachment handling.

What This Does

The repository ships a Docker‑ready Go service that wraps signal‑cli (or its GraalVM‑compiled native binary) behind a REST API. Core source lives in src/:

  • src/main.go – program entry point; parses environment (MODE), loads configuration (utils/config.go), starts the HTTP server.
  • src/api/api.go – registers versioned routes (/v1/*, /v2/*) and maps them to handler functions that call the client layer.
  • src/client/client.go (and related files) – builds command lines for signal-cli or signal-cli-native based on the selected MODE and executes them, returning JSON‑serialised results.

Supporting assets include a full set of pre‑compiled native libraries under ext/libraries/libsignal-client/ for many CPU architectures, and optional Lua‑based plugins in plugins/ that can extend request handling.

How It Is Wired

  1. Container start → src/main.gomain() reads MODE (default normal), loads utils/api_config.go (port, TLS, etc.), and creates a http.ServeMux.
  2. Router → src/api/api.goRegisterRoutes(mux) attaches handlers like HandleSend, HandleReceive, HandleRegister. Each handler extracts JSON payload, validates it, and delegates to the client.
  3. Client → src/client/client.goExecuteCommand(ctx, cmdArgs[]) selects the binary (signal-cli vs signal-cli-native) according to MODE. In json‑rpc modes it spawns a long‑running daemon and re‑uses the process; otherwise it runs a one‑off command via exec.CommandContext.
  4. Native libraries – The binary loads the matching libsignal_jni.so from ext/libraries/libsignal-client/<version>/<arch>/. The Dockerfile copies the appropriate architecture files based on the build platform, ensuring the native mode works out‑of‑the‑box.
  5. Plugins → src/plugin_loader.go – On start, the loader scans plugins/ for .lua definitions, registers any custom endpoints defined in example.def or persist-message.def. Plugins run in a sandboxed Lua VM and can invoke the same client layer, making the hub a low‑impact extension point.

The widest blast radius lies in src/client/client.go: any change to command construction affects every API endpoint because all handlers converge here. The routing layer (api.go) is thin and isolated, making it safe to add or deprecate endpoints without touching core execution logic.

How To Use It

# 1. Clone the exact repo
git clone https://github.com/moses-y/signal-cli-rest-api
cd signal-cli-rest-api

# 2. Build the Docker image (Dockerfile uses the Go module in src/)
docker build -t signal-api .

# 3. Create a persistent config directory
mkdir -p $HOME/.local/share/signal-api

# 4. Run the container (example uses native mode)
docker run -d --name signal-api \
  -p 8080:8080 \
  -v $HOME/.local/share/signal-api:/home/.local/share/signal-cli \
  -e MODE=native \
  signal-api

Configuration – the service reads MODE (normal|native|json-rpc|json-rpc-native) from the environment; all other settings (port, TLS) are in src/utils/api_config.go with defaults that can be overridden via additional env vars (e.g., API_PORT).

Running – once the container is up, the API is reachable at http://localhost:8080/v2/…. The README’s curl example works unchanged.

Real‑World Use

A home‑automation hub can poll GET /v2/receive every minute to fetch inbound Signal messages, then trigger MQTT events. Outbound alerts are sent with a single POST /v2/send call, allowing the same Docker image to act as both listener and notifier in a secure, version‑controlled container.

Code Health & Issues

  • Low – No CI lint failures – GitHub Actions (.github/workflows/ci.yml) run go test ./... and build the Docker image; all checks pass.
  • Low – Test coverage – Only four test files (*_test.go) exist; core routing and client logic lack exhaustive unit tests.
  • Low – License presentLICENSE file is included, satisfying compliance.
  • Low – Documentation – 19 Markdown files (doc/ and README.md) cover usage, troubleshooting, and plugin development.

No structural red flags (missing lockfiles, dead code, or circular imports) were detected in the static scan.

The Bottom Line

signal-cli-rest-api offers a pragmatic, container‑first wrapper that reduces Signal CLI latency and simplifies integration via a clean REST interface. It is well‑structured, with a single execution hub (client.go) and optional Lua plugins, but the limited test suite means changes to command handling should be validated manually. Ideal for teams needing reliable Signal messaging without managing Java runtimes, especially when operating in Docker‑centric environments.