Appearance
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)
essencerepo: public atgithub.com/Telegram-Bot-Essentials/essence,maintaggedv0.12.0(2026-09-02).LICENSE(MIT) andcomposer.json'slicensefield 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-essentialsis the retired source (pushed into the org's placeholder repo rather than transferred, so there is no redirect from it).- The
Telegram-Bot-Essentialsorg now holds all 10 repos public:essence, the 8 companions, andskeleton. Private vulnerability reporting and secret scanning are on for each; each has a description andlaravel/telegram/telegram-bot/phptopics. - All 8 companions are public, released, and on Packagist:
billing0.0.32,settings0.0.17,gateway-card0.1.4,gateway-zibal0.0.11,user-wallet0.0.22,affiliates0.0.16,user-management0.0.16,announcements0.0.12. Each locksessenceto^0.12. Companions and vshop resolve the whole suite from packagist.org — no VCSrepositoriesentries remain fortelegram-bot-essentials/*. laravel/telescopeis no longer a hard runtime dependency — moved tosuggest/require-dev, theTelescope::tag()call is guarded withclass_exists().elyar/personal-laravel-helpersis gone entirely. It was not dead weight — auditing found 20 live call sites of itsapiResponse()helper — so it was internalized as essence's ownApiResponseservice andtbeApiResponse()helper rather than just dropped.- Each companion's integration branch (
chore/locale-lazy-handlerson the six with handler classes to migrate,chore/workbench-setupongateway-card/gateway-zibal) is merged, released, and public, each with its ownLICENSE,CHANGELOG.md, and README (see Phase 2). - Laravel Workbench is now set up on essence, all 8 companions, and
skeleton. essence ships a reusableTelegramBotEssentials\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. Thephpstan-bootstrap.phprecursion-stub workaround has been removed everywhere now that essence'sExceptionHandlerguards its fallback path. - The forked SDK,
Elyar0/telegram-bot-sdk, is public and stays underElyar0/— essence itself requires upstreamirazasyed/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 theTelegram-Bot-Essentialsorg.- 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 throughStateData, 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
| Question | Decision |
|---|---|
| Scope | essence launches first; the rest of the suite opens in phases afterward |
| License | MIT (dropping the currently-declared, never-actually-applied GPL-3.0) |
| Branding | Keep "Telegram Bot Essentials" naming as-is — no rename |
| Test framework | Pest, covering bus dispatch, encode/decode round-trips, MessageMeta lifecycle, tenancy scoping |
| Static analysis / style | PHPStan/Larastan + Laravel Pint in CI |
| Governance | Open to PRs, solo maintainer — CONTRIBUTING.md, issue/PR templates, CODE_OF_CONDUCT.md |
| GitHub org | Move to a dedicated org (telegram-bot-essentials — already implied by existing docs) before public launch |
| Version target | No 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
LICENSEfile (MIT), fixcomposer.json'slicensefield to match - [x] Internalize the
elyar/personal-laravel-helpersdependency rather than drop it — it had 20 live call sites, not zero - [x] Make
laravel/telescopeoptional — moved tosuggest/require-dev, theTelescope::tag()call is guarded withclass_exists(Telescope::class) - [x] Fix
encodeCallback()'s silent overflow — it now throwsCallbackDataTooLonginstead of shipping a broken button, and separately throwsInvalidCallbackParamon 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 inTelegramBotServiceProvider. Essence's built-ins register inboot(), the consuming app'sapp/Telegram/**registers afterward inbooted(), 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_iddedup, 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'sdescriptionfield - [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 withHttp::fake()on rather than hand-rolled SDK mocks. Plus a Laravel Workbench setup on every package for interactive development, andtbe:make:*generators that write into a package's ownsrc/when run under Testbench.PrunableonStateData/InlineConfirmation/MessageMeta, scheduled hourly viamodel:prune, replacing an ad-hoc cleanup query that ran on everyInlineConfirmationaccept/decline.state_datagainedtimestamps()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 dereferencingnulland crashing the worker.StateAnswer::stateData()stopped reporting an exception as a side effect of returningnull— a missing row is now the caller's call to makeA config flag + prefix to let the consuming app opt out of essence's own routes (
tbe-essence.routes)update_iddeduplication on the webhook via an atomicCache::addclaim, 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, notarray/file)A
ResolvesBotLocaleextension point: essence binds a default that returnsconfig('app.locale');tbe-settingsrebinds 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 referencestbe-settings— the companion rebinds the contract, essence doesn't depend on itgateway-zibal's payment callback controller called the now-removedapiResponse()global directly without declaring essence as a dependency for it — a latent fatal on every Zibal payment callback once this branch ships. Fixed onmain, ahead of everything else here since it didn't need to wait on the rest of the migrationBot-user reachability tracking (shipped in 0.12.0, not in the original plan): a
bot_users.status(active/blocked/unreachable) column,telegram_users.deactivated_atfor the peer-global "account deleted" state,BotUser::reachability()and thereachable()/withStatus()/deactivated()scopes, and aBotUserStatusservice (botUserStatus()) that folds Telegram'smy_chat_memberpush and send-failure errors into one persisted transition plus aBotUserStatusChangedevent. Announcements' broadcast loop is the first consumer.nextInArray()/prevInArray()were internalized into essence's own helpers. They came in withelyar/personal-laravel-helpersand were missed in the first pass of that removal —MyServicesFeatureand several companions still call them. They landed onmainright after the firstv0.12.0tag, 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-hygieneand all 8 companion integration branches merged to theirmains.essencetaggedv0.12.0; companions taggedbilling0.0.32 /settings0.0.17 /gateway-card0.1.4 /gateway-zibal0.0.11 /user-wallet0.0.22 /affiliates0.0.16 /user-management0.0.16 /announcements0.0.12. Each companion locksessenceto^0.12(the real floor the 0.12 breaking changes warranted — not "any 0.x").[x] vshop
stagingmoved off the branch aliases onto the released constraints (commitse4f299f,6118c54).[x] Fix the
Essence\Services\ExceptionHandlerself-recursion. Its fallbackcatch (Throwable)calledwHook()->update()/->api()/->user(), which with no active webhook threw and routed straight back intoexceptionHandler()->handle(), looping until the worker OOMed. Now guarded behindwHook()->check(); the per-companionphpstan-bootstrap.phpstubs are all removed.[x] essence pushed into the org's placeholder repo, renamed
Essence→essence, public. All 8 companionsgh repo transfer'd into the org, renamed to bare Composer names (tbe-billing→billing,tbe-gateway-card→gateway-card, …), public.skeletonpublic. Descriptions, topics, private vulnerability reporting, secret scanning set on all 10.[x] Repointed every
composer.jsonrepositoriesURL fromElyar0/*to the org, verified CI green suite-wide, then published all 9 to Packagist astelegram-bot-essentials/*and stripped the VCSrepositoriesentries 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-apientries (still private).[x] Each org repo carries a Packagist push webhook, so new tags auto-publish.
[x]
Elyar0/telegram-bot-essentialsarchived. The 8Elyar0/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 arepositoriesentry" tip removed (the suite is on Packagist now). TheElyar0/telegram-bot-sdkfork references stay — that repo is deliberately not moving.[x]
skeleton:chore/workbench-setupfast-forwarded intomainand the branch deleted; dropped its VCSrepositoriesentry, moved the essence constraint off the0.12.x-devalias 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-docsstays 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) andCHANGELOG.md(Keep a Changelog, starting at the current released version) — already present in all 8 companions andskeleton; the earlier roadmap entry here was wrong. - [x] README for every companion — 7 were missing entirely,
announcementshad 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/.githubrepo withCONTRIBUTING/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-specificCONTRIBUTING.md; its duplicate CoC/SECURITY (with staleElyar0URLs) were removed. - [x]
Introductionnow 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/*.mdpages — "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.ymlin the org.githubrepo holds the whole PHP 8.3/8.4/8.5 × Laravel 12/13 matrix plus Pest / Pint / PHPStan /composer validate --strict, behind a singleci-passedgate job. Every package'stests.ymlis now four lines:uses: Telegram-Bot-Essentials/.github/.github/workflows/ci.yml@main. All 10 repos migrated, all green.[x]
--prefer-lowestcell in the matrix (PHP 8.3 / Laravel 12),continue-on-errorfor now — informative until the declared floors are audited suite-wide, then flip to blocking.[x] Release automation (option A).
.github/workflows/release.yml(reusable) + arelease.ymlin each repo that, on a pushedv*tag, creates the GitHub Release with generated notes. CHANGELOG stays hand-written for the "why". Backfilled the missingessencev0.12.0Release from its CHANGELOG.[x] Renovate. Shared preset
default.jsonin the org.githubrepo (semantic-commit chores, grouped dev-deps,pinDigestsfor Actions,essencepeer constraint pinned off-limits); each repo carries a one-linerenovate.jsonextending 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
mainbranches:ci / ci-passedrequired, linear history, no force-push, no deletion, conversation resolution required.enforce_admins: falseso 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
pinDigestsonce the app runs — no separate pass needed.[x] Audited the version floors —
--prefer-lowestresolves and passes on all 10 packages, so the matrix cell is now blocking, notcontinue-on-error.[x] Stale + PR-size workflows: reusable
stale.yml(label-only, 90 days, never auto-closes) andpr-size.yml(onesize/*label by changed-line count) in the org.githubrepo, 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-Essentialsorg (github.com/apps/renovate) — until then therenovate.jsonfiles are inert. Confirm thegithub>Telegram-Bot-Essentials/.githubpreset 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