Skip to content

0.5.2.0: Citrus core, server groups, balance, CDN, support access and more - #151

Merged
Miroshka000 merged 106 commits into
mainfrom
dev
Oct 22, 2026
Merged

Miroshka000 merged 106 commits into
mainfrom
dev

Conversation

@Miroshka000

@Miroshka000 Miroshka000 commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

The 0.5.1.1 betas (rc.1 to rc.15) ship as the stable 0.5.2.0. The full notes are the ## 0.5.2.0 section of CHANGELOG.md, which the release and the panel show.

Summary

  • Nodes on the Citrus core with PROXY protocol, the node's SNI router on a shared 443, speed limits held by the core, Shadowsocks-2022 with a key per client; VMess becomes VLESS.
  • Server groups with traffic of their own (main, own, unlimited, closed), servers in a plan, packages for chosen plans and as gifts, "Apply to issued subscriptions".
  • Subscriptions: a subscription domain of its own, a notice in place of the servers, behind-a-CDN presets, X-Mikan-Groups for the Mikan app; Mikan (Android) and Kabosu (desktop) first on the subscription page, INCY on the iPhone.
  • Money: the clients' balance with manual top-ups, ad campaign links, extra devices and a second subscription in the bot, refunds through addons.
  • Operations: secrets out of .env (config.json + secrets/), the node's API for the panel's addresses only, diagnostics, logs and restart without SSH, support access with an extended mode and a terminal, the activity log, community addons, the region and geo bases checks, front page for strangers, the panel as an installable app (PWA).
  • CI fixes for this PR: the installer's secret files change owner only under root (6 tests failed as a non-root runner), rustls-webpki 0.103.16 and webpki-roots 1.0.10 (RUSTSEC-2026-0382, -0381), CodeQL alerts 32-36 (a held API answer states its type with nosniff, the log tail copies from the ring, installer tests print no secret), a race in the bot's reissue test.
  • main merged back into dev: the site and README pages written on main, with the beta notes now saying 0.5.2.0.

Before updating

  • Update the nodes together with the panel: migration 0043 stores REALITY's max-time-difference in milliseconds, as Citrus reads it.
  • Shadowsocks-2022 (AES): apps must refresh their subscriptions after the node is updated (a key per client).
  • The nodes' core is Citrus from its dev branch (getmikan/Citrus ca8204d9), not a tagged Citrus release.

Migrations

0032 to 0052: support access, community addons, VMess to VLESS, extra devices, campaigns, balancing, node HTTP redirect, balance, sites, node SNI router, node API lockdown, REALITY time in ms, server groups, plan servers, node front page, support extended mode, Telegram clients, dropped tables of inbound settings and bot events, relay traffic.

Not tested live

  • A real top-up by transfer in Telegram, campaigns with real buyers
  • A protocol through a real CDN; the subscription domain's ACME on a real server
  • The Citrus speed limits and SS-2022 per-client keys under real load
  • The Mikan app's server groups and mikan:// / kabosu:// links on devices (Kabosu has no release yet)

…l levels

A grant (ms_ token, 2/6/24 hours, one live at a time) lets an operator in by a
one-time browser link or a bearer token. view only reads, change also writes,
terminal stores and validates the servers for the coming terminal. Auth, API
keys, the grants themselves, the secret path, the audit journal and Telegram
backups stay closed. Without show_people names, Telegram, contacts, notes,
addresses and subscription links are masked in every JSON answer. The journal
gets support.grant/enter/revoke/expire and the operator's actions under actor
support:<id>; the admin is told in Telegram when the operator comes and goes.
The panel and the node keep the last 2000 lines of their log in memory with
tokens, keys, passwords, subscription links and secret paths cut out. New
endpoints: GET /api/v1/logs?source=panel|node:<id>&tail=N, POST
/api/v1/system/doctor and /nodes/{id}/doctor, POST /api/v1/system/restart and
/nodes/{id}/restart (session only). The node answers GET /v1/logs, POST
/v1/doctor and POST /v1/restart. The doctor and the restart reach the host
through request files its systemd units watch (internal/hostchan).
…a restart, mikan support

mikan doctor --json prints the report with secrets cut out; --requested (the
doctor request unit) takes doctor/request and writes doctor/report.json through
panelfs. mikan restart --requested does the same for restarts. Both units are
written only where they differ and come with mikan update on older installs.
mikan support <code> sends the report, mikan status and the last log lines,
all redacted, to the support desk; --dry-run shows what would be sent.
Conflicts kept both sides in api.New (support and host operations register).
A doctor run is a read-only diagnostic and passes the view level of a support
grant; a restart is a write and needs the change level.
…nded by the grant

mikan terminal-agent runs one session per request file of a path unit: a pty shell in its
own process group, a unix socket for the panel's user alone, an asciicast v2 recording kept
30 days, ended at the grant's end, a revocation marker, the panel letting go or 15 minutes
of silence. support/ stays root's through mikan update.
…PI, with recordings

GET /api/v1/support/terminal (WebSocket, same origin, a live grant of the terminal level
that names the target) bridges the operator to the host's agent: the panel's own server
through a unix socket in its data directory, a remote node through POST /v1/terminal on the
panel-only TLS channel. A hub of open sessions closes them when the grant ends; the host's
agents are told through a revocation marker. support.terminal.start/end go to the audit and
the admin's Telegram. GET /api/v1/support/recordings[/{name}] are the admin's only.
coder/websocket (already in go.mod, indirect) is the WebSocket library.
…age, terminal and recordings player

Settings > Security gets the Support access card (level, hours, client data, terminal servers with password and TOTP, one-time link with QR and copy, journal, revoke). A strip across every page shows a live grant to the admin and the mode to the operator. /support takes the token from the address fragment once, clears it and enters. The new Diagnostics page has redacted logs with filter and auto refresh, the host check with restart, and the xterm.js terminal (own chunk) for operators with the terminal level. Recordings open in a small asciicast player. Parts closed to operators (credentials, API keys, Telegram backup and broadcast) show a notice. ru and en texts.
…leaks of a masked operator, open the server checks at view level
…erver's .env

Settings support_access (on by default, the admin's session only) and
MIKAN_SUPPORT_ACCESS=off in .env (set by mikan support-access on|off): off, no
grant can be made, tokens and sessions of the live one open nothing and it ends
at once (its terminals too); the server takes no support terminal units. The
card shows the switch and why it is off; README and the rules page say there is
no standing access.
…pages for support access and diagnostics without SSH
…l checks a one-time code from a paid ticket with the support bot and hands the access to it itself, never showing the link; an access the bot did not take is withdrawn
Addons → Community lists third-party addons from the registry a mikan workflow
syncs with the authors' releases and signs with the catalog's key; the panel
reads only that index. They install as unofficial addons by digest. Every install
and every update of an unofficial addon (registry or source) asks the admin to take
the risk (ack, refused by the API without it; Update all skips them), an update
signed with another author key waits for the admin to accept its fingerprint, and
a revoked addon is marked with the reason and installs no more, from the registry
or a source of its repository. A source cannot claim an id the registry lists.
Migration 0033 (addon_community).
…very unofficial install and update, revocation
…s root

The installer is root on a server; a test run by anyone else (CI's runner) failed on the chown of secrets/ and stopped six tests. The owner is checked only under root, as panelfs and the terminal already do.
…he iPhone

Mikan (Android) and Kabosu (Windows, macOS, Linux) are mikan's own apps: first on their platforms, with mikan:// and kabosu:// links no other app takes. INCY joins the iPhone list after Happ. A Kabosu whose User-Agent names no core still gets the Clash profile.
The bot answers a tap at once and draws its screen after; the test waited for the answer only, so the late screen of someone else's tap was now and then taken for the subscriber's own and the link checked before it changed.
…, not its value

CodeQL took the assert messages that printed the fake secrets for cleartext logging (alerts 35, 36).
…om the ring

CodeQL alerts 32-34. An answer held for masking goes out with its handler's type, or the one net/http would sniff from the same bytes, and nosniff, so a browser never reads it as a page. Tail copies what the ring holds instead of sizing an allocation by the n asked for.
# Conflicts:
#	CHANGELOG.md
#	README.es.md
#	README.fa.md
#	README.md
#	README.ru.md
#	README.tr.md
#	README.zh-CN.md
#	site/astro.config.mjs
#	site/src/content/docs/en/operations/cli.mdx
#	site/src/content/docs/en/operations/community-addons.mdx
#	site/src/content/docs/en/operations/diagnostics.mdx
#	site/src/content/docs/en/operations/support-access.mdx
#	site/src/content/docs/en/protocols/presets.mdx
#	site/src/content/docs/en/support/paid.mdx
#	site/src/content/docs/operations/cli.mdx
#	site/src/content/docs/operations/community-addons.mdx
#	site/src/content/docs/operations/diagnostics.mdx
#	site/src/content/docs/operations/support-access.mdx
#	site/src/content/docs/protocols/presets.mdx
#	site/src/content/docs/support/paid.mdx
"In beta 0.5.1.1" becomes "New in 0.5.2.0" ("С версии 0.5.2.0"), "older than 0.5.1.1" becomes "older than 0.5.2.0", and the pointers to the beta channel go: stable panels get all of it with 0.5.2.0.
@Miroshka000
Miroshka000 marked this pull request as ready for review October 22, 2026 16:13
Miroshka000 and others added 6 commits October 22, 2026 19:14
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.190 to 0.2.191.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Changelog](https://github.com/rust-lang/libc/blob/0.2.191/CHANGELOG.md)
- [Commits](rust-lang/libc@0.2.190...0.2.191)

---
updated-dependencies:
- dependency-name: libc
  dependency-version: 0.2.191
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [sha2](https://github.com/RustCrypto/hashes) from 0.11.0 to 0.11.1.
- [Commits](RustCrypto/hashes@sha2-v0.11.0...sha2-v0.11.1)

---
updated-dependencies:
- dependency-name: sha2
  dependency-version: 0.11.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps the site group with 1 update in the /site directory: [astro](https://github.com/withastro/astro/tree/HEAD/packages/astro).


Updates `astro` from 7.3.8 to 7.4.1
- [Changelog](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md)
- [Commits](https://github.com/withastro/astro/commits/astro@7.4.1/packages/astro)

---
updated-dependencies:
- dependency-name: astro
  dependency-version: 7.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: site
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps the actions group with 1 update in the / directory: [taiki-e/install-action](https://github.com/taiki-e/install-action).


Updates `taiki-e/install-action` from 2.87.28 to 2.87.29
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](taiki-e/install-action@1168f68...8cca091)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-version: 2.87.29
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps the web group with 9 updates in the /web directory:

| Package | From | To |
| --- | --- | --- |
| [@radix-ui/react-dialog](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/dialog) | `1.3.0` | `1.3.1` |
| [@radix-ui/react-dropdown-menu](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/dropdown-menu) | `2.2.0` | `2.2.1` |
| [@radix-ui/react-switch](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/switch) | `1.4.0` | `1.4.1` |
| [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) | `5.104.2` | `5.104.4` |
| [@tanstack/react-router](https://github.com/TanStack/router/tree/HEAD/packages/react-router) | `1.170.42` | `1.170.45` |
| [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `1.55.0` | `1.59.0` |
| [motion](https://github.com/motiondivision/motion) | `14.1.0` | `14.4.0` |
| [react](https://github.com/react/react/tree/HEAD/packages/react) | `19.3.0` | `19.3.1` |
| [react-dom](https://github.com/react/react/tree/HEAD/packages/react-dom) | `19.3.0` | `19.3.1` |



Updates `@radix-ui/react-dialog` from 1.3.0 to 1.3.1
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/dialog/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/dialog)

Updates `@radix-ui/react-dropdown-menu` from 2.2.0 to 2.2.1
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/dropdown-menu/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/dropdown-menu)

Updates `@radix-ui/react-switch` from 1.4.0 to 1.4.1
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/switch/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/switch)

Updates `@tanstack/react-query` from 5.104.2 to 5.104.4
- [Changelog](https://github.com/TanStack/query/blob/main/packages/react-query/CHANGELOG.md)
- [Commits](https://github.com/TanStack/query/commits/@tanstack/react-query@5.104.4/packages/react-query)

Updates `@tanstack/react-router` from 1.170.42 to 1.170.45
- [Changelog](https://github.com/TanStack/router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/TanStack/router/commits/@tanstack/react-router@1.170.45/packages/react-router)

Updates `lucide-react` from 1.55.0 to 1.59.0
- [Commits](https://github.com/lucide-icons/lucide/commits/1.59.0/packages/lucide-react)

Updates `motion` from 14.1.0 to 14.4.0
- [Changelog](https://github.com/motiondivision/motion/blob/main/CHANGELOG.md)
- [Commits](motiondivision/motion@v14.1.0...v14.4.0)

Updates `react` from 19.3.0 to 19.3.1
- [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/react/react/commits/v19.3.1/packages/react)

Updates `react-dom` from 19.3.0 to 19.3.1
- [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/react/react/commits/v19.3.1/packages/react-dom)

---
updated-dependencies:
- dependency-name: "@radix-ui/react-dialog"
  dependency-version: 1.3.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: web
- dependency-name: "@radix-ui/react-dropdown-menu"
  dependency-version: 2.2.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: web
- dependency-name: "@radix-ui/react-switch"
  dependency-version: 1.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: web
- dependency-name: "@tanstack/react-query"
  dependency-version: 5.104.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: web
- dependency-name: "@tanstack/react-router"
  dependency-version: 1.170.45
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: web
- dependency-name: lucide-react
  dependency-version: 1.59.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: web
- dependency-name: motion
  dependency-version: 14.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: web
- dependency-name: react
  dependency-version: 19.3.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: web
- dependency-name: react-dom
  dependency-version: 19.3.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: web
...

Signed-off-by: dependabot[bot] <support@github.com>
@Miroshka000 Miroshka000 changed the title 0.5.1.1: Citrus core, balance, CDN, sub domain, balancing and more 0.5.2.0: Citrus core, server groups, balance, CDN, support access and more Oct 22, 2026
… drops the .env of before

After a failed migration or start that leaves the new release (no safe rollback), its settings are in config.json and secrets/, but the .env from before stayed, and the next update read the server by it: a move from SQLite whose import had committed was taken for one not made and imported again over the newer data. The .env goes, as after a good update (the update's archive keeps it).
…ept since secrets left .env

The fixture's compose reads ${...} as an older one does; the kept database password is checked in secrets/ and in the .env a rollback writes for the old release; the new release is read from config.json, and a failure that leaves it leaves no .env. The script stopped at its first stale check since the move to secrets, hidden behind the cargo tests that failed before it.
…t goes

CodeQL alert 32: the masked API's pass-through write. An answer whose handler wrote without a status gets the type it gave, or what net/http would sniff from the same first bytes, and nosniff, said before the headers go.
…m 2.87.28 to 2.87.29 in the actions group across 1 directory
…in /site in the site group across 1 directory
@Miroshka000 Miroshka000 reopened this Oct 22, 2026
@Miroshka000
Miroshka000 merged commit 789d5f0 into main Oct 22, 2026
11 checks passed
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.

3 participants