Signal gateway #
eukhe gateway connects Signal on your phone to the session you use in the CLI.
- A Signal message goes to the newest root session (the one the CLI shows), as a
[signal]row. - The agent replies in short ASD-STE100 English, to Signal. Signal shows "typing" while it works.
- Typing in the CLI keeps replies in the CLI; the next Signal message routes them to Signal again.
- Daemon stopped by
eukhe shutdown: the gateway does not restart it; it reattaches when a daemon starts, or starts one at the next Signal message. - Daemon stopped otherwise: the gateway restarts it and reattaches; messages wait.
Setup #
nix profile install nixpkgs#signal-cli # or Home Manager programs.eukhe.gateway.enable (see Service)
eukhe gateway # follow its steps
- No linked account: it shows a QR code. Phone: Signal > Settings > Linked devices > Link new device; scan.
- Service installed: starts the service and exits. Else runs in the foreground (Ctrl+C stops).
- Then send a message to Note to Self. No settings needed; uses the only signal-cli account.
Service #
Home Manager (Linux), starts with the computer:
programs.eukhe.gateway.enable = true;
| Option | Default | Effect |
|---|---|---|
gateway.command |
lib.getExe programs.eukhe.package |
eukhe executable; set when package is null (mise: ~/.local/share/mise/shims/eukhe) |
gateway.signalCli |
pkgs.signal-cli |
signal-cli package |
gateway.path |
[] |
Extra PATH dirs, also for tools of a daemon it starts |
gateway.environment |
{} |
Extra env vars, for the gateway and a daemon it starts |
- Starts only after linking (needs
~/.local/share/signal-cli/data/accounts.json); restarts after a crash. - On reconnect to a daemon not older than its build (an upgrade), the gateway exits; the service restarts it on the new build. It never replaces a daemon.
- Boot start needs linger:
loginctl enable-linger $USER.
journalctl --user -u eukhe-gateway -f # log
Other people (dedicated number) #
Your own number: only Note to Self works. For other senders, give the gateway a second number:
signal-cli -a +15557654321 register # captcha asked: add --captcha <token> (https://signalcaptchas.org/registration/generate.html)
signal-cli -a +15557654321 verify 123456 # SMS code
Then add senders to allow; with two accounts also set account.
Settings #
Optional, global only (~/.eukhe/settings.json); project settings cannot set gateway. Home Manager: programs.eukhe.settings.gateway (activation replaces ~/.eukhe/settings.json).
{
"gateway": {
"signal": {
"account": "+15557654321",
"allow": ["+15551234567"],
"cliPath": "signal-cli",
"cwd": "~/work",
"model": "elpis-fast/qwen3.8-27b"
}
}
}
| Key | Default | Effect |
|---|---|---|
account |
The only signal-cli account | signal-cli account |
allow |
[] |
Numbers or ACIs allowed to send; Note to Self always accepted |
cliPath |
signal-cli on PATH |
signal-cli executable |
cwd |
$HOME |
Folder of a new session |
model |
The session model | Model for Signal messages (provider/model-id, see eukhe model list); /model gateway sets or clears it (settings.md) |
--daemon-socket <path>: daemon on another socket.
Model #
model runs Signal messages on one model, e.g. a local one.
- Before each Signal message, the gateway switches the session to
modelif different; the shared CLI then uses it too. - The switch also makes
modelthe default, as thechatrole of CLI/modeldoes. - Switch fails (unknown model, provider not signed in): message not sent; Signal gets
Error: .... /statusshows the session's model.
Phone commands #
Whole message, case-insensitive.
| Command | Effect |
|---|---|
/help |
Show the commands |
/sessions |
9 newest root sessions: number, name or first message, folder, age; * marks the open one |
/use N |
Next messages go to session N of the last /sessions list |
/latest |
Next messages go to the newest session (default) |
/new |
New session in cwd, used next |
/stop |
Stop the current turn |
/status |
Session name, folder, model, mode, busy state |
Other text, including session commands such as /compact, goes to the agent.
Replies #
- Signal gets each final agent message: text,
Error: ..., orStopped.; alsoError: ...when a turn fails before the model answers (e.g. no model set). - No thinking, tool calls, or tool output.
- What each settled event tells the phone (text, error line, stop, nothing) runs as the compiled core:
bend/gateway_answer.bend(entrybend/ffi_gateway_answer.bend, lawslaws/gateway_answer.bend); Rust only reads the event. - Later turns (e.g. after a subagent report) also reply to Signal, until you type in the CLI.
Security #
Warning
A Signal message runs tools on this machine with your user permissions. No approval step: with only the gateway attached, each step that would ask for approval runs at once.
- Only Note to Self and
allowreach the agent; other senders are ignored. - Steps that write eukhe's own files (config, credentials, a
.gitor.eukhedir, aPATHdir) or read or write a secret (ssh keys, credential stores) never run from the phone: the agent gets an immediate refusal and can ask you to run it from the TUI or dashboard. If the TUI or dashboard is attached to the session, it gets the approval question instead (docs/tools.md G5). - Keep
allowshort: each number can run commands here. - Lock the phone screen: an open phone can use the agent.
- Network: only signal-cli to the Signal service, only while
eukhe gatewayruns.
Limits #
- Images only, at most 4 per message; other attachments are ignored and the agent is told.
- Group messages ignored.
- One session at a time for all senders.
- Replies over 2000 characters arrive split.