contenox
Browse docs/

The MVP: Telegram and GitHub bots on one chain engine

contenox/mvp treated “Co-Pilots” and “Bots” as the same problem wearing two faces. One Go service split into a standalone runtime — the model-orchestration and task-engine kernel, published as its own container image and Go module — and a gateway layered on top carrying everything MVP-specific: users and JWT auth, a Postgres-backed job queue with lease-based dispatch, a Vald-backed RAG pipeline over uploaded documents, and two live bridges out to the world. A React admin dashboard, proxied through the same gateway, let an operator upload documents, wire up bots, and watch jobs move.

The two bridges

The bridges shared everything except the wire format on either end. A Telegram poller ticked against every registered bot frontend, called getUpdates, and turned each message into a job keyed to a per-chat-per-user subject; the job’s payload carried a chain ID assigned to that frontend, so processing it meant loading that chain, replaying the subject’s stored chat history through it, and sending the model’s reply straight back over the Bot API. Independently, a GitHub worker polled every connected repo’s open PRs, diffed new comments against what was already stored for that PR, and turned each new human comment into its own job — while a brand-new PR, or a comment containing /review, additionally queued a full-diff code-review job. Both landed on bot records carrying their own TaskChainID, ran through the identical envService.Execute call, and posted back through PostComment with a small “Generated by contenox” footer. Same lease-based Postgres queue, same chain-execution call, two unrelated integrations plugged into it — and neither needed an inbound webhook: both bridges ran outbound-only, polling on a tick, deployable behind a firewall with nothing but a bot token or a PAT in the environment.

The stack

The full shape was two Postgres databases (one behind the extracted runtime, one for the gateway’s own bots, jobs, users and chat history), NATS for pubsub, Valkey for KV state, a gRPC tokenizer microservice, Vald for the document vector index, and model routing to Ollama, vLLM, OpenAI, or Gemini through a pluggable provider library. The chains were the same shape they are today — named tasks, typed handlers, branching transitions on operators like > and equals, hook calls and model-execution steps chained together and addressed by ID — running a moderation classifier ahead of the model call on every turn, Telegram and GitHub alike. The runtime was a separately versioned dependency (github.com/contenox/runtime, tagged v0.0.51) that the gateway imported like any third-party library.

Problems it solved

  • Forked bot logic. One dispatch substrate drove a real-time Telegram chat and an autonomous GitHub PR watcher; the only bot-specific code turned an external event into a job and a job’s result into a reply.
  • A noisy review bot. The GitHub bot diffed comments against stored per-PR history before acting, ran a full-diff code review on every new PR, and let a human retrigger one with /review — all polling-based, no webhook required.

The review that reviewed itself

The GitHub bot’s own implementation arrived as a pull request. Posting /review on that pull request triggered the platform to launch a code-review bot against the code that created it: minutes later the bot had posted an analysis of its own implementation — naming the components, describing the architecture, and flagging issues in the change that had just introduced it.

A pull request comment reading /review, answered minutes later by contenox-bot with a structured analysis of the GitHub Code Review Processor — the very feature that pull request introduced

Related: the chain format this MVP ran is the one contenox executes today, and its two bridges became the shape of contenox-cli mcp add and contenox-cli tools add for tools, and contenox-cli acp for an external driver running a chain over a protocol.

Esc to close