Skip to main content

Connectors and reply targets

Kheish separates transport configuration from output routing. That split is intentional:
  • a connector is a daemon-managed transport resource such as one Telegram bot, one Slack bot, one HTTP webhook definition, or one external sidecar bridge
  • a reply target is one normalized output destination attached to a session, run, or connector
This keeps routing durable without copying transport secrets into every session or run.

Connectors

Connectors live at the daemon level, not inside one session. They own transport-specific configuration such as:
  • ingress behavior such as Telegram polling versus webhook mode
  • connector-local auth such as Slack signing secrets or HTTP bearer tokens
  • transport defaults such as self-output and additional reply targets
  • stable connector identifiers used by ingress routes and structured reply-target requests
Kheish currently supports daemon-managed connectors for:
  • telegram
  • slack
  • http
  • external
The daemon can load connectors from a file at startup and can also create or update daemon-managed connectors through the runtime control plane.

Reply targets

Reply targets are the normalized delivery destinations used by the output pipeline. They are stored as ReplyHandle records:
  • plugin
  • address
The address is transport-specific and opaque to callers when they use raw reply handles. For structured session APIs, the daemon also accepts higher-level request shapes for Telegram, Slack, and HTTP. Examples:
  • one Telegram reply route names a connector and chat or topic coordinates
  • one Slack reply route names a connector and channel or thread coordinates
  • one HTTP reply route names a URL and optional headers
  • one external reply route names a connector plus one opaque sidecar-defined route payload

Where routing lives

Kheish keeps routing at three levels:
  1. connector defaults
  2. session reply-target defaults
  3. run snapshots
Connector defaults are useful for ingress-driven conversations. For example, a Telegram connector can derive a natural self-output destination for replies in the same chat. For external connectors, durable session identity can come from:
  • thread.path
  • routing_key
  • or one connector-level fixed_session_id
If you want many ingress sources to converge on one known session, use the same fixed_session_id across those connectors. Read Multiple input connectors in one session for the exact pattern and tradeoffs. Session reply-target defaults are durable, session-scoped routing preferences stored in session metadata. They are edited through the session control plane and reused for future work in that session. Run reply targets are immutable snapshots captured when concrete work is created. They are used by:
  • normal input runs
  • scheduled input materialized into runs
  • parent clarification flows

Prospective, not retroactive

Changing a session’s reply-target defaults does not rewrite work that already exists. That means a session-level reply-target update affects:
  • future session inputs
  • future background shell tasks created after the change
  • future schedule fires when the schedule request does not already carry explicit reply routing
  • future daemon-owned output that falls back to the session defaults
It does not rewrite:
  • an active run
  • a queued run
  • a suspended run waiting for approval or user input
  • an already materialized run created from a schedule fire
  • an already created background shell task
  • an already queued delivery item
This keeps restart and retry behavior deterministic.

Structured session reply targets

The session control plane accepts structured reply-target requests for the built-in connectors:
  • Telegram: connector plus chat and optional thread or reply ids
  • Slack: connector plus channel and optional thread timestamp
  • HTTP: URL plus optional headers
The daemon validates these requests, normalizes them into reply handles, and persists the normalized form in session metadata. Callers can also submit raw plugin plus address pairs when they need one plugin-specific route that the structured forms do not cover.

Connectors and the secret store

Daemon-managed connectors do not expose raw secret values on read APIs. Instead:
  • connector reads return redacted secret metadata such as configured, source, secret_ref, and env
  • connector writes can point at an existing secret_ref
  • connector writes can also include a write-only secret value, which the daemon stores into the secret store before binding the connector to that slot
This keeps transport configuration inspectable without turning the daemon into a secret reveal service.

Deletion safety

Connectors are not deleted blindly. The daemon rejects connector deletion when the connector is still referenced by:
  • another connector’s configured reply targets
  • a stored schedule payload
When a connector disappears, the daemon also clears now-invalid session reply-target defaults instead of leaving stale durable routing behind.

Recovery model

The runtime connector inventory and session reply-target defaults both survive restart.
  • daemon-managed connectors are stored under the state root in the runtime connector store
  • session reply-target defaults live in session metadata and are also cached in the compact daemon index
On restart, the daemon reloads connectors, re-resolves connector secrets against the secret store, and repairs reply-target cache state from authoritative session metadata when needed. Use this split consistently:
  • connector = transport definition owned by the daemon
  • reply target = concrete routing destination used by one session or run
If you need to change bot tokens, polling mode, signing secrets, or webhook auth, mutate the connector. If you need one session to start replying to a different Telegram chat, Slack thread, or HTTP webhook, mutate the session reply targets. Read Connectors, Output routing, Connectors API, and Security and auth for the operational surface.