* fix(mtproto): use hosts for public share links
Generate MTProto subscription, client, copy, QR, and export links from managed Hosts so reverse-proxied public ports are advertised correctly. Migrate the redundant legacy custom share address into a Host and keep old imports compatible.
Closes#5126.
* fix(mtproto): keep host share links lossless and consistent
Address review on the MTProto hosts share-link change.
The migration no longer drops a legacy custom share address: an unrelated
(or disabled) Host stopped suppressing it, so only a Host already advertising
the same address does. An imported address now clears the same validation the
strict normalizer applies to every other protocol before it becomes a Host.
Panel and subscription agree on the endpoint a Host advertises: a portless host
string inherits the inbound port rather than the group's, and a port-only host
inherits the inbound address instead of emitting server=%3A8443.
LinksForClient prefers host endpoints for every protocol, the way getSubs and
inboundLinks already do, so the client-links API no longer ignores managed
hosts.
* fix(mtproto): migrate legacy share address past unusable hosts
The seeder skipped the conversion whenever any Host already carried the
address, including one that is disabled or excludes the raw sub type.
hostEndpoints drops those, so nothing advertised the address afterwards and
the marker committed with no way back. The duplicate check now mirrors that
same predicate.
UpdateInbound cleared a legacy MTProto shareAddr without the Host conversion
AddInbound runs, so re-applying an inbound definition through the API dropped
the public address silently. Both paths share one capture helper now.
Refresh the generated clients API reference for the summary reworded in the
previous commit.
* fix(inbounds): wait for the hosts list before building mtproto links
The page destructured only `hosts` from useHostsQuery, and that list reads
empty both while /panel/api/hosts/list is in flight and after it fails.
withMtprotoHostEndpoints then returns the inbound untouched, so Copy, QR and
Export advertise the internal listen port — the endpoint this branch exists to
replace. It is worse than not fixing it: the seeder has already moved a legacy
custom share address into a Host, so the fallback is the panel's own hostname
instead of the operator's address, and the Go generators reading the same rows
from the DB stay correct, so the two disagree for one inbound.
Fold the query into the page's existing readiness gate, the same way
useInbounds and HostsPage already consume that hook, so an empty list means
"no hosts" rather than "not loaded yet". The error branch fires only when
nothing is cached, so a refetch failing on window focus does not blank a page
whose host rows are still perfectly usable.
---------
Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
genMtprotoLink appended the panel remark as a URL fragment (tg://proxy?...&secret=...#remark). Because secret/server is the last query value, lenient Telegram parsers fold the "#remark" into it and the imported proxy breaks with "incorrect client random". Telegram proxy deep links have no name field, so emit a clean link on both the backend (internal/sub) and frontend (inbound-link.ts). The remark still shows as a separate tag in the inbound info modal, which reads it from genAllLinks, not the URL.
Guards: Go TestGenMtprotoLinkFields asserts no fragment; the frontend mtproto link test asserts no '#'.
Replace the upstream 9seconds/mtg sidecar with the dolonet/mtg-multi fork so a single MTProto inbound can serve many per-user secrets. Each panel client is now one named FakeTLS secret in the fork's [secrets] section: clients are first-class (attach/detach, limits, expiry, per-client tg:// links) exactly like every other protocol, mirroring the WireGuard multi-client model. Per-client traffic and online status come from the fork's /stats JSON API (its Prometheus output has no per-user label), fed into the existing email-keyed client_traffics accumulator; an optional throttle caps concurrent connections. A one-time seeder converts each legacy single-secret inbound into a one-client inbound.
The fork ships only linux/darwin amd64/arm64 binaries but is pure Go, so provisioning builds it from source for every supported platform (release.yml, DockerInit.sh) while keeping the panel-expected mtg-<os>-<arch> filename and the 'run' verb, so process.go is untouched. Also fixes a pre-existing update.sh gap that never renamed the mtg binary for armv6/armv7 updates.