What it does
Telegram was the first way to talk to Vision and is still the one on my phone. The adapter between the two is deliberately thin: it passes my messages to agent core and the answers back, with no model, no database and no records of its own.
It is also where Vision learns who is talking to it. Every message the adapter passes on is signed, and agent core accepts nothing else. This page names those layers and why each exists; the details stay private.
Why this way
Decision 01
A thin adapter
The adapter has no model access, no database and no run records. It passes my messages to agent core and sends the answers back; agent core does everything else and records every message. Replacing Telegram later means replacing only this one small service.
Decision 02
One user, one chat, everything else dropped
A message counts only if it comes from me, in my private chat with the bot. Anything else gets no reply and no call to agent core, and leaves a single log line without sender, chat or text. Only text is accepted for now; voice notes, photos and files get a short “text only” reply and never reach agent core.
Decision 03
Safe behaviour after an outage
Messages from one chat are handled one at a time, so the conversation stays in order, while commands skip the queue, so a stop isn’t stuck behind a slow start. A message older than 15 minutes isn’t run at all, commands included; I get a note that it arrived while the bot was offline and should be sent again. A backlog of paid messages never runs unseen after an outage.
Decision 05
Every message signed, unsigned ones refused
The adapter signs every message it passes on, commands included, with a key that only it holds. Agent core checks the signature against what it knows itself, not against what the request claims, and only a verified message from me in my private chat counts as me. Anything that isn’t a fresh, verified message from me is refused before a run starts or any model is called. The trusted keys are part of the reviewed configuration, so a change of trust shows up in review, and several can be trusted at once so a new key can overlap the old one. Every accepted run gets an origin record that agent core can only add to and read.
Rejected: treating unsigned requests as an untrusted sender in a separate conversation
That was my own first idea. An untrusted sender that still reached the strong model would get my knowledge, file and homelab tools. No legitimate caller sends unsigned requests, so they are simply refused.
How it's tested
A probe after the switch
After signing went live, an unsigned request got a refusal and a refused-run record without its text. My phone tests passed: a message, the status line with its count of origin records, and stop and resume.
The origin audit
The status command checks that every chat run of the last 7 days has a valid origin record, and an audit every 10 minutes logs a warning for any run that doesn’t. Refused requests are recorded too, without their text.
What I learned
Case 01
The sender was just a claim
What happened
To test a new skill, the coding agent sent three messages straight into agent core from inside the system. I had asked for the test, but the messages didn’t come from my phone, and the run records looked exactly like mine. Agent core believed whichever sender a request named.
What I changed
First a hard rule, enforced by the guardrail: the coding agent never sends chat messages, and every test that depends on who I am is sent by me from my phone. Then the signed sender: the adapter signs every message, and agent core refuses anything unsigned before a run starts.
Result
Only a verified message from me counts as me, and every chat run carries a record of where it came from.