Skip to content

Messenger: one-run setup on any glibc 2.34 Linux, sessions from the phone, machine names, approvals in plain words - #422

Merged
hmaximilian merged 14 commits into
mainfrom
feat/messenger-easy-setup
Oct 26, 2026
Merged

hmaximilian merged 14 commits into
mainfrom
feat/messenger-easy-setup

Conversation

@hmaximilian

Copy link
Copy Markdown
Collaborator

The messenger now sets itself up in one run on any Linux with systemd and glibc >= 2.34. The phone can start sessions, every chat shows which machine it comes from, and an approval says in plain words what it would do.

sudo bash scripts/pi/pi_messenger.sh --whisper --pair --name=Pi

Setup (scripts/pi/pi_messenger.sh)

Before this, it could not have been run on the Pi:

  • Platform. Only Ubuntu was recognised, so Raspberry Pi OS was refused.

    • Ubuntu 24.04 keeps its own build.
    • Every other Linux with glibc >= 2.34 takes the Ubuntu 22.04 build: Raspberry Pi OS, Debian 12/13, RHEL 9. That build needs glibc 2.34, OpenSSL 3, libgmp and zlib (ldd/objdump of the pinned binary).
    • An older glibc is refused before any download.
  • Bot profile. The daemon used to stop at a name prompt on stdin. It now starts with --create-bot-display-name DELFIN-<label> --create-bot-allow-files. Measured with the pinned v7.0.3 binary: the first start creates the profile, the second keeps it.

  • Gateway venv. It used to be a manual step. The script now:

    • makes the venv from Python 3.11 (MESSENGER_PYTHON, uv, or python3.11);
    • installs websockets 17.3 from the new messenger-requirements.txt with --require-hashes --no-deps;
    • adds a root-owned copy of the delfin package through a .pth file. Nothing is built, and nothing unhashed is fetched.

    Checked for real here: pip installs with the hashes, and the gateway and bridge import from the copy.

  • Bridge. With ProtectHome=read-only it could write nothing in the service home, so phone files and /sync records failed. It now gets ReadWritePaths=<service home>; every other home stays read-only.

  • Agent service. New unit delfin-agent-service. It uses the user's ~/delfin/.venv and agent.env, with KillMode=mixed so sessions are saved on stop. The bridge has a service without a terminal open.

  • Re-runs. A re-run restarts the daemon, bridge and gateway (new code, new unit lines). The agent service is never restarted, so running sessions stay.

  • New options.

    • --pair: pairs the phone at the end. The invitation is also drawn as a QR code when qrencode is installed.
    • --name=LABEL: names the machine (default: the short host name).
    • --no-phone-sessions: turns off sessions started from the phone.

New session from the phone: neu NAME[: task]

  • Bridge command create. The phone sends a name and nothing else:
    • The folder is ~/DELFIN-Handy/<name>. The name is reduced to [A-Za-z0-9._-], with no leading dot and at most 40 characters.
    • An existing entry is reused only if it is a real directory, never a symlink.
    • Folder, model, mode and permission fields from the gateway are refused.
  • Permission profile default, whatever is configured: the session asks before commands and edits.
  • The session is enabled for the messenger right away and its chat follows.
  • At most 3 starts per 10 minutes. Without DELFIN_MESSENGER_WORKSPACES set, every start is refused.
  • Checked against a real private agent service: the folder is created, the session starts and is enabled.

Machine name in every chat

  • DELFIN_MESSENGER_LABEL appears in the bot name, in every session chat (DELFIN-Pi Butan …) and in the forum (DELFIN Forum Pi).
  • name NAME keeps the machine label when it renames a chat.
  • The label is display only. Rooms stay bound by chat ID and session ID.

Approvals in plain words

  • permission.asked carries purpose: the tool call's own one-line description (bash requires one).
    • It is masked by the line masking and by the service redactor. The line masking alone missed a bare API key.
    • One line, at most 200 characters.
  • The phone shows three parts, in this order:
    1. What the action is: a command on DELFIN-, a file change, or a read outside the folder.
    2. Angabe des Agenten (ungeprüft): … — the agent's own statement, marked as not checked.
    3. The full command. The purpose never replaces it.

Gate (py3.11 delfin-ci, -m "not slow")

  • The 95 test files that use the messenger, the bridge, the agent service, the approvals or the Pi scripts: 2293 passed.
  • CI lint rule set: clean.
  • New tests, each committed red before its fix:
    • test_a_session_can_be_started_from_the_phone
    • test_the_messenger_sets_itself_up_in_one_run
    • test_the_pairing_link_is_shown_as_a_qr_code
    • test_every_chat_names_the_machine_it_comes_from
    • test_a_phone_approval_says_what_it_would_do

Not verified: a live run on the Pi or a phone. It is planned on the Pi, the server and the login node. The aarch64 binary on Raspberry Pi OS has been checked by its library needs only, not started.

…m the phone

Red before the change: Debian/Raspberry Pi OS refused, no bot profile,
no gateway venv, the bridge unable to write the service home, no agent
service unit, no create command in the bridge, no neu in the gateway.
messenger-requirements.txt pins websockets 17.3 with every PyPI digest
(hashed_requirements.py).
Bridge command create: the phone supplies a name only. The folder is
DELFIN_MESSENGER_WORKSPACES/<name> (name reduced to [A-Za-z0-9._-], no
leading dot, 40 characters; an existing entry is reused only as a real
directory, never through a symlink). The session starts with the
permission profile default, whatever the configured one is, and is
enabled for the messenger so its chat follows without /sync at the
computer. Unset root: refused. At most three starts per ten minutes.

Gateway: neu NAME[: task] calls create, sets up and invites to the
session's chat, and sends the task as the first prompt.
With qrencode on PATH the invitation is also drawn in the terminal
(-t ansiutf8, the link as one argument, no shell); without it the link
is printed with a note naming the package.
…inux

- Platform: Ubuntu 24.04 keeps its build; any other Linux with glibc
  >= 2.34 (Raspberry Pi OS, Debian 12/13, RHEL 9) takes the Ubuntu
  22.04 build, which needs glibc 2.34, OpenSSL 3, libgmp and zlib (ldd
  and objdump of the pinned v7.0.3 binary). Before, only Ubuntu was
  recognised and Raspberry Pi OS was refused.
- The daemon creates its bot profile (--create-bot-display-name DELFIN
  --create-bot-allow-files); before, it asked for a name on stdin and
  stopped. Both first and second start measured with the pinned binary.
- The gateway venv is made from Python 3.11 (MESSENGER_PYTHON, uv, or
  python3.11), filled from messenger-requirements.txt with
  --require-hashes --no-deps and a root-owned copy of the delfin package
  through a .pth file: nothing built, nothing unhashed fetched.
- The bridge may write the service user's home (ReadWritePaths); with
  ProtectHome=read-only alone, files from the phone and /sync records
  could not be written.
- delfin-agent-service.service runs the service with the user's DELFIN
  venv and agent.env, so the bridge has a service without a terminal;
  KillMode=mixed lets it save its sessions. The bridge carries
  DELFIN_MESSENGER_WORKSPACES=~/DELFIN-Handy (0700) unless
  --no-phone-sessions.
- --pair pairs the phone at the end of the run.
enable --now leaves a running unit on its old code and unit lines; an
update re-run copies new code and the daemon gained flags. The agent
service is only enabled: a restart would end its running sessions.
DELFIN_MESSENGER_LABEL (provisioner: --name=LABEL, else the short host
name; [A-Za-z0-9_-], 24 characters) goes into the bot's display name
DELFIN-<label>, every session chat and the forum. A renamed session
keeps it in its chat name. Display only: rooms stay bound by chat and
session ID. Without a label the names are as before.
permission.asked carries purpose: the tool call's own one-line
description (bash requires one), masked by the preview's line masking
and the service redactor (which also finds a bare key; the preview's
masking alone did not), one line, 200 characters; absent without one.

The bridge passes purpose and the action kind (shell/write/read from
the request's flags). The phone text names the action in plain words
(a command on DELFIN-<machine>, a file change, a read outside the
folder), then the purpose labelled as the agent's unchecked statement,
then the full command, which the purpose never replaces.
bun scripts/gen-protocol.ts (Bun 1.4.3); --check: matches the Python
definition.
@hmaximilian
hmaximilian merged commit c0a78d2 into main Oct 26, 2026
26 checks passed
@hmaximilian
hmaximilian deleted the feat/messenger-easy-setup branch October 26, 2026 23:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant