AsamK/signal-cli
signal-cli provides an unofficial commandline, JSON-RPC and dbus interface for the Signal messenger.
Single-maintainer risk — review before adopting
weakest axiscopyleft license (GPL-3.0) — review compatibility
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
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 →Show less
- ✓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 Concerns → Mixed 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.
[](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:
- 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. - 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.
- 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 |
#!/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).
⚡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
- Define a new public method signature in Manager.java (e.g.,
public void newOperation(...) throws ...). Use existing exception classes fromapi/or create new ones. (lib/src/main/java/org/asamk/signal/manager/Manager.java) - If the operation is long-running, create a new Action class implementing HandleAction in
actions/NewOperationAction.javaand call it from Manager.execute(). (lib/src/main/java/org/asamk/signal/manager/actions/HandleAction.java) - 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) - The method is automatically exposed via JSON-RPC daemon (jsonrpsee macros) and D-BUS interface; test with
signal-cli --helpordbus-send. (build.gradle.kts)
Add a new JSON-RPC transport or modify Rust client
- Implement the Transport trait in a new file under
client/src/transports/(e.g.,websocket.rs), withconnect()andsend_request()methods. (client/src/transports/mod.rs) - Update
client/src/main.rsCLI argument parsing (clap derive macro) to accept the new transport type and instantiate it in main(). (client/src/main.rs) - If adding a new codec format (not JSON), implement your frame codec in
transports/stream_codec.rsusing tokio_util::codec traits. (client/src/transports/stream_codec.rs) - 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
- Update the libsignal-service-java version in
gradle/libs.versions.toml(or inline inlib/build.gradle.kts), respecting the 3-month server compatibility window. (gradle/libs.versions.toml) - 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) - 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) - 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
- 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) - 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) - 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
- 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.
🔗Related repos
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
Top contributors
- @AsamK — 81 commits
- @tonycpsu — 3 commits
- @dependabot[bot] — 2 commits
- @moppman — 2 commits
- @zmjohnso — 2 commits
📝Recent commits
Click to expand
Recent commits
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.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.