Skip to content

Roadmap: Going Public ​

A phased plan to take telegram-bot-essentials/essence (and eventually the rest of the TelegramBotEssentials\* ecosystem) from private, single-developer tooling to a public, MIT-licensed framework aimed at being reliable and easy for outside developers to adopt.

Status: launched 2026-09-02. Phases 1–3 done. essence (v0.12.0) and all 8 companions are public in the Telegram-Bot-Essentials org, renamed to their bare Composer names (tbe-billing → billing, …); skeleton is public too. All 9 packages are published on Packagist as telegram-bot-essentials/*, each org repo carries a Packagist push webhook so new tags auto-publish, and every composer.json repositories entry — the 8 companions and vshop — has been stripped: the whole suite now resolves from packagist.org. vshop staging runs on it. Every repo carries its own LICENSE / CHANGELOG / README, an org .github repo holds the shared health files + reusable CI/release workflows + a Renovate preset, and all 10 main branches are protected behind the ci / ci-passed check. The docs site stays private for now — a dedicated public docs site comes later. Remaining loose ends are listed under each phase; the biggest is installing the Renovate GitHub App on the org.

The database-per-tenant multi-tenancy change is not on this roadmap — it's a planned feature initiative, tracked separately in Database-per-Tenant Migration.

Tag hygiene note: v0.12.0 was moved after vshop first locked to it — the nextInArray()/prevInArray() internalization (see below) landed on main after the first tag and the tag was re-pointed to include it, forcing vshop commit 6118c54 to re-lock. Acceptable this once because the only consumer was vshop and it re-locked immediately; from here on a published tag is immutable — a fix ships as 0.12.1.

Current State (facts, not opinions) ​

  • essence repo: public at github.com/Telegram-Bot-Essentials/essence, main tagged v0.12.0 (2026-09-02). LICENSE (MIT) and composer.json's license field match. Pest suite green, GitHub Actions CI, PHPStan at level max with a committed baseline, CHANGELOG (starts at 0.12.0, "first public release"), CONTRIBUTING/CODE_OF_CONDUCT/SECURITY, issue/PR templates. Elyar0/telegram-bot-essentials is the retired source (pushed into the org's placeholder repo rather than transferred, so there is no redirect from it).
  • The Telegram-Bot-Essentials org now holds all 10 repos public: essence, the 8 companions, and skeleton. Private vulnerability reporting and secret scanning are on for each; each has a description and laravel/telegram/telegram-bot/php topics.
  • All 8 companions are public, released, and on Packagist: billing 0.0.32, settings 0.0.17, gateway-card 0.1.4, gateway-zibal 0.0.11, user-wallet 0.0.22, affiliates 0.0.16, user-management 0.0.16, announcements 0.0.12. Each locks essence to ^0.12. Companions and vshop resolve the whole suite from packagist.org — no VCS repositories entries remain for telegram-bot-essentials/*.
  • laravel/telescope is no longer a hard runtime dependency — moved to suggest/require-dev, the Telescope::tag() call is guarded with class_exists().
  • elyar/personal-laravel-helpers is gone entirely. It was not dead weight — auditing found 20 live call sites of its apiResponse() helper — so it was internalized as essence's own ApiResponse service and tbeApiResponse() helper rather than just dropped.
  • Each companion's integration branch (chore/locale-lazy-handlers on the six with handler classes to migrate, chore/workbench-setup on gateway-card/gateway-zibal) is merged, released, and public, each with its own LICENSE, CHANGELOG.md, and README (see Phase 2).
  • Laravel Workbench is now set up on essence, all 8 companions, and skeleton. essence ships a reusable TelegramBotEssentials\Essence\Testing\TestCase (Testbench-based, Http::fake() on by default so no Telegram call is ever real) that every companion extends. Pest + Pint + Larastan (level max, generated baseline) + CI are now in on essence and all 8 companions — see Testing. The phpstan-bootstrap.php recursion-stub workaround has been removed everywhere now that essence's ExceptionHandler guards its fallback path.
  • The forked SDK, Elyar0/telegram-bot-sdk, is public and stays under Elyar0/ — essence itself requires upstream irazasyed/telegram-bot-sdk ^3.15, not the fork; only vshop uses the fork branch, so it is not part of the framework and was not moved.
  • essence-docs (this site) stays private for now. A dedicated public docs site is a later task. Its install instructions already reference the Telegram-Bot-Essentials org.
  • The two bugs originally written up in SDK Choice & Structure Review are fixed, but not quite as first scoped:
    • encodeCallback() now throws (CallbackDataTooLong) instead of silently truncating on 64-byte overflow — it fails loud rather than auto-routing through StateData, since a silent reroute would hide the same problem one layer down.
    • The webhook controller no longer rescans and re-registers every handler on every request — but "load once per worker boot" alone would have broken per-bot localization on Octane's persistent workers. Handlers now hold translation keys instead of translated strings and resolve them lazily via __() at read time, so one registered instance is correct in every locale. See Extending for what this means for handler authors.

Decisions Locked ​

QuestionDecision
Scopeessence launches first; the rest of the suite opens in phases afterward
LicenseMIT (dropping the currently-declared, never-actually-applied GPL-3.0)
BrandingKeep "Telegram Bot Essentials" naming as-is — no rename
Test frameworkPest, covering bus dispatch, encode/decode round-trips, MessageMeta lifecycle, tenancy scoping
Static analysis / stylePHPStan/Larastan + Laravel Pint in CI
GovernanceOpen to PRs, solo maintainer — CONTRIBUTING.md, issue/PR templates, CODE_OF_CONDUCT.md
GitHub orgMove to a dedicated org (telegram-bot-essentials — already implied by existing docs) before public launch
Version targetNo v1.0 promise yet. Public launch ships as 0.12.0; breaking changes continue to ship as 0.x minors (with a !/BREAKING CHANGE: marker and a CHANGELOG entry) until the API is proven stable enough to commit to a real SemVer/BC contract

Phase 0 — Hygiene & Repo Readiness ✅ done, merged, tagged v0.12.0 ​

Private repo, essence only. No public-facing change yet — the repo is still private. All items below merged to main and shipped in v0.12.0 on 2026-09-02.

  • [x] Add an actual LICENSE file (MIT), fix composer.json's license field to match
  • [x] Internalize the elyar/personal-laravel-helpers dependency rather than drop it — it had 20 live call sites, not zero
  • [x] Make laravel/telescope optional — moved to suggest/require-dev, the Telescope::tag() call is guarded with class_exists(Telescope::class)
  • [x] Fix encodeCallback()'s silent overflow — it now throws CallbackDataTooLong instead of shipping a broken button, and separately throws InvalidCallbackParam on a non-scalar param instead of silently reordering later params
  • [x] Fix bus registration re-scanning app/Telegram/** on every request — handlers now hold translation keys and resolve them lazily, registered once per worker in TelegramBotServiceProvider. Essence's built-ins register in boot(), the consuming app's app/Telegram/** registers afterward in booted(), so app handlers win any collision with essence or a companion package (previously essence, scanned last, silently won)
  • [x] Add a Pest test suite: 58 tests covering bus dispatch/registration, encode/decode round-trips, locale resolution, pruning, route opt-out, update_id dedup, and more
  • [x] Add GitHub Actions CI: PHP 8.3/8.4/8.5 × Laravel 12/13 running Pest, plus Pint and PHPStan (level max, with a committed baseline) as a separate job. (Not 8.2 — Laravel 13 itself requires PHP ^8.3, so an 8.2×13 cell would never resolve. essence's own PHP floor was raised to 8.3 to match.)
  • [x] Add CHANGELOG.md (Keep a Changelog format, starting at [Unreleased] — nothing before this point is tracked)
  • [x] Add CONTRIBUTING.md, CODE_OF_CONDUCT.md, SECURITY.md (vulnerability reports route through GitHub's private security-advisory flow, not a personal email)
  • [x] Add GitHub issue templates (bug report, feature request) and a PR template
  • [x] Rewrite composer.json's description field
  • [x] Improve README.md: CI badge, license badge, a "why essence?" positioning blurb, and corrected three facts that had gone stale mid-refactor (PHP floor, license, per-request handler registration)

Found and fixed along the way, not in the original scope:

  • TelegramBotEssentials\Essence\Testing\TestCase — a shared Testbench base for the whole ecosystem, so every companion tests through the real route/middleware/controller stack with Http::fake() on rather than hand-rolled SDK mocks. Plus a Laravel Workbench setup on every package for interactive development, and tbe:make:* generators that write into a package's own src/ when run under Testbench.

  • Prunable on StateData/InlineConfirmation/MessageMeta, scheduled hourly via model:prune, replacing an ad-hoc cleanup query that ran on every InlineConfirmation accept/decline. state_data gained timestamps() so the age check has something to read.

  • HandlerContextExpired + StateAnswer::requireStateData()/requireMessageMeta(): a multi-step flow that resumes against a row the pruner already deleted now surfaces a "this step expired" notice and clears the stuck state, instead of dereferencing null and crashing the worker. StateAnswer::stateData() stopped reporting an exception as a side effect of returning null — a missing row is now the caller's call to make

  • A config flag + prefix to let the consuming app opt out of essence's own routes (tbe-essence.routes)

  • update_id deduplication on the webhook via an atomic Cache::add claim, released on failure so a crashed handler still retries but a slow-yet-successful one is never double-processed (documented: this needs an atomic cache driver — redis/memcached, not array/file)

  • A ResolvesBotLocale extension point: essence binds a default that returns config('app.locale'); tbe-settings rebinds it to read its own settings store. Fixes a real bug found while auditing the handler-registration fix: tbe:set-webhook's per-bot command-menu loop has no webhook request to hang a locale-setting listener off of, so every bot's Telegram command menu was being built in whatever locale the app process happened to be in, regardless of that bot's actual locale. Essence never references tbe-settings — the companion rebinds the contract, essence doesn't depend on it

  • gateway-zibal's payment callback controller called the now-removed apiResponse() global directly without declaring essence as a dependency for it — a latent fatal on every Zibal payment callback once this branch ships. Fixed on main, ahead of everything else here since it didn't need to wait on the rest of the migration

  • Bot-user reachability tracking (shipped in 0.12.0, not in the original plan): a bot_users.status (active/blocked/unreachable) column, telegram_users.deactivated_at for the peer-global "account deleted" state, BotUser::reachability() and the reachable()/withStatus()/deactivated() scopes, and a BotUserStatus service (botUserStatus()) that folds Telegram's my_chat_member push and send-failure errors into one persisted transition plus a BotUserStatusChanged event. Announcements' broadcast loop is the first consumer.

  • nextInArray() / prevInArray() were internalized into essence's own helpers. They came in with elyar/personal-laravel-helpers and were missed in the first pass of that removal — MyServicesFeature and several companions still call them. They landed on main right after the first v0.12.0 tag, which is why that tag was moved (see the tag-hygiene note up top).

Phase 1 — Public Launch ✅ shipped 2026-09-02 as 0.12.0 ​

The public debut. Done. No v1.0 promise here; see "Version target" above.

Done (2026-09-02):

  • [x] Release coordination, integrate-first: essence chore/phase-0-hygiene and all 8 companion integration branches merged to their mains. essence tagged v0.12.0; companions tagged billing 0.0.32 / settings 0.0.17 / gateway-card 0.1.4 / gateway-zibal 0.0.11 / user-wallet 0.0.22 / affiliates 0.0.16 / user-management 0.0.16 / announcements 0.0.12. Each companion locks essence to ^0.12 (the real floor the 0.12 breaking changes warranted — not "any 0.x").

  • [x] vshop staging moved off the branch aliases onto the released constraints (commits e4f299f, 6118c54).

  • [x] Fix the Essence\Services\ExceptionHandler self-recursion. Its fallback catch (Throwable) called wHook()->update()/->api()/->user(), which with no active webhook threw and routed straight back into exceptionHandler()->handle(), looping until the worker OOMed. Now guarded behind wHook()->check(); the per-companion phpstan-bootstrap.php stubs are all removed.

  • [x] essence pushed into the org's placeholder repo, renamed Essence → essence, public. All 8 companions gh repo transfer'd into the org, renamed to bare Composer names (tbe-billing → billing, tbe-gateway-card → gateway-card, …), public. skeleton public. Descriptions, topics, private vulnerability reporting, secret scanning set on all 10.

  • [x] Repointed every composer.json repositories URL from Elyar0/* to the org, verified CI green suite-wide, then published all 9 to Packagist as telegram-bot-essentials/* and stripped the VCS repositories entries entirely from the 8 companions and from vshop — the suite resolves from packagist.org now. vshop keeps only its SDK-fork + marzban-api / pasarguard-api / rebecca-api entries (still private).

  • [x] Each org repo carries a Packagist push webhook, so new tags auto-publish.

  • [x] Elyar0/telegram-bot-essentials archived. The 8 Elyar0/tbe-* paths are GitHub transfer redirects (no repo to archive).

  • [x] CI green on all 10 org repos.

Carried to Phase 2 / later:

  • [x] Swept the docs and essence's README for stale Elyar0/* and pre-Packagist language: README tests badge → org, Packagist version badge added, "Related Packages" lists all 8, the getting-started "Private packages / add a repositories entry" tip removed (the suite is on Packagist now). The Elyar0/telegram-bot-sdk fork references stay — that repo is deliberately not moving.

  • [x] skeleton: chore/workbench-setup fast-forwarded into main and the branch deleted; dropped its VCS repositories entry, moved the essence constraint off the 0.12.x-dev alias to ^0.12, fixed the stale README link. Deferred by decision (2026-09-02):

  • [ ] Public docs site (separate from this private one) — will be its own project later; essence-docs stays private for now, README doc-links point at "the documentation site".

  • [ ] Optional demo bot repo — on the backlog, not now.

  • [ ] Announce (Laravel News, r/laravel, Telegram/Laravel communities) — a manual step for whenever the docs site is public.

Phase 2 — Per-Package Hygiene ✅ mostly done ​

The companions went public and onto Packagist with essence rather than one-at-a-time, so "phased opening" collapsed into a shared cleanup backlog. Test suite + Pint + Larastan + CI were already done and released for all 8.

  • [x] LICENSE (MIT) and CHANGELOG.md (Keep a Changelog, starting at the current released version) — already present in all 8 companions and skeleton; the earlier roadmap entry here was wrong.
  • [x] README for every companion — 7 were missing entirely, announcements had a stale one. All 8 now carry a consistent README: Packagist/tests/license badges, a "what this adds to essence" paragraph, install + minimal usage, a pointer to the docs site.
  • [x] Shared community health files: created the Telegram-Bot-Essentials/.github repo with CONTRIBUTING / CODE_OF_CONDUCT / SECURITY, issue + PR templates, and the org profile README. GitHub applies them to every repo without its own. essence keeps its package-specific CONTRIBUTING.md; its duplicate CoC/SECURITY (with stale Elyar0 URLs) were removed.
  • [x] Introduction now states the packages are on Packagist / MIT / in the org.

Deferred by decision:

  • [ ] README content depth — the companion READMEs are launch-adequate but thin; revisit once the public docs site exists to link into.

  • [x] Synced the docs/modules/*.md pages — "Current version" lines bumped to the tagged releases, the "essence-0.12 integration (unmerged branch)" tips removed, settings / user-wallet / sdk-choice present-tensed.

Phase 3 — Open-Source Repo Automation ✅ mostly done ​

Was: one hand-copied tests.yml per repo (10 copies), manual tagging, no dependency or security automation. Now centralized in the Telegram-Bot-Essentials/.github repo.

Done:

  • [x] Reusable CI. .github/workflows/ci.yml in the org .github repo holds the whole PHP 8.3/8.4/8.5 × Laravel 12/13 matrix plus Pest / Pint / PHPStan / composer validate --strict, behind a single ci-passed gate job. Every package's tests.yml is now four lines: uses: Telegram-Bot-Essentials/.github/.github/workflows/ci.yml@main. All 10 repos migrated, all green.

  • [x] --prefer-lowest cell in the matrix (PHP 8.3 / Laravel 12), continue-on-error for now — informative until the declared floors are audited suite-wide, then flip to blocking.

  • [x] Release automation (option A). .github/workflows/release.yml (reusable) + a release.yml in each repo that, on a pushed v* tag, creates the GitHub Release with generated notes. CHANGELOG stays hand-written for the "why". Backfilled the missing essence v0.12.0 Release from its CHANGELOG.

  • [x] Renovate. Shared preset default.json in the org .github repo (semantic-commit chores, grouped dev-deps, pinDigests for Actions, essence peer constraint pinned off-limits); each repo carries a one-line renovate.json extending it. Needs the Renovate GitHub App installed on the org to activate — see below.

  • [x] Dependabot alerts + dependency graph enabled on all 10 (the advisory feed; Renovate raises the PRs). Dependabot security updates deliberately left off to avoid duelling with Renovate.

  • [x] Branch protection as code on all 10 main branches: ci / ci-passed required, linear history, no force-push, no deletion, conversation resolution required. enforce_admins: false so the solo maintainer can still hotfix directly.

  • [x] CODEOWNERS (* @Elyar0) and .editorconfig (matches Pint) in all 10.

  • [x] Third-party action SHA-pinning is handled by the Renovate preset's pinDigests once the app runs — no separate pass needed.

  • [x] Audited the version floors — --prefer-lowest resolves and passes on all 10 packages, so the matrix cell is now blocking, not continue-on-error.

  • [x] Stale + PR-size workflows: reusable stale.yml (label-only, 90 days, never auto-closes) and pr-size.yml (one size/* label by changed-line count) in the org .github repo, wired into all 10. Tuned conservatively for a near-empty queue.

Still open (all blocked on an external account or a deliberate defer):

  • [ ] Install the Renovate GitHub App on the Telegram-Bot-Essentials org (github.com/apps/renovate) — until then the renovate.json files are inert. Confirm the github>Telegram-Bot-Essentials/.github preset reference resolves (the repo is literally named .github).
  • [ ] Coverage → Codecov: still coverage: none. Needs a Codecov account + token; deferred until someone will actually watch the dashboard.
  • [ ] FUNDING.yml: no sponsorship set up — skip until there is.

Phase 4 — Post-Launch Growth ​

  • [ ] Investigate Telegram Mini Apps / WebApp support — likely an HTTP-validation-layer addition, not a core SDK change (see SDK Choice → Future Feature Fit)
  • [ ] Consider a community channel (GitHub Discussions, or fittingly, a Telegram group)
  • [ ] Watch Nutgram/TeleBot/LaraGram for feature-parity gaps worth closing
  • [ ] Revisit the solo-maintainer governance model once contributor volume justifies a co-maintainer
  • [ ] Seed a "good first issue" backlog to give early contributors somewhere to start