⚠️ Experimental Project - This project is under active development and changing rapidly. APIs, configuration, and features may change without notice.🔓 Security Warning - This project is highly insecure. It executes arbitrary plugins, has no authentication beyond Signal's, and should only be run in trusted environments. Do not expose to untrusted users.
A Signal messaging bot with LLM integration and plugin support.
Once running, chat with the bot naturally. It uses plugins automatically based on your requests:
You: "What tasks do I have pending?"
Bot: [invokes task plugin]
You have 3 pending tasks:
1. Call the plumber (due: tomorrow)
2. Review budget spreadsheet (due: Friday)
3. Book dentist appointment
You: "Add a task to pick up groceries"
Bot: [invokes task plugin]
Added: "Pick up groceries"
You: "What's using all my CPU?"
Bot: [invokes ps plugin]
Top processes by CPU:
- firefox (12.3%)
- code (8.1%)
- slack (4.2%)
You: "Mark task 1 as done"
Bot: [invokes task plugin]
Completed: "Call the plumber"
In group chats, prefix messages with the trigger keyword (default: T):
You: "T what tasks are due today?"
Bot: [responds to the group]
- Go 1.25.3+
- signal-cli running in JSON-RPC daemon mode
- An LLM API endpoint (OpenAI-compatible, e.g., DeepInfra, OpenAI, local Ollama)
- SQLite3 (for conversation memory and reminders)
- Taskwarrior (optional, for task plugin)
- Install signal-cli from https://github.com/AsamK/signal-cli
- Register or link your bot account
- Run in JSON-RPC daemon mode:
signal-cli -a +1234567890 daemon --http=localhost:8080The bot connects to the JSON-RPC endpoint for sending messages and subscribes to SSE events for receiving them.
# Build the binary
make build
# Build with formatting, vetting, and tests
make all
# Clean build artifacts
make clean# Source environment config
source .env
# Build and run
make run
# Run with debug logging
make run-debugConfiguration can be done via YAML file, environment variables, or both. Environment variables take precedence over YAML values.
Priority: Defaults → YAML file → Environment variables (highest)
Copy the example config and customize:
cp config.example.yaml config.yamlRun with the config file:
./bin/tron -config config.yamlExample config.yaml:
# Signal Configuration
signal_cli_url: "http://localhost:8080"
signal_bot_account: "+1234567890"
signal_operator: "+0987654321"
# LLM Configuration
llm_api_url: "https://api.deepinfra.com/v1/openai"
llm_api_key: "your-api-key-here"
llm_model: "deepseek-ai/DeepSeek-V3.1"
llm_system_prompt: |
You are a personal assistant bot on Signal.
Keep responses short. Never use emojis.
# Storage
plugin_dir: "plugins.d"
db_path: "tron.db"
# Behavior
trigger_keyword: "T"
memory_max_messages: 50
memory_max_minutes: 60
daily_summary_hour: 7You can also use environment variables (useful for secrets or overriding config):
# Required (if not in YAML)
export SIGNAL_BOT_ACCOUNT="+1234567890"
export SIGNAL_OPERATOR="your-uuid-or-number"
export LLM_API_KEY="your-api-key"
# Optional
export SIGNAL_CLI_URL="http://localhost:8080"
export LLM_API_URL="https://api.deepinfra.com/v1/openai"
export LLM_MODEL="deepseek-ai/DeepSeek-V3.1"
export LLM_SYSTEM_PROMPT="You are a helpful assistant..."
export PLUGIN_DIR="plugins.d"
export DB_PATH="tron.db"
export TRIGGER_KEYWORD="T"
export MEMORY_MAX_MESSAGES="50"
export MEMORY_MAX_MINUTES="60"
export DAILY_SUMMARY_HOUR="7"A common pattern is to put non-sensitive config in YAML and secrets in environment variables:
# Keep API key out of config file
export LLM_API_KEY="your-api-key"
# Run with YAML config (env var overrides any llm_api_key in YAML)
./bin/tron -config config.yamlTron supports external plugins (shell scripts, Python, etc.) and internal tools (Go-based).
See PLUGINS.md for:
- Using included plugins (
task,ps) - Creating custom plugins
- Plugin configuration
🚧 Work in Progress - The reminder system is not fully functional yet. The core scheduling logic exists but may have bugs or missing features.
The reminder system allows scheduling prompts that execute with full tool access. The scheduler checks for due reminders every minute.
| Format | Example | Description |
|---|---|---|
daily:HH:MM |
daily:08:00 |
Run daily at specified time |
hourly:MM |
hourly:30 |
Run every hour at specified minute |
interval:DURATION |
interval:2h |
Run every N duration (30m, 2h, etc.) |
cron:EXPR |
cron:0 8 * * 1-5 |
Standard 5-field cron expression |
once:DATETIME |
once:2024-01-15T08:00 |
Run once at specified time |
| Action | Description |
|---|---|
list |
Show all configured reminders |
add |
Create a new reminder (requires: name, prompt, schedule) |
delete |
Remove a reminder by ID |
enable |
Enable a paused reminder |
disable |
Pause a reminder without deleting |
run |
Execute a reminder immediately |
Creating reminders (via chat):
"Set a reminder to check my tasks every morning at 8am"
→ Creates: daily:08:00 reminder with prompt to list pending tasks
"Remind me every 2 hours to take a break"
→ Creates: interval:2h reminder
"Every weekday at 9am, summarize my calendar"
→ Creates: cron:0 9 * * 1-5 reminder
How it works:
- The scheduler runs in the background, checking every minute for due reminders
- When a reminder is due, its prompt is sent to the LLM with full tool access
- The LLM executes the prompt (can use plugins, check tasks, etc.)
- The response is sent to the original chat
- The next run time is calculated based on the schedule type
Reminders are stored in SQLite and persist across restarts.
| Target | Description |
|---|---|
build |
Compile binary to bin/tron |
run |
Build and run |
run-debug |
Build and run with debug flag |
clean |
Remove build artifacts |
test |
Run tests |
vet |
Run go vet |
fmt |
Format code |
all |
fmt, vet, test, build |
Once running, the bot:
- Responds to direct messages from the configured operator
- Responds to group messages prefixed with the trigger keyword (default:
T) - Maintains conversation context per chat
- Sends a daily summary at the configured hour