REPOSITORY / ScuroNeko/Laniakea

Wiki

KNOWLEDGE REPOSITORY
2
Webhook Runtime
ScuroNeko edited this page 2026-05-20 13:19:27 +03:00

Webhook Runtime

Russian version: Webhook-Runtime-RU

This page explains the bot-level webhook runtime in Laniakea: how RunWebhookWithContext(...) works, what it owns, how it differs from low-level tgapi webhook calls, and what runtime guarantees it shares with polling mode.

Important naming note:

  • the current public API uses the historical WebHook spelling in identifiers such as RunWebhookWithContext(...), RunWebhook(...), and BotWebhookOpts;
  • this page uses the more common English term "webhook" for readability, but examples keep the actual Go API names.

When to use webhook runtime

Use webhook runtime when Telegram should push updates to your bot over HTTP instead of your bot pulling updates through long polling.

This is usually a good fit when:

  • your bot runs behind a stable public HTTPS endpoint;
  • you already have reverse-proxy or ingress infrastructure;
  • you want Telegram to deliver updates directly instead of keeping a polling loop open.

Use polling when:

  • you want the simplest local or small-server setup;
  • you do not want to expose an incoming HTTP endpoint;
  • you do not need webhook-style deployment.

Entry points

The main bot-level entry points are:

  • RunWebhookWithContext(ctx, opts, tlsFiles...)
  • RunWebhook(opts, tlsFiles...)
  • NewBotWebhookOpts()

RunWebhook(...) is only a shorthand for RunWebhookWithContext(context.Background(), ...).

The usual pattern looks like:

ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()

bot, err := laniakea.NewBot[*App](opts)
if err != nil {
	return err
}
defer bot.Close()

bot.SetAppData(app)
bot.AddPlugins(plugin)

webhookOpts := laniakea.NewBotWebhookOpts().
	SetURL("https://bot.example.com").
	SetPath("/telegram").
	SetLocalPort(8080).
	SetSecretToken("shared-secret")

if err := bot.RunWebhookWithContext(ctx, webhookOpts); err != nil {
	return err
}

What the bot-level runtime owns

RunWebhookWithContext(...) is more than a wrapper around Telegram's setWebhook.

It:

  • validates bot startup preconditions such as prefixes and registered plugins;
  • keeps the same single-use runtime model as polling;
  • starts registered runners;
  • configures Telegram webhook delivery;
  • starts a local HTTP server owned by the bot;
  • accepts incoming updates and feeds them into the same internal queue and worker pool used by polling;
  • shuts down gracefully when ctx.Done() fires.

This means webhook mode is part of the main runtime model, not just a low-level transport setting.

Runtime guarantees shared with polling

Webhook runtime shares the same main guarantees as RunWithContext(...):

  • the bot is single-use;
  • updates go through the internal queue;
  • BotOpts.MaxWorkers still controls concurrent handler execution;
  • runners start at runtime entry, not at NewBot(...);
  • shutdown drains already accepted work before returning;
  • Close() is still required afterward to release local resources.

If you already understand Bot-Lifecycle, the webhook mode should feel like a different update ingress path, not a second framework.

Main webhook options

BotWebhookOpts controls both Telegram webhook registration and the local server behavior.

Fields you will care about first:

URL

This is the public base URL Telegram will call.

If:

  • URL == "https://bot.example.com"
  • Path == "/telegram"

then Telegram is registered with:

  • https://bot.example.com/telegram

URL is required.

Path

This is the local HTTP path the bot serves.

Use it to keep webhook traffic off / and make reverse-proxy routing explicit.

LocalPort

This is the local server port the bot binds to.

Common deployment pattern:

  • public TLS terminates at a reverse proxy;
  • the bot itself listens on an internal HTTP port such as 8080.

SecretToken

This is the shared secret expected in Telegram's X-Telegram-Bot-Api-Secret-Token header.

You should almost always set it.

Without it, the endpoint still works, but the bot has to trust that only Telegram reaches that path.

AllowedUpdates

If you call SetAllowedUpdates(...) explicitly, those update kinds are registered for webhook delivery.

If you leave it empty, Laniakea falls back to the bot's configured update types from:

  • SetUpdateTypes(...)
  • AddUpdateType(...)

That keeps webhook mode aligned with the rest of your bot configuration by default.

MaxConnections

This maps to Telegram's webhook max_connections setting.

Laniakea currently enforces the Telegram 1..100 range before startup.

DropPendingUpdates

Use this when you want Telegram to discard already queued updates while replacing the webhook.

This is a deployment decision, not a normal runtime requirement.

Certificate

Set this when you need to upload a self-signed certificate to Telegram.

The bot then uses uploader-based webhook registration internally.

UseStatusPath

If enabled, the bot also serves /status, which returns the current Telegram webhook info as JSON.

This endpoint requires a non-empty SecretToken. Laniakea rejects startup if you enable /status without setting one.

Treat this as an operational endpoint, not a public user-facing route.

IPAddress

Use this only when you specifically need Telegram's ip_address webhook option.

HTTP and TLS behavior

By default, RunWebhookWithContext(...) starts a plain HTTP server on LocalPort.

If you pass two TLS files, it starts HTTPS locally instead.

Important:

  • the current public API expects the existing key-then-cert argument order when calling RunWebhookWithContext(...);
  • that differs from the more common cert, key mental model many Go developers expect from ListenAndServeTLS.

Be explicit in your own setup code so this does not become a deployment footgun.

Request handling behavior

The built-in webhook server currently:

  • accepts only POST;
  • optionally validates X-Telegram-Bot-Api-Secret-Token;
  • decodes the incoming Telegram Update;
  • enqueues the update into the normal bot runtime;
  • returns 200 OK after successful enqueue.

If enqueue fails because runtime is shutting down, the server returns 503.

The important design point is that webhook mode does not run handlers inline inside the HTTP request. It hands accepted updates to the same queue and worker pool model used elsewhere.

Security and exposure guidance

At minimum:

  • set SecretToken;
  • keep the webhook path specific, not guessable by accident;
  • expose /status only when you actually need it;
  • prefer putting the bot behind a real reverse proxy or ingress layer.

Also keep in mind:

  • URL is what Telegram sees;
  • Path and LocalPort are what your bot actually serves;
  • these are often not the same thing in production.
  • if you switch a running deployment from webhook mode to polling, delete the webhook first with CloseWebhook() or tgapi.DeleteWebhook(...); Telegram does not stop webhook delivery automatically.

Relation to tgapi webhook methods

Use bot-level webhook runtime when you want Laniakea to own:

  • webhook registration;
  • the local HTTP server;
  • update ingestion into the normal runtime queue;
  • worker-pool dispatch and shutdown behavior.

Use lower-level tgapi calls such as:

  • SetWebhook(...)
  • DeleteWebhook(...)
  • GetWebhookInfo(...)
  • Uploader.SetWebhook(...)

when you need custom infrastructure around the webhook path and do not want the bot to own the HTTP server itself.

In other words:

  • RunWebhookWithContext(...) is the framework runtime API;
  • tgapi webhook methods are the lower-level transport primitives.

Common mistakes

  • Forgetting that webhook mode is still single-use per bot instance.
  • Forgetting to call Close() after runtime returns.
  • Switching from webhook mode to polling without deleting the webhook first.
  • Mixing up public URL with local Path and LocalPort.
  • Omitting SecretToken in production-style deployments.
  • Assuming TLS file order follows the usual cert, key convention.
  • Treating /status like a harmless public endpoint.