RepoPilotOpen in app →

AsamK/signal-cli

signal-cli provides an unofficial commandline, JSON-RPC and dbus interface for the Signal messenger.

Mixed

Single-maintainer risk — review before adopting

weakest axis
Use as dependencyConcerns

copyleft license (GPL-3.0) — review compatibility

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 3d ago
  • 15 active contributors
  • GPL-3.0 licensed
Show all 7 evidence items →
  • CI configured
  • Tests present
  • Single-maintainer risk — top contributor 81% of recent commits
  • GPL-3.0 is copyleft — check downstream compatibility
What would change the summary?
  • Use as dependency ConcernsMixed if: relicense under MIT/Apache-2.0 (rare for established libs)

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Forkable" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Forkable
[![RepoPilot: Forkable](https://repopilot.app/api/badge/asamk/signal-cli?axis=fork)](https://repopilot.app/r/asamk/signal-cli)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/asamk/signal-cli on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: AsamK/signal-cli

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/AsamK/signal-cli shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

WAIT — Single-maintainer risk — review before adopting

  • Last commit 3d ago
  • 15 active contributors
  • GPL-3.0 licensed
  • CI configured
  • Tests present
  • ⚠ Single-maintainer risk — top contributor 81% of recent commits
  • ⚠ GPL-3.0 is copyleft — check downstream compatibility

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live AsamK/signal-cli repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/AsamK/signal-cli.

What it runs against: a local clone of AsamK/signal-cli — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in AsamK/signal-cli | Confirms the artifact applies here, not a fork | | 2 | License is still GPL-3.0 | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 33 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>AsamK/signal-cli</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of AsamK/signal-cli. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/AsamK/signal-cli.git
#   cd signal-cli
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of AsamK/signal-cli and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "AsamK/signal-cli(\\.git)?\\b" \\
  && ok "origin remote is AsamK/signal-cli" \\
  || miss "origin remote is not AsamK/signal-cli (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(GPL-3\\.0)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"GPL-3\\.0\"" package.json 2>/dev/null) \\
  && ok "license is GPL-3.0" \\
  || miss "license drift — was GPL-3.0 at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "lib/src/main/java/org/asamk/signal/manager/Manager.java" \\
  && ok "lib/src/main/java/org/asamk/signal/manager/Manager.java" \\
  || miss "missing critical file: lib/src/main/java/org/asamk/signal/manager/Manager.java"
test -f "lib/build.gradle.kts" \\
  && ok "lib/build.gradle.kts" \\
  || miss "missing critical file: lib/build.gradle.kts"
test -f "client/src/main.rs" \\
  && ok "client/src/main.rs" \\
  || miss "missing critical file: client/src/main.rs"
test -f "build.gradle.kts" \\
  && ok "build.gradle.kts" \\
  || miss "missing critical file: build.gradle.kts"
test -f "gradle/libs.versions.toml" \\
  && ok "gradle/libs.versions.toml" \\
  || miss "missing critical file: gradle/libs.versions.toml"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 33 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~3d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/AsamK/signal-cli"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

signal-cli is an unofficial command-line and daemon interface for the Signal messenger that enables programmatic access to Signal's messaging capabilities on servers. It wraps a patched libsignal-service-java (extracted from Signal-Android) and exposes functionality via CLI, JSON-RPC, and D-BUS interfaces, primarily designed for server-side notifications and automated messaging workflows. Dual-language monorepo: Java/Kotlin backend in lib/ (2.1MB Java, 3.5KB Kotlin) handles Signal protocol via libsignal-service-java; Rust CLI client in client/ (Cargo.toml-driven) provides JSON-RPC and IPC transport bindings to the daemon. Data files (data/) define D-BUS service registration, systemd units, and AppStream metadata. Build system uses Gradle (build.gradle.kts) with custom Kotlin plugins in buildSrc/.

👥Who it's for

System administrators and server operators who need to send Signal messages from automated systems; developers building Signal integrations into backend services; DevOps engineers deploying notification systems that require Signal as a transport.

🌱Maturity & risk

Active and production-ready. The project has consistent releases (with a critical constraint: releases older than 3 months stop working due to Signal-Server incompatibility), organized CI/CD in .github/workflows/ (build.yml, release.yml, codeql-analysis.yml), and regular maintenance. Single active maintainer (AsamK) is a risk factor, but the project is actively maintained and battle-tested.

Three concrete risks: (1) Forced update cadence — releases expire after ~3 months due to Signal-Server protocol changes, making stale deployments non-functional; (2) Single maintainer — all critical decisions and releases depend on one person (AsamK); (3) Native library coupling — requires libsignal-client binary distribution (bundled for x86_64 Linux/Windows/macOS only, other architectures need manual setup per wiki). Dependency count is reasonable but tight coupling to Signal protocol makes this fragile.

Active areas of work

No specific PR/milestone data visible in the file list provided, but the project maintains active release cadence (indicated by .github/workflows/release.yml automation). Recent work focuses on keeping Signal protocol compatibility (3-month release constraint) and platform support across Linux, macOS, and Windows.

🚀Get running

git clone https://github.com/AsamK/signal-cli.git
cd signal-cli
# Requires Java 25+, then build with Gradle
./gradlew build
# Or use pre-built binaries from releases
VERSION=$(curl -Ls -o /dev/null -w %{url_effective} https://github.com/AsamK/signal-cli/releases/latest | sed -e 's/^.*\/v//')
curl -L -O https://github.com/AsamK/signal-cli/releases/download/v"${VERSION}"/signal-cli-"${VERSION}".tar.gz
tar xf signal-cli-"${VERSION}".tar.gz

Daily commands:

# Build the full project
./gradlew build
# Start daemon (after initial account registration)
signal-cli -c ~/.local/share/signal-cli daemon
# CLI commands
signal-cli send -m "Hello" <phone-number>
# JSON-RPC client (Rust, requires daemon running)
cd client && cargo run -- --help

🗺️Map of the codebase

  • lib/src/main/java/org/asamk/signal/manager/Manager.java — Core Signal account manager handling message sending/receiving, contact management, and protocol operations—foundational to all CLI/RPC/D-BUS interfaces.
  • lib/build.gradle.kts — Defines Java library dependencies including libsignal-service-java and protobuf; critical for understanding build output and Signal protocol integration.
  • client/src/main.rs — Rust JSON-RPC client entry point; demonstrates how external tools consume signal-cli's daemon interface.
  • build.gradle.kts — Root build orchestration for multi-module (lib + CLI + client) Gradle project; controls versioning, publishing, and library versions.
  • gradle/libs.versions.toml — Centralized dependency version catalog; Signal protocol library versions directly impact compatibility with Signal servers.
  • lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java — Manages multiple Signal accounts; critical for understanding how signal-cli handles multi-user daemon scenarios.
  • client/src/jsonrpc.rs — Rust JSON-RPC protocol serialization/deserialization; shows contract between daemon and external clients.

🛠️How to make changes

Add a new Account Manager API method

  1. Define a new public method signature in Manager.java (e.g., public void newOperation(...) throws ...). Use existing exception classes from api/ or create new ones. (lib/src/main/java/org/asamk/signal/manager/Manager.java)
  2. If the operation is long-running, create a new Action class implementing HandleAction in actions/NewOperationAction.java and call it from Manager.execute(). (lib/src/main/java/org/asamk/signal/manager/actions/HandleAction.java)
  3. Add corresponding DTO in api/ folder if returning complex data (e.g., NewResult.java), following existing patterns like Contact, Group, etc. (lib/src/main/java/org/asamk/signal/manager/api/Contact.java)
  4. The method is automatically exposed via JSON-RPC daemon (jsonrpsee macros) and D-BUS interface; test with signal-cli --help or dbus-send. (build.gradle.kts)

Add a new JSON-RPC transport or modify Rust client

  1. Implement the Transport trait in a new file under client/src/transports/ (e.g., websocket.rs), with connect() and send_request() methods. (client/src/transports/mod.rs)
  2. Update client/src/main.rs CLI argument parsing (clap derive macro) to accept the new transport type and instantiate it in main(). (client/src/main.rs)
  3. If adding a new codec format (not JSON), implement your frame codec in transports/stream_codec.rs using tokio_util::codec traits. (client/src/transports/stream_codec.rs)
  4. Update Cargo.toml to add any new async dependencies (e.g., tokio-tungstenite for WebSocket) and bump versions as needed. (client/Cargo.toml)

Update Signal protocol library version or handle server API changes

  1. Update the libsignal-service-java version in gradle/libs.versions.toml (or inline in lib/build.gradle.kts), respecting the 3-month server compatibility window. (gradle/libs.versions.toml)
  2. If Signal server API changed (e.g., new message features), modify the corresponding action in actions/ or Manager.java to adapt protocol calls. (lib/src/main/java/org/asamk/signal/manager/Manager.java)
  3. Add new exception types in api/ if new error conditions arise (e.g., NewServerException.java). (lib/src/main/java/org/asamk/signal/manager/api/Contact.java)
  4. Update CHANGELOG.md to document the protocol version bump and any breaking API changes for CLI/RPC consumers. (CHANGELOG.md)

Add a new Account Settings option

  1. Add a new field to the Settings class (with getter/setter and serialization logic for account persistence). (lib/src/main/java/org/asamk/signal/manager/Settings.java)
  2. Expose it via Manager API (e.g., public void setSetting(String key, String value)) and ensure serialization to disk in SignalAccountFiles. (lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java)
  3. The setting is automatically available in JSON-RPC and D-BUS without additional CLI/RPC glue code (framework handles reflection). (lib/src/main/java/org/asamk/signal/manager/Manager.java)

🪤Traps & gotchas

  1. 3-month expiry: Deployments using releases >3 months old silently fail to connect to Signal servers. Set up automated updates or CI monitoring. 2. libsignal-client binary: Platform-specific native library; if your architecture isn't x86_64 Linux/Windows/macOS, manual compilation per wiki (Provide native lib for libsignal) is required. 3. Phone number registration: Initial account setup requires receiving an SMS or call to verify the phone number; cannot be fully automated. 4. Gradle version lock: build.gradle.kts uses gradle/wrapper pinned version; running with system Gradle may cause incompatibilities. 5. D-BUS socket permissions: Daemon socket at /run/user/$UID/signal-cli/ requires correct ownership; systemd user service setup is non-obvious.

🏗️Architecture

💡Concepts to learn

  • Signal Protocol (Double Ratchet Algorithm) — signal-cli's core functionality depends on correctly implementing this E2E encryption protocol; understanding it explains why Signal-Server changes break old releases and why libsignal-service-java version updates are critical.
  • D-BUS (Desktop Bus) — signal-cli exposes daemon functionality via D-BUS interface (data/org.asamk.Signal.conf, data/org.asamk.Signal.service); essential for systemd integration and inter-process communication on Linux.
  • JSON-RPC 2.0 — Primary programmatic API for signal-cli daemon (client/src/jsonrpc.rs); enables language-agnostic clients like the Rust CLI to call daemon methods.
  • systemd User Services — signal-cli daemon runs as per-user systemd service (data/signal-cli@.service); understanding socket activation and unit dependencies is required for server deployments.
  • Tokio async runtime (Rust) — Rust client uses tokio for concurrent I/O (features: rt, macros, net); essential for understanding the Rust side's async transport handling (client/src/transports/).
  • Gradle Plugin Development — buildSrc/src/main/kotlin/ defines custom plugins (CheckLibVersionsPlugin, ExcludeFileFromJar) for reproducible builds; modifying the build process requires understanding Gradle plugin APIs.
  • Native Library Bundling (JNI) — signal-cli bundles libsignal-client (Rust native lib) as JNI artifacts; deployment complications (x86_64 Linux vs. other architectures) stem from this architecture mismatch.
  • signalapp/Signal-Android — Official Signal Android client; signal-cli's libsignal-service-java is a patched extract from this repo's source.
  • Turasa/libsignal-service-java — Maintains the patched Java Signal protocol library that signal-cli depends on; upstream for protocol compatibility.
  • signalapp/libsignal — Official native Signal crypto library (Rust); libsignal-client binaries bundled in signal-cli are built from this.
  • moxie-social/dbus-codegen-rust — D-BUS interface code generation; directly applicable to automating signal-cli's D-BUS service definition (data/org.asamk.Signal.conf).
  • matrix-org/matrix-cli-tools — Similar daemon+CLI pattern for another messaging protocol; architectural reference for CLI-to-daemon IPC design.

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add integration tests for Rust client JSON-RPC transport layer

The Rust client (client/src/transports/) has three transport implementations (IPC, TCP, stream_codec) but no visible integration tests. Given this is a communication layer, adding tests would ensure reliability across different transport mechanisms and catch regressions early. This is high-value since the client is mentioned in the README as an example implementation that other users reference.

  • [ ] Create client/tests/ directory with integration test files
  • [ ] Add tests for client/src/transports/ipc.rs covering connection, serialization, and error cases
  • [ ] Add tests for client/src/transports/tcp.rs with mock server scenarios
  • [ ] Add tests for client/src/transports/stream_codec.rs codec round-trip behavior
  • [ ] Update client/Cargo.toml to include dev-dependencies (e.g., tokio-test, mockito)
  • [ ] Document test setup in CONTRIBUTING.md for the Rust client module

Add GitHub Actions workflow for Rust client CI (separate from main Java build)

The repo has build.yml and release.yml for Java/Gradle, but no dedicated CI for the Rust client (client/). Since Cargo.toml and Cargo.lock exist, the client can be tested independently. This prevents Rust-specific breakages from going undetected and reduces contributor friction.

  • [ ] Create .github/workflows/rust-client-ci.yml with steps to: install Rust toolchain, run 'cargo build' in client/, run 'cargo test' in client/, and optionally 'cargo clippy'
  • [ ] Add a step to check Cargo.lock is up-to-date to prevent dependency drift
  • [ ] Configure the workflow to run on push to main and on PRs affecting client/ directory
  • [ ] Add a badge to README.md linking to the workflow status

Document JSON-RPC API reference in docs/ directory with schema examples

The README mentions JSON-RPC interface and links to a man page, but there's no structured API documentation in docs/. The file docs/CALL_TUNNEL.md exists but is feature-specific. Adding a docs/JSONRPC_API.md with endpoint schemas, request/response examples, and error codes would lower the barrier for users building clients and integrations.

  • [ ] Create docs/JSONRPC_API.md with sections for each RPC method (send_message, receive_messages, etc.)
  • [ ] Include JSON schema or TypeScript interface examples for request/response payloads
  • [ ] Add examples showing how to use the Rust client (client/src/main.rs) to call each method
  • [ ] Document error codes and their meanings returned by the daemon
  • [ ] Link this new doc from README.md in the 'JSON-RPC interface' section

🌿Good first issues

  • Add integration tests for client/src/transports/tcp.rs: currently ipc.rs and stream_codec.rs lack coverage for timeout and reconnection scenarios; write tokio-based tests.
  • Document the Signal protocol version update process: CHANGELOG.md lacks a section explaining when and how libsignal-service-java version must be bumped; create PROTOCOL_UPDATES.md with concrete examples.
  • Add JSON schema for D-BUS method signatures: data/org.asamk.Signal.conf is manually written; generate it from Rust type definitions in client/src/jsonrpc.rs to prevent drift.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 5f94b7b — fix: Attempted immutable list modification causes runtime exception (#2038) (connorlanigan)
  • dc43e44 — fix: flush SSE response headers immediately on connect (#2034) (legacycode)
  • 251bd2d — Refactor profile key extraction (AsamK)
  • a3fcda7 — Update kotlin jvm version for buildSrc (AsamK)
  • c9e2504 — Store profile keys from group requesting members (#2031) (PatrickDattilio)
  • 9b09df5 — Bump rustls-webpki from 0.103.12 to 0.103.13 in /client (#2030) (dependabot[bot])
  • 5fe94ff — Temporarily disable 26 build due to container issue (AsamK)
  • 6286a05 — Update libsignal-service (AsamK)
  • e6635d1 — Prepare next release (AsamK)
  • 056878f — Bump version to 0.14.3 (AsamK)

🔒Security observations

Failed to generate security analysis.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.