Skip to content

gateway

The messaging gateway runs a long-lived daemon that connects Kolega Code to a chat platform, so you can drive your agent from your phone. Today it ships a Telegram adapter (official Bot API via @BotFather); the adapter layer is designed for more platforms later.

Each chat gets its own durable Kolega session. Turns stream into the chat as edit-in-place messages, permission approvals and ask_user_choice questions arrive as inline buttons, and voice notes, images, and documents are handled (the local voice-transcription provider needs the stt extra).

  1. Create a bot with @BotFather and copy its token.

  2. Save it and explicitly authorize your numeric Telegram user ID — either from the CLI (replace the example ID with your own):

    Terminal window
    kolega-code gateway telegram setup --verify --allow '123456789'

    or from the TUI: Settings → Gateway (bot token, allowed users, pairing, permission mode, adapter, and project; voice transcription lives under Settings → Tools). Everything is stored in settings.json like every other key — see Gateway settings.

  3. Run it:

    Terminal window
    kolega-code gateway run --project ~/kolega-code-workspace

Without --project/gateway.project, sessions work in ~/kolega-code-workspace — never the daemon’s launch directory. Message the bot from your phone; unknown senders are dropped (or get a pairing code, see Pairing the first or another sender).

--allow '123456789,987654321' authorizes multiple users. Use numeric IDs, not @handles, display names, or *. The bot token authenticates the bot to Telegram; it does not authorize a person to use your agent.

Token-only setup, including piped input, is a successful configuration save, not proof that anyone can use the bot. When no operators are authorized, setup warns that access is locked (or pairing-only if already enabled) and explains how to supply --allow or enable pairing in Settings → Gateway. Setup never enables pairing implicitly.

  • Omitting --allow preserves the existing configured IDs.
  • Explicit --allow '' clears only gateway.allowed_users. It does not revoke users previously approved through pairing.
  • Settings-based access changes require a gateway restart; saving settings does not automatically restart the service.
Command Description
run Run the gateway in the foreground (graceful stop on Ctrl-C/SIGTERM).
status Show daemon/service health, heartbeat freshness, and the running daemon’s effective access policy.
telegram setup Save the @BotFather token (with optional --verify, --allow <ids>, --clear).
pairing list / pairing approve <code> List pending sender pairing requests, or admit a sender.
install / uninstall / restart Install, remove, or restart the gateway as a user-level background service (systemd user unit / launchd agent — no root needed).

run options: --adapter echo|telegram, --project, --state-dir, --provider, --model.

Running kolega-code update automatically restarts the gateway service if it is installed, so the running daemon picks up new versions immediately. For other upgrade methods or a foreground daemon, explicitly restart the process. Updating files on disk does not harden an old process still running.

The background service launches through your login shell ($SHELL), so the daemon sees the same environment as an interactive terminal — service managers alone provide a minimal PATH that would hide user-installed tools from gateway-driven sessions. Restart the service after changing your shell profile to pick up the new environment.

Startup and kolega-code gateway status report the enforced policy, configured user count, valid paired-user count, and whether pairing is enabled. Status uses the running daemon’s heartbeat, not newly edited settings, to describe what that process enforces:

Policy Meaning
locked No configured or valid paired users, and pairing is disabled. Remote messages cannot use the agent.
pairing-only No authorized users yet; unknown senders can request a code, but cannot use the agent until locally approved.
restricted Only configured or valid paired users are authorized; group restrictions still apply.

A locked daemon can remain running and warns at startup when onboarding is disabled. Older heartbeats without access fields show access policy unknown; restart/update to verify — they are not evidence of safe enforcement. Local echo is identified separately from remote access.

/new, /status, /model [model], /permissions [ask|auto], /stop, /help. The same commands are registered with Telegram so they appear in the slash menu while typing.

Background child jobs keep their chat session alive after the parent turn ends. Their reports are delivered to the chat even while it is idle, and the parent normally continues using the report. These continuations use the same per-chat queue as incoming messages, so parent turns never overlap.

/stop pauses automatic continuation without throwing away child reports. Another ordinary message resumes normal operation. /model preserves the job supervisor and keeps the old generation’s services alive until its children finish. /new cancels and joins old-session work before teardown; late reports are never injected into the new session.

Sessions with active jobs or pending reports are protected from idle and capacity eviction. The live session count can therefore temporarily exceed the configured limit. Daemon shutdown cancels and joins jobs before closing session services. Jobs do not continue executing after the daemon exits.

Remote access is deny-by-default. Authorized sender IDs are the union of gateway.allowed_users in settings.json and valid approvals in gateway_allowlist.json, both in the gateway’s state directory. An empty configured list means paired users only; if neither source authorizes anyone, the gateway is locked with pairing off or onboarding-only with pairing on. There is no implicit public mode, wildcard, or first-message-wins admission.

Unknown senders cannot create or resume sessions, run slash commands, approve tools, change permissions, or trigger attachment downloads, file writes, or transcription. Enabling pairing permits only the onboarding reply, not agent access. Rejected permission-button taps do not consume the legitimate pending prompt and do not generate pairing replies.

Group admission also requires an authorized sender. gateway.group_ids and mention gating restrict where the bot responds; neither grants permission to otherwise unauthorized group members.

Pairing can onboard the first user, with no configured IDs. It is disabled by default and always requires approval from the local machine:

  1. Save the bot token, then enable pairing in Settings → Gateway and apply the change. You may leave allowed users empty.

  2. Start the gateway, or restart it if it was already running, so it loads gateway.pairing_enabled: true.

  3. From the intended Telegram account, DM the bot to receive a pairing code. This does not create an agent session or grant access.

  4. On the local machine, using the same state directory as the daemon, run:

    Terminal window
    kolega-code gateway pairing list

    Review the code, numeric sender ID, display name, channel, and chat. Confirm the code with the intended person through a trusted channel. Display names alone are not proof of identity.

  5. Approve only that confirmed request locally:

    Terminal window
    kolega-code gateway pairing approve <code>
  6. DM the bot again. Persisted approvals are reread dynamically, so the approved user’s next message is admitted without restarting the daemon.

Codes expire after one hour by default (gateway.pairing_code_ttl_seconds). Other unknown senders remain unauthorized. Disabling pairing stops onboarding, but does not revoke existing approvals.

Intentional security change: old deployments that relied on an empty list to admit everyone become locked or pairing-only after restarting with the updated code, unless valid persisted approvals already authorize users. Restart any old daemon; an upgrade on disk alone is insufficient. No existing sessions or approvals are automatically deleted.

To restrict a deployment to one owner:

  1. Stop the gateway before editing access data. For a foreground run, use Ctrl-C. For an installed service, kolega-code gateway uninstall stops and removes the service registration without deleting settings or approvals; check gateway status to confirm it is no longer running.
  2. Inspect both settings.json → gateway.allowed_users and gateway_allowlist.json in that daemon’s state directory. Remove unwanted approval records and set the configured list to only the owner’s quoted numeric ID, such as ["123456789"]. Set gateway.pairing_enabled to false if no further onboarding is wanted. Preserve unrelated settings, tokens, credentials, and any intended approval records.
  3. Restart the foreground run, or use kolega-code gateway install to restore and start the service removed in step 1. Check gateway status for the running policy and counts.

Clearing only one authorization source does not clear the other. In particular, telegram setup --allow '' does not revoke persisted approvals, and removing approvals does not revoke configured users. Removing the final authorization locks access rather than opening it.

Malformed security settings fail with an actionable error naming the field and expected form; they are not silently discarded or replaced with permissive defaults. Repair the identified field in settings.json — do not delete the settings file or credentials:

  • gateway must be a JSON object when present.
  • gateway.allowed_users and gateway.group_ids must be lists of nonempty strings. For Telegram, use positive ASCII-decimal user IDs and signed nonzero ASCII-decimal group IDs, for example ["123456789"] and ["-1001234567890"]. JSON numbers, booleans, scalar strings, blank entries, handles, and wildcards are invalid.
  • gateway.pairing_enabled must be the JSON boolean true or false, not a string such as "false".

Missing keys and empty lists are valid safe settings; surrounding ID whitespace is trimmed and duplicates are removed. Invalid CLI/TUI input is rejected before saving, including before replacing a token. After repairing settings, restart the gateway to load the correction.

Voice notes transcribe remotely through the provider selected in the settings TUI under Tools → Voice transcription:

  • groq uses hosted whisper-large-v3-turbo by default and reuses the Groq API key stored on the Providers page (or GROQ_API_KEY).
  • openai uses gpt-transcribe by default and reuses the OpenAI API key stored on the Providers page (or OPENAI_API_KEY).

stt_enabled, stt_provider, and stt_model live at the top level of settings.json. Audio up to 25 MB is uploaded to the selected provider’s transcription endpoint for the request and never stored by Kolega Code. Transcription is remote-only — there is no local Whisper backend.

If session startup or cleanup stalls, the request stops waiting but cleanup remains owned. Other chats can continue; the affected chat cannot start a turn, reset, or switch models until the cleanup operation returns. /stop does not cancel cleanup. A cleanup failure reported to the gateway keeps the chat fenced and is not automatically retried.

Some lower-level resource managers log or suppress cleanup errors. A successful return from cleanup therefore does not prove every underlying resource closed.

Shutdown stops admission before draining sessions. If the bounded graceful wait expires, gateway status reports shutdown as incomplete, not stopped. The process retains its adapter and instance lock while cleanup is live. The foreground launcher exits unsuccessfully if shutdown is incomplete and has a process watchdog for work that cannot drain; the watchdog forcibly exits and can interrupt cleanup or persistence. It never treats a timeout as clean shutdown. Inspect the gateway log before restarting after a cleanup failure.

  • Groups are mention-gated (@yourbot) and optionally restricted to gateway.group_ids.
  • The echo adapter (--adapter echo) drives the full transport pipeline over stdin/stdout without any LLM or Telegram account — handy for testing. Only the actual local echo adapter’s standard owner sender is admitted without an explicit configured allowlist; explicit lists retain their restrictions and custom echo sender IDs must be authorized. This local console allowance never applies to Telegram or arbitrary adapters.
  • The Telegram connection uses the official Bot API with a BotFather token, never a personal user account.