CHANGELOG.md
Rama main · réplica de lectura del repositorio original.
# Changelog
All notable changes to this project are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.4.0] - 2026-09-28
Every country's flag, on every platform. On PasarGuard, the applications and
the announcement link the operator already configured in the panel. Live
figures on PasarGuard and Rebecca, as on 3X-UI. And on a fresh PasarGuard or
Rebecca install, the panel's own name and support link are offered instead of
being typed again. Row stays the default design, and nothing changes for an
existing install except what is listed below.
### Added
- **Every country's flag, on every platform.** A node's flag used to be drawn by
the reader's own emoji font, and Windows has none with flags, so Chromium-based
browsers there — most desktop readers — printed the two letters (`TR`, `DE`)
instead; only six countries were drawn another way. The page now carries the
Twemoji flags as a small colour font and uses it for the flag badge alone, so
all 258 flags draw the same on Windows, Android, iOS, macOS and Linux, in
Chromium, Safari and Firefox. It covers flag emoji and ISO codes alike
(`🇹🇷 Istanbul`, `TR | Ankara`, `GB-LON-1`). `UK` now draws the British flag
(no emoji set draws a `UK` pair), and `XK` (Kosovo) is recognised. The font is
a 64-units-per-em subset of Twemoji Country Flags — visually identical at badge
size and a third smaller — rebuilt reproducibly by `tools/subset-flag-font.sh`;
the artwork is CC-BY 4.0 (`src/fonts/TWEMOJI-LICENSE.txt`).
- **PasarGuard: the operator's own applications.** When the operator has listed
applications in PasarGuard (Settings → Subscription → Applications), the
Connect card shows those instead of the built-in list: one tab for each
platform the list covers (Android, iOS, Windows, macOS, Linux, Android TV,
Apple TV), the recommended application first, the operator's description in
the reader's language, and a download link. Import uses the link PasarGuard
built for this subscriber — completed with the page's own address when
PasarGuard has no subscription URL prefix and gives only a path — and for a
device-bound subscription PasarGuard has already left out the applications
that do not apply. With no list configured,
the page keeps its built-in catalogue. The icons the panel names are never
loaded, because the page makes no request to another site.
- **PasarGuard: the announcement's own link.** An announcement with an address
set in the panel (`announce_url`) gains an *Open link* under its text.
- **Live figures on PasarGuard and Rebecca.** Usage, expiry and online status now
refresh while the page is open on every panel. Both panels serve them at
`/<token>/info` in their own format; the page reads that format and stops,
leaving the figures it has, if a panel ever answers with anything else.
- **The panel's own name and support link, offered at install.** PasarGuard and
Rebecca already hold a subscription title and a support URL. A fresh install
on either reads them (read-only) and asks *Use them for this page?*. In a
script, `RT_PANEL_BRANDING=1` takes them and `RT_PANEL_BRANDING=0` never offers
them; `RT_SERVICE_NAME` and `RT_SUPPORT_URL` always win. On an existing
install, **Reconfigure → Use PasarGuard's (Rebecca's) name and support link**
in the manager, or `RT_PANEL_BRANDING=1 row-template config`, does the same.
The panels' defaults (`Subscription`, `https://t.me/`) are never offered, nor a
PasarGuard title that still carries a per-subscriber placeholder such as
`{USERNAME}`; every value passes the same checks a typed one does.
### Fixed
- **A panel's default support link no longer replaces yours.** Rebecca's
support URL defaults to the bare `https://t.me/`, and a support link from the
panel outranks the one configured in Row-Template, so on a default Rebecca
every page's *Contact support* went to Telegram's front page. The bare
default now counts as no link, on Rebecca and PasarGuard alike.
- **Rolling back after the update keeps your design.** The backup a
pre-1.4.0 updater takes of the page it replaces is written after the new
designs are installed, so it matches none of them and names no design; a
rollback to it restored Row instead of the design you had. The rollback now
takes the design that backup's own saved settings record, and restores it from
the installed designs.
### Changed
- **The page is larger, and paints as early as before.** 1.4.0 adds 81,052
bytes to every design, 68,748 of them the flag font (the largest design is now
285,757 bytes); the size ceiling is 280 KiB (was 200 KiB). The font is placed after the page's markup, not in
its head, so the first paint does not wait for it: measured on a 4× slower CPU
and a 1.6 Mbit/s link, first paint is unchanged or earlier, and the page
finishes loading about 0.4 s later on that link (about 0.1 s on 4G). Script
time is unchanged.
- **Tabs wrap.** With more than four platforms in a panel's list, the tab strip
wraps onto a second line in every design instead of shortening the labels.
### Known limitations
- The panel-branding offer reads SQLite databases only; with MySQL, MariaDB or
PostgreSQL the installer simply asks as before.
- PasarGuard's application icons are not shown (see above), and the page does
not state a device limit; the panel's own filtering of the application list is
what a device-bound subscription gets.
- PasarGuard's page title (`subTitle`) and Clash templates are still not
produced, and Rebecca on MySQL/MariaDB still needs its one setting entered in
the dashboard.
- Rebecca's Docker image is still the 0.0.x Python edition, which cannot render
this page, and is refused before anything is changed. Use Rebecca 1.x from its
binary installer (`rebecca-binary.sh`).
### Development
- **Every built script is compiled by the suite.** The runtime modules are
concatenated into one scope for the page, so two modules declaring the same
name are fine as modules and a syntax error as a page. `tests/artifact-scripts
.test.mjs` compiles every script of every design on every panel, and refuses
a top-level name declared twice.
- **The flag font is tested from its bytes.** `tests/flag-font.test.mjs` reads
the committed WOFF2 with Node's own Brotli and proves that every code the page
can emit has a colour glyph, and where the face sits in every page.
- **Browser validation in three engines.** The PasarGuard and Rebecca pages,
rendered by the panels' real engines, and the 3X-UI pages from the fixture
renderer, were checked in Chromium, WebKit and Firefox: every flag drawn as a
flag, the panel's applications and platforms, the announcement link, live
refresh repainting, Persian right-to-left, no request to another host.
- `tools/rebaseline.mjs` re-pins the seventeen artifact locks to the current
build and prints what moved, for a deliberate change to the shared runtime.
### Upgrading
- From **1.1.0, 1.2.0 or 1.3.x**, on any panel: run `row-template update`. Your
design, branding and panel wiring are kept; the new page is in place at once.
- To take PasarGuard's or Rebecca's own name and support link on an existing
install: `row-template` → **Reconfigure** → the last item, or
`RT_PANEL_BRANDING=1 row-template config`.
- Rolling back after the update returns the page and the recorded version, not
the manager, as before; only the two newest backups are kept.
## [1.3.1] - 2026-09-27
A hotfix release: everything fixed after v1.3.0 was published, packaged as one
small upgrade. The PasarGuard and Rebecca pages now name themselves after the
subscriber instead of the generic word "Subscription", a node name that carries
an ISO 3166-1 alpha-2 code now draws that country's flag, and the release
validation is reproducible and fully green on Linux. No installer adapter
changes behaviour, and nothing changes for an existing 3X-UI install.
### Fixed
- **PasarGuard: the page no longer titles itself "Subscription".** PasarGuard's
page context carries the subscriber (`user.username`) and the admin's own
columns, but **not** the panel-wide subscription settings, so the template had
nothing to read and every page fell back to the generic word. The page is now
named by the admin's configured profile title when there is one, and by the
subscriber otherwise — the order PasarGuard itself resolves a title in. A
profile title that still carries a PasarGuard format placeholder
(`{DATA_LIMIT}`, `{EXPIRE_DATE}`, `{USERNAME}`, …) is refused rather than
printed literally, because the page cannot resolve those variables and a
literal placeholder is worse than the subscriber's own name. The subscriber's
address (`user.ip`) is still never read.
- **Rebecca: the page no longer titles itself "Subscription" either.** Rebecca's
page context carries **no profile title at all**: the panel-wide
`subscription_profile_title` is written into the `/info` `profile-title`
**header** (`subscriptionHeaders`) and the page render path
(`renderSubscriptionHTML` → `renderSubscriptionPageTemplate`) never passes it,
so the title the adapter reads from that header is invisible to the page. The
one identity the page does receive is `user.username`, so that now names the
page — the same fallback PasarGuard takes when no profile title is
configured. A value with no word in it at all (absent, empty,
whitespace-only) is not a name and the page keeps its generic fallback; the
value is never rewritten. No `{` guard is needed here, unlike PasarGuard:
Rebecca never runs a title through `str.format_map`. The subscriber's token
and address are still never read, and nothing changes for an existing 3X-UI
or PasarGuard install.
- **Country flags: a node name that carries an ISO alpha-2 code now gets that
country's flag.** `TR | Istanbul`, `RU-01`, `GB-LON-1`, `FI-01`, `DE` and the
rest previously found no regional-indicator pair and fell to the monogram
(`T-I`, `R-U`, `G-B`, `F-I`) — which on any platform reads as the code, which
is why the badge appeared to show `TR`/`RU`/`GB`/`FI` instead of a flag. A
two-letter token is read as a country only when it is uppercase, stands alone
between non-letters, and names a real country in the **same 258-code registry
the emoji path already uses**; `LON` out of `GB-LON-1`, `USA`, `A1B`, `ZZ-01`
and lowercase English words (`no`, `it`, `us`, `in`) are all still refused. An
explicit flag emoji still wins over a code in the same name, and the displayed
name is never rewritten. Country *names* and *city names* — `Turkey -
Istanbul`, `Türkiye`, `Turkiye`, `Finland Helsinki` — still draw the monogram:
a name table does not fit the frozen artifact budget, and guessing from an
ambiguous name is worse than a monogram. All 15 artifacts were re-baselined
(+179 B each; `pulsenova` is 204,705 B with 95 B of headroom).
### Documentation
- `docs/design/PGCLOCK-AUDIT.md` — a read-only audit of the third-party PGClock
template as a secondary reference. It confirms `user.username` as the exposed
subscriber identity, has no service-name, support-URL or flag logic, and is
unlicensed, so nothing was derived from it and `PROVENANCE.md` is unchanged.
- `docs/design/PASARGUARD-ADAPTER-AUDIT.md` §8 — the page context is not the
`/info` payload: the headers carry the values the panel *resolved*, the page
context carries the admin's own columns and nothing else.
- `docs/design/REBECCA-ADAPTER-AUDIT.md` §2 — the same distinction for Rebecca,
stated against `subscriptionHeaders` and `subscriptionTemplateContext`: the
page context carries no profile title at all, so the page's title and the
adapter's header-derived `title` field can legitimately differ.
- `docs/design/FLAG-RENDERER-AUDIT.md` §17 — the alpha-2 path is an **input**
extension; the return shape, `CODES` and every fallback are unchanged.
### Development
- **The release validation now runs on Linux, in CI.** The repository's only
workflow was `Docs`, which builds the documentation site and never ran the
suite — so a green check on a pull request said nothing about whether the page
still rendered. `.github/workflows/release-validation.yml` runs the four
release commands (`npm test`, `npm run verify`, `npm run lint:sh`,
`npm run build:panels`) on `ubuntu-latest`, installing the three tools the
suite needs and a Node runtime does not provide: **Go** (the fixture renderer
and the pongo2 harness), **Python 3 with Jinja2** (the PasarGuard harness) and
**ShellCheck**. Each command reports its own result even when an earlier one
fails, because the point of the job is the four results.
- **The panel life-cycle tests are offline, and no longer validate a published
release.** Both installer panel suites built a two-design payload while the
release registry names seventeen, so `rt_complete_install` always judged the
install short of what its own version ships and downloaded the release pinned
to that version — the published v1.3.0 tarball. On a host with network the
download succeeded, the template store was rebuilt from that release, and
`verify` then compared it against the working tree: `FAIL canonical artifact
does not match the selected template (row)`. The payload is now the complete
release, so the download is never reached, and `curl` and `wget` are denied in
every fake host and record the attempt, so a regression fails the suite loudly
instead of quietly reaching the internet.
- **`sqlite: false` now means sqlite3 is *unavailable*, not merely
unimplemented.** The adapters gate on `command -v sqlite3`, and `ubuntu-latest`
ships a real one, so the manual-activation test read `outcome=auto` where it
expected `manual`. Every directory holding a sqlite3 is now removed from the
child's `PATH`, replaced by a mirror of itself so nothing else on the host
stops resolving; a new test plants a real-looking sqlite3 and proves it cannot
reach the child.
- **The release validation is reproducible and fully green.** With both leaks
closed, the four release commands pass on `ubuntu-latest` — `npm test` with no
failures, `npm run verify` all checks passed, `npm run lint:sh` 12 scripts
clean, `npm run build:panels` 51 shells — and the result no longer depends on
whether the host has network or happens to ship a `sqlite3`.
## [1.3.0] - 2026-09-26
Row-Template now installs on **PasarGuard** and **Rebecca** as well as 3X-UI,
and ships two more designs. A minor release: nothing changes for an existing
3X-UI install except what is listed below, and Row stays the default design.
It also carries every fix prepared for 1.2.1, which was not released on its
own.
### Added
- **PasarGuard support.** PasarGuard is supported from this release: detect,
install, activate, verify, back up, restore and uninstall, on the official
Docker install and on a source install (`pasarguard.service`). The page is
placed at `/var/lib/pasarguard/templates/row-template/index.html` (or inside
your own `CUSTOM_TEMPLATES_DIRECTORY`) and selected by one marked block
appended to `/opt/pasarguard/.env`; a running panel is restarted once. None
of your own `.env` lines is edited, and uninstall returns the file to its
exact previous bytes. `row-template verify` also reports the two panel
settings that still take precedence over the page: an admin's own
`sub_template`, and `disable_sub_template`.
- **Rebecca support.** Rebecca 1.x — the Go edition, which Rebecca publishes for
its binary install — is supported from this release, with the same seven
operations. The page is placed at
`/var/lib/rebecca/templates/row-template/index.html` (or inside your own
custom templates directory) and selected in the newest
`subscription_settings` row, which Rebecca reads on every request — so
nothing is ever restarted. Activation is automatic with the default SQLite
database and `sqlite3`; with MySQL/MariaDB the page is still placed and the
installer prints the two values to enter in the dashboard. `NULL`, empty and
a set templates directory are each restored exactly.
- **Panel detection and choice.** The installer finds the panel on the server
and installs for it (`/etc/3x-ui/sub_templates/row-template` for 3X-UI,
`/etc/row-template` for PasarGuard and Rebecca). A panel counts only when two
independent signals agree; a half-installed panel is refused, not guessed at.
On a server with more than one panel it asks, or reads
`RT_PANEL=3xui|pasarguard|rebecca` in a script.
- **Transactional activation on PasarGuard and Rebecca.** The panel's state is
snapshotted, changed and verified; if any step fails it is restored exactly,
and the installer says so — and shows the real cause.
- **Two new designs: Meter and Notebook.** Meter is a calm instrument
dashboard of rounded cards with a segmented traffic meter; Notebook is a
page from a dotted notebook, hand-inked. Both were contributed by the
project's author, ported onto the shared runtime, and held to the same
contract as the other fifteen — seventeen designs in all, on every panel.
- **Every design, for every panel.** Each release now carries a PasarGuard
(Jinja2) and a Rebecca (pongo2) page for every design, under `shells/`,
checksum-verified like the 3X-UI pages.
### Fixed
- **Rolling back to a backup taken under 1.1.0 works.** 1.2.x refused it with
"backup artifact matches no installed template". A backup that names its
design is restored as that design; one whose page is none of this release's
designs (1.1.0's) is restored as this release's Row, so `verify`, design
switching and updates keep working afterwards.
- **A successful rollback is reported as a success.** The transaction engine
checked, after restoring the panel, that the panel was still pointing at
Row-Template's directory — which is exactly the state a correct rollback has
just undone. Every rollback therefore ended in "the rollback failed" even
when the panel had been restored perfectly. The engine no longer asks that
question: the restore verifies itself. Each panel adapter now re-reads the
panel's own setting after restoring and confirms it matches the value it
recorded before changing anything, and a restore that does not land is
reported as a failed rollback with the real cause. A regression test pins
this: the engine must never re-run the forward check after a restore.
- **The manual PasarGuard instructions are complete.** When activation cannot
be done automatically, the installer printed only `SUBSCRIPTION_PAGE_TEMPLATE`
and told you to edit `.env` — but the page had not been copied anywhere the
panel could read. It now prints both the copy and the two `.env` values
(`CUSTOM_TEMPLATES_DIRECTORY` and `SUBSCRIPTION_PAGE_TEMPLATE`), and says to
keep your own templates directory if you already have one.
- **A rollback right after a change undoes that change.** Backup names have
one-second resolution, and two backups made in the same second — a design
switch followed at once by `row-template rollback --auto`, which snapshots
the current state first — shared one directory. The newer snapshot
overwrote the older one, so the rollback re-applied the state it was meant
to undo. A backup now waits for the next second rather than reuse a name.
- **The live check after `config`, `update` and `rollback` runs on 3X-UI.**
It always said "skipped (no test URL available without sqlite3)", even with
`sqlite3` installed, because those commands had not located the panel
database. And the check made right after activation no longer warns "could
not reach the subscription endpoint" while 3X-UI is still restarting.
- **A page change that cannot reach PasarGuard or Rebecca changes nothing.**
Regenerating the page (a rebrand, a design switch, an update) replaced
`sub.html` before copying it into the panel; if that copy failed, `sub.html`
was left newer than the page the panel serves. It is now put back, and on a
first activation, where there was no `sub.html` before, none is left behind.
- **A valid page is never refused under load.** The structural check before
every install, update and design switch read the page through
`head | grep -q`. On a busy server `grep -q` could stop reading before `head`
finished writing, and the shell then reported the match as a failure —
"generated template does not begin with <!doctype html>" for a perfectly
valid page, about once in 150 checks. Every such check is now written so
that it cannot be cut short.
- All fixes prepared for 1.2.1 (below): one `row-template update` is enough to
move from 1.1.0, misplaced designs are moved back, branding works on an
install the 1.1.0 updater left incomplete, and `verify` names missing and
damaged designs.
### Security
- **Every value is escaped on every panel.** PasarGuard renders pages with a
non-sandboxed Jinja2 whose autoescaping is off. Every PasarGuard and Rebecca
page therefore wraps its body in an explicit autoescape block, and is tested
with the panels' real engines against hostile usernames, notes, links and
malformed data.
- **Branding can never open a template tag.** `{` and `}` in your service name,
support link or logo are written as `{` and `}`, so no branding
value can start a Jinja2 or pongo2 expression.
- **Panel secrets stay where they are.** PasarGuard's `.env` and Rebecca's
database URL are read only for the keys the installer needs, never printed,
and never copied into a backup. A MySQL/MariaDB password is never asked for
or read.
- **Backups record their panel** and are never restored onto another one.
### Changed
- `row-template version` shows the panel it serves; on 3X-UI it still shows the
minimum-supported and detected versions.
- `row-template uninstall` returns each panel to the page it had before
Row-Template, and leaves a page you chose afterwards alone.
- **A scripted fresh install on a server with more than one supported panel
needs `RT_PANEL`.** 1.2 installed for 3X-UI there because it was the only
panel it supported; 1.3.0 does not guess which panel you meant, and stops
with a message naming `RT_PANEL=3xui|pasarguard|rebecca`. An interactive
install asks instead. Re-running the installer on an existing install, and
`row-template update`, keep the install's own panel and are unaffected.
- The `on_hold` state on PasarGuard and Rebecca is shown as active: with its
"starts on first connection" duration on PasarGuard, and with an unknown
expiry on Rebecca, which does not give the page that duration
(`docs/design/PANEL-ON-HOLD-DECISION.md`).
### Known limitations
- On PasarGuard and Rebecca the page shows the values as of when it was opened;
live refresh (`?format=info`) is 3X-UI only, because both panels serve live
status on a path suffix.
- PasarGuard's page title (`subTitle`) and Clash templates are not produced.
- Rebecca on MySQL/MariaDB needs its one setting entered in the dashboard.
- Rebecca's Docker image (`rebeccapanel/rebecca` on Docker Hub) is still the
0.0.x Python edition, which cannot render this page. The installer
identifies it and refuses before changing anything; Rebecca's own
`rebecca migrate-binary` moves a Docker install to 1.x.
### Documentation
- The compatibility page, installation, configuration and troubleshooting
cover all three panels, in English, Persian and Arabic; the READMEs in all
five languages describe PasarGuard and Rebecca as supported.
- `docs/design/PASARGUARD-INSTALLER-AUDIT.md` and
`docs/design/REBECCA-INSTALLER-AUDIT.md` record, from each panel's source,
what activation is and how the installer follows it.
### Development
- The test suite renders the PasarGuard and Rebecca pages with the real
engines, and needs Python 3 with Jinja2 as well as Go; a missing engine is a
failure, never a skip.
- `tools/make-release.sh` writes checksums in the text form on every platform.
### Upgrading
- From **1.2.0** or **1.1.0** on 3X-UI: run `row-template update`. From 1.1.0,
the next `row-template`, `row-template config` or `row-template verify`
completes the install. Your design, branding and panel wiring are kept.
- On **PasarGuard** or **Rebecca**: run the installer. Earlier releases did not
install on these panels. Rebecca must be 1.x (its binary install); a Docker
Rebecca is 0.0.x and is refused until it is moved to 1.x.
- **Rolling back after the update.** `row-template update` backs up the version
it replaces, and `row-template rollback --to <that backup>` returns to its page
and branding. A rollback restores the page and the recorded version, not the
manager itself: `row-template` stays 1.3.0 and reports the version it rolled
back to, and the next `row-template update` returns to 1.3.0. Only the two
newest backups are kept, so the pre-update backup is replaced after two
further changes (a design switch, an update or a rollback each make one).
## [1.2.1] - Unreleased (shipped in 1.3.0)
Fixes the update from 1.1.0, which could leave the manager with no designs to
choose from. 3X-UI (>= 3.6.0) stays the only supported panel.
### Fixed
- **One `row-template update` is enough to move from 1.1.0.** 1.1.0's own
updater installs the new version but copies only four files, so in 1.2.0 the
designs were missing until a second update, and **Reconfigure branding →
Template** said "No templates are installed". Now the first time you open
`row-template`, or run `row-template config` or `row-template verify` as
root, after the update, it downloads the rest of the same release — every
design and the remaining installer files, checksum-verified — before doing
anything else. It downloads the version you have installed, never a newer
one, and changes nothing else: the live page, branding, selected design and
backups stay as they are. If the release cannot be reached, it says so and
tries again the next time the manager opens.
- **Designs found outside their folder are moved back.** The designs belong in
`dist/templates/`. A copy at the install root's `templates/` — where a copied
or extracted release leaves it — is now moved into place automatically by
`install`, `update` and `verify`. Each design is checked against its own
checksum first; one that fails is reported and left where it is, and files
Row-Template does not recognise are never removed.
- **Changing branding works on an install the 1.1.0 updater left incomplete.**
`row-template config` and the manager's branding editors refused with "the
template selection could not be reconciled" until a second update; they now
complete the install first.
- **`row-template verify` names missing and damaged designs.** A design that
fails its checksum is reported by name as a failure; missing designs are a
warning that names them. It previously reported a failing store without
saying which design, and did not report missing ones at all.
### Changed
- `row-template verify` is no longer strictly read-only. Run as root, it first
repairs the template store — moving misplaced designs back into place and
downloading any the installed version is missing, from that same release —
and then checks it. It makes no other change, and none at all when run
without root.
### Documentation
- The compatibility page lists, per panel, what the installer can do today:
detection, install, activation, verification, and backup and rollback. For
PasarGuard and Rebecca the answer is none of them — only the page shells are
built and packaged — so both stay **research targets, not supported panels**.
A test checks every README and compatibility page against the installer.
### Known issues
- Rolling back from 1.2.x to a backup taken under 1.1.0 fails with "backup
artifact matches no installed template": 1.1.0's page is not one of the
current release's designs. The rollback stops before changing anything, so
the running page stays as it was. Rolling back to a backup taken under 1.2.x
is not affected.
### Upgrading
- From **1.1.0**: run `row-template update`. The next `row-template`,
`row-template config` or `row-template verify` completes the install.
- From **1.2.0**: run `row-template update`. This also completes a 1.2.0
install that the 1.1.0 updater left without its designs.
## [1.2.0] - 2026-09-24
Turns Row-Template from one page into a collection of designs. A minor release:
Row stays the default design, and 3X-UI (>= 3.6.0) stays the only supported
panel.
### Added
- **Fifteen designs.** Row plus Editorial, Canvas, Prism, Terminal, Pulse,
Brutal, Arcade, Sketch, Signature, Saffron, Pulse Nova, Prism Nova, Terminal
Nova, and Arcade Nova. Every design is built from the same runtime and
translations, so status, Connect, QR codes, the Configuration Explorer, and
the five languages behave the same in each.
- **Choosing a design.** A fresh interactive install shows a design chooser
(Enter keeps Row). `RT_TEMPLATE=<id>` picks one for a scripted install, and
the manager's **Reconfigure branding → Template** changes it later. Updates
keep the selected design.
- **Checksummed designs.** Each design ships in the release with its own
SHA-256 checksum. `row-template verify` checks every installed design against
its checksum and confirms the live page is the selected design.
- **Painted country flags.** The flags of Germany, France, the Netherlands,
Japan, Sweden, and the United States are drawn with CSS, so they appear on
Windows in Chromium-based browsers, which otherwise show the two letters
(`DE`) instead of a flag. Other countries keep the platform's flag emoji.
- **Documentation site** in English, Persian, and Arabic: installation,
configuration, a template gallery with real previews, branding, security,
compatibility, a developer reference, and troubleshooting.
- **Panel shells for research.** The release carries each design's page shell
for every panel in the registry under `shells/`: 3X-UI, PasarGuard (Jinja2)
and Rebecca (pongo2). The installer does not place these files. PasarGuard
and Rebecca are research targets, not supported panels, and there are no
installation instructions for them.
### Changed
- **Panel database detection fails closed.** Row-Template previously used the
first `x-ui.db` it found. It now uses the first database file that exists —
the one `XUI_DB_FOLDER` names, then the default locations in order — and
only if it is a real SQLite database. If that file is not, it is refused with
a warning instead of silently moving on to another, possibly stale, database;
activation then falls back to the manual instructions.
- **Live refresh accepts only its own data.** The status refresh now checks
that a response carries the page's own fields before using it. An
unexpected response stops the refresh and leaves the server-rendered figures
in place, instead of repainting the page with wrong values.
- The country-code table is stored as a bitmap, saving about 800 bytes in
every page.
### Internal
- Groundwork for installing on more than one panel: a normalized data
contract, build-time adapters for 3X-UI, PasarGuard and Rebecca, a frozen
panel interface, a transaction engine, a 3X-UI panel adapter, and a
format-2 backup snapshot. No `row-template` command calls any of it yet;
backups and rollback still use the 1.1.0 format.
- The release ships the management library's companion files
(`lib/transaction.sh` and `panels/`), and install and update put them next
to the library. A payload whose library is present without them is refused
before anything changes.
### Development
- `npm test` first renders every design's fixture pages (`npm run
fixtures:all`), so a fresh clone can run the suite; it needs Go 1.22 or
newer.
- `npm run lint:sh` runs ShellCheck over every shell script.
- The test harness runs on Linux and on Windows with Git Bash.
- `tools/make-release.sh` is executable, as its usage line documents.
- Design records moved from the repository root to
[`docs/design/`](docs/design/README.md), with an index.
### Compatibility
- Requires 3X-UI (MHSanaei) **>= 3.6.0**.
- **Updating from 1.1.0 takes two runs of `row-template update`.** The first
is carried out by 1.1.0's own updater: it installs the new version — the
page updates and your branding is kept — but copies only the library and
the command, so only Row is available. The second, carried out by 1.2.0,
installs every design and the remaining installer files. `row-template
verify` reports whether the second run is still needed. (Fixed in 1.2.1,
which needs one run.)
## [1.1.0] - 2026-08-30
Evolves the subscriber page into a connection and configuration hub. A minor,
backward-compatible release: the v1.0.0 experience (status, Copy subscription,
QR, and per-app Connect actions) is unchanged and extended.
### Added
- **Configuration Explorer.** A new "Configurations" section that lists each
usable server on its own row — a country flag or monogram badge, the
configuration name, and a protocol label — with per-configuration **View**
(local QR code and copy) and **Copy** actions. When the list is long it gains
a search box; exact-duplicate links are collapsed and malformed links are
isolated rather than shown.
- **Protocol awareness** for VLESS, VMess, Trojan, Shadowsocks,
Hysteria/Hysteria2, WireGuard, AmneziaWG, and Telegram MTProto, presented as
proper-noun labels.
- **Restrained country badges** derived only from a valid ISO country code in a
configuration's own label; a monogram is shown when there is no valid flag —
the badge is never empty, and the country is never inferred from a host or IP.
### Improved
- Individual configurations are secret-aware: links stay masked until you open
**View**, are never written to the console, and QR codes render locally with
no external QR, geolocation, or telemetry requests.
- Configuration names and labels are always inserted as text, never as markup,
keeping the page safe against hostile link fragments; links are isolated for
correct left-to-right display within right-to-left interfaces.
- Broader automated coverage: client-side URI parsing and classification, flag
mapping, and explorer rendering, filtering, and XSS safety, plus expanded Go
rendering fixtures (30 cases, including real WireGuard, AmneziaWG, MTProto,
and Hysteria links, flags, bidirectional text, and large lists).
- The build size gate now warns at 185 KiB and fails only at 200 KiB
(previously a hard failure at 185 KiB), matching the artifact's real budget
while keeping a firm upper bound.
### Security
- The served page still makes no third-party requests. The Configuration
Explorer adds no network calls, no external QR or geolocation lookups, and no
telemetry, and it never logs subscriber links or identifiers.
### Compatibility
- Requires 3X-UI (MHSanaei) **>= 3.6.0**; validated against stock 3.7.0.
- Updating from v1.0.0 preserves your branding and configuration and keeps a
restorable backup for rollback.
## [1.0.0] - 2026-08-30
First stable release.
### Added
- Self-contained custom subscription page for 3X-UI panels — a single HTML
artifact with all CSS, JavaScript, fonts, and the QR generator inlined. No
third-party CDNs and no external network requests from the served page.
- White-label branding: configurable service name, support link, and logo, all
stored as data and injected safely (never executed).
- Five interface languages: English, فارسی, العربية, Русский, and 简体中文,
with right-to-left support.
- One-command installer with a mandatory SHA-256 integrity check on every
download (no skip path), safe archive extraction, and atomic activation.
- `row-template` manager with an interactive menu and direct commands:
`config`, `update`, `rollback`, `verify`, `version`, `uninstall`, and `help`.
- Update checking against the public stable release channel, with graceful
handling when the network or release source is unavailable.
- Backup and rollback: the previously installed version is snapshotted before an
update and can be restored without losing branding configuration.
### Compatibility
- Requires 3X-UI (MHSanaei) **>= 3.6.0**; validated against stock 3.7.0.
- Recommended operating system: Ubuntu 24.04 LTS (x86_64).
[1.3.1]: https://github.com/iitzSeriZdev/Row-Template/compare/v1.3.0...v1.3.1
[1.3.0]: https://github.com/iitzSeriZdev/Row-Template/releases/tag/v1.3.0
[1.2.1]: https://github.com/iitzSeriZdev/Row-Template/compare/v1.2.0...v1.3.0
[1.2.0]: https://github.com/iitzSeriZdev/Row-Template/releases/tag/v1.2.0
[1.1.0]: https://github.com/iitzSeriZdev/Row-Template/releases/tag/v1.1.0
[1.0.0]: https://github.com/iitzSeriZdev/Row-Template/releases/tag/v1.0.0