Skip to content

Enforce tiers on MTProto secrets - #16

Merged
GlobalTechInfo merged 1 commit into
mainfrom
feat/mtproto-tiers
Oct 18, 2026
Merged

GlobalTechInfo merged 1 commit into
mainfrom
feat/mtproto-tiers

Conversation

@GlobalTechInfo

Copy link
Copy Markdown
Owner

The gap

The Telegram relay terminates obfuscated2 itself and dials the datacenter directly, so it never passes through the SOCKS5 pipeline. Every phone therefore got the same unlimited pipe regardless of what its account was on the books for — a free-tier customer and a pro one were indistinguishable on the wire, and neither was metered.

What it does

Each secret carries a tier from the same Tiers map the SOCKS5 port uses, so the two cannot drift into meaning different things. The plan is enforced where the traffic actually is:

  • bandwidth paced at the tier's rate, both directions
  • concurrent sessions capped
  • data allowance — a secret that has moved its cap is refused

The panel shows live sessions and bytes per phone, so a plan can be seen doing something rather than only being configured.

One implementation note worth a look: counting bytes had to move out of io.CopyBuffer, which cannot report what it moved, back into the copy loop that reads. That is what the plan is billed on, and it is why the data cap knows about finished sessions at all.

Migration

Existing installs pick the column up on the next start. SQLite has no IF NOT EXISTS for columns, so the ALTER is attempted and a duplicate-column error is the success case — the one migration the database cannot do on its own.

Secrets that were already there keep working and default to unlimited: silently dropping someone to 1Mbps because of an upgrade would be the wrong way for an operator to learn about a missing column. A test seeds a pre-migration row and re-runs the migration to prove both.

Verified

  • The cap logic is tested directly: at the limit and over it, unlimited tiers never cap, and unknown tiers are refused rather than silently stored.
  • Every tier in the map round-trips through storage.
  • Live: a secret created as free through the API shows its session and its bytes counted against it.
  • gofmt/go vet clean; full suite green with and without xray; -race clean.

What is still not covered

Expiry does not apply to a Telegram secret, and the README says so plainly rather than leaving it to be discovered: there is no end date on a secret, and a cap or a revocation is how such an account is stopped. Adding one would mean an expiry column and the management that goes with it — worth doing if you bill on time as well as data.

REALITY sessions still bypass tier limits entirely: xray forwards them through the socks outbound using the service's own credentials, so there is no per-UUID identity to meter. Fixing that means per-user routing rules inside the xray config, which is a much larger change than this one.

The Telegram relay terminates obfuscated2 itself and dials the datacenter,
so it never passes through the SOCKS5 pipeline -- which meant every phone
got the same unlimited pipe regardless of what the account was on the books
for. A free-tier customer and a pro one looked identical on the wire.

Each secret now carries a tier, and the plan is enforced where the traffic
actually is: bandwidth is paced at the tier's rate, concurrent sessions are
capped, and a secret that has moved its data allowance is refused. The same
Tiers map the SOCKS5 port uses, so the two cannot drift into meaning
different things.

Counting bytes had to move from a hand-rolled relay loop into the pacer
path: io.CopyBuffer cannot report what it moved, so the copy counts as it
reads. That is what the plan is billed on, and it is why the data cap knows
about a session at all rather than only about sessions still running.

Existing installs pick the column up on the next start. SQLite has no "if
not exists" for columns, so the ALTER is attempted and a duplicate-column
error is the success case -- the one migration the database cannot do on
its own. Secrets that were already there keep working and default to
unlimited, because silently dropping someone to 1Mbps for an upgrade would
be the wrong way to learn about a missing column.

Verified live: a secret created as free shows its session and its bytes
counted against it, and the panel shows the same.

Expiry is the one part of a tier that does not apply here, and the README
says so rather than leaving it to be discovered: a Telegram secret has no
end date, and a cap or a revocation is how such an account is stopped.
@coderabbitai

coderabbitai Bot commented Oct 18, 2026

Copy link
Copy Markdown

Review in Change Stack →

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: ab4b6847-0cee-47f1-9836-c9cf3a16d97a
📥 Commits

Reviewing files that changed from the base of the PR and between c11e27e and 1480447.

📒 Files selected for processing: 8
  • README.md
  • database.go
  • faketls.go
  • faketls_test.go
  • main.go
  • mtproto.go
  • mtproto_test.go
  • web/index.html
+-------------------------------+
| Copilot: Off. CodeRabbit: On. |
+-------------------------------+
  \
   \     /)/)
        ( '')
        /  |
      o(___)
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@GlobalTechInfo
GlobalTechInfo merged commit 62f9f33 into main Oct 18, 2026
2 of 3 checks passed
@GlobalTechInfo
GlobalTechInfo deleted the feat/mtproto-tiers branch October 18, 2026 09:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant