Skip to content

Row-Template v1.3.0: PasarGuard and Rebecca 1.x support, Meter and Notebook - #6

Merged
iitzSeriZdev merged 26 commits into
mainfrom
release/v1.3.0
Sep 26, 2026
Merged

iitzSeriZdev merged 26 commits into
mainfrom
release/v1.3.0

Conversation

@iitzSeriZdev

@iitzSeriZdev iitzSeriZdev commented Sep 26, 2026 •

Copy link
Copy Markdown
Owner

Row-Template v1.3.0: PasarGuard and Rebecca 1.x support, and two new designs (Meter, Notebook). The v1.2.1 fixes, which were never released on their own, ship here too.

Do not tag or publish yet. This PR is for review. The tag and the GitHub release wait for the go-ahead.

What's in it

  • PasarGuard 5.x. The installer detects it, places the page in the templates directory and selects it with a marked block in /opt/pasarguard/.env, restarting a running panel once. Uninstall returns .env to its exact previous bytes. verify reports admin sub_template and disable_sub_template overrides.
  • Rebecca 1.x (the Go edition, Rebecca's binary install). The page is selected in the newest subscription_settings row and takes effect on the next request, with no restart. Activation is automatic with SQLite and sqlite3; on MySQL/MariaDB the installer prints the one dashboard setting instead.
  • Rebecca Docker is refused. Docker Hub's rebeccapanel/rebecca:latest is still v0.0.37-alpha, the Python edition, which cannot render this page. The installer detects it and refuses before changing anything, naming rebecca migrate-binary.
  • Choosing a panel. A panel counts only when two independent signals agree. On a server with several panels, the installer asks, or reads RT_PANEL in scripts.
  • Transactional activation. Each change is snapshotted, applied and verified, and restored exactly (with a read-back) if any step fails.
  • Two new designs: Meter and Notebook, ported from the author's Pulse and Sketch. That makes 17 designs, each built for all three panels.
  • Fixes found during real-host validation, each with a test that fails without it:
    • the page check (head | grep -q under pipefail) wrongly refused a valid page about 0.7% of the time under load;
    • two backups made in the same second overwrote each other;
    • rolling back to a 1.1.0 backup left an install that failed verify;
    • on 3X-UI the live check after config/update/rollback always skipped, and right after activation it falsely warned "could not reach";
    • a correct rollback was reported as FAILED;
    • build outputs were rewritten in place while parallel tests read them;
    • a failed PasarGuard/Rebecca page refresh left sub.html replaced (raised in review, fixed in ea71ca2).

Panel capability matrix

Panel Detect Install Activate Verify Backup Restore Uninstall Status
3X-UI ≥ 3.6.0 ✅ ✅ ✅ ✅ ✅ ✅ ✅ Supported
PasarGuard 5.x ✅ ✅ ✅ ✅ ✅ ✅ ✅ Supported
Rebecca 1.x (binary) ✅ ✅ ✅ (manual on MySQL) ✅ ✅ ✅ ✅ Supported
Rebecca 0.0.x (Docker image) ✅ ❌ refused — — — — — Not supported

Validation on ea71ca2 (the merged head)

  • npm test, clean Linux: 761/761 passed, 0 skipped.
  • npm test, Windows: 754 passed, 0 failed, 7 skipped. Windows can't create symlinks, and two tests are Linux-only pipefail checks; all 7 run and pass on Linux.
  • npm run verify: all checks pass. npm run lint:sh: all 12 scripts clean. npm run build:panels: 51 shells.
  • Release packaging: two independent builds are byte-identical, row-template-1.3.0.tar.gz sha256 1b232b5ce9d95a1159e7a305ac9c8b9715b450d3cd564d1fbbf3192879005dc1. The working tree is still clean afterwards.
  • Real panels, from that exact artifact: 208/208 checks, run in a throwaway WSL2 Ubuntu 26.04 with each panel installed by its official installer.
    • 3X-UI 3.8.5: upgrade from the published v1.1.0 and v1.2.0, a fresh install, all 17 designs, rebrand, rollback including to old backups, the manager menu, and uninstall.
    • PasarGuard 5.4.1 (Docker): install, activate, all 17 designs, hostile data, override warnings, rollback, and a byte-exact uninstall.
    • Rebecca 1.x (binary): the same checks plus manual activation, and the settings row restored exactly on uninstall.

Known limitations (documented)

  • Live-status refresh is 3X-UI only; PasarGuard and Rebecca serve it on a path suffix.
  • PasarGuard's subTitle and Clash templates are not produced.
  • Rebecca on MySQL/MariaDB needs one manual setting.
  • A rollback restores the page and recorded version, not the manager itself, and only the two newest backups are kept.

Commits since v1.2.0

  • 2258162 fix(installer): repair and complete the template store after an update
  • 5f973d4 test(panels): pin PasarGuard and Rebecca support to what the installer does
  • 926b172 docs: release notes for 1.2.1 and verify's new behaviour
  • 8c88665 docs(readme): describe update and offline installs as they actually work
  • 275b140 Merge pull request docs(readme): describe update and offline installs as they actually work #5 from iitzSeriZdev/claude/tender-maxwell-quxfys
  • 9822b6c Merge v1.2.1 template-store fixes into the v1.3.0 line
  • 114bdcd fix(rollback): restore backups the template store cannot identify
  • ddc5653 fix(release): write checksums in the text form on every platform
  • 0f1fb2c feat(shells): render PasarGuard and Rebecca pages from their real context
  • 0a623e6 feat(installer): support PasarGuard and Rebecca end to end
  • 0b3d5f5 feat(templates): add Meter and Notebook, ported from the author's Pulse and Sketch
  • 21f0f7e feat(pasarguard): report the database settings that outrank the page; audit docs
  • cb0232b fix(transaction): report a verified rollback as ROLLED_BACK
  • 2ae89f5 docs: describe 1.3.0 — PasarGuard, Rebecca, Meter and Notebook
  • ec25bf3 release: prepare v1.3.0
  • cdd151a fix(backup): two backups in the same second no longer share a directory
  • ea28150 docs(changelog): record the backup-collision and live-check fixes
  • 3d89bff fix(build): never truncate a build output in place
  • 563136d fix(rollback): restore an unrecognised backup page as the installed design
  • 0cd41d6 fix(rebecca): refuse the 0.0.x Python edition instead of failing silently
  • 5ae7434 docs: Rebecca support is Rebecca 1.x, the Go edition
  • 0866c11 fix(install): run the panel preflight only for panels that have one
  • 14be1e4 fix(installer): no pipe into grep -q or head can misreport a match
  • 24245f8 style(backup): keep rt_backup_latest a bare one-liner
  • 6926e32 style(backup): give rt_backup_latest its comment inside the body
  • 87b7c7e docs(changelog): record the structural-check fix
  • dd156f2 docs: state the upgrade limits of rollback and backup retention
  • ea71ca2 fix(activate): a failed panel refresh leaves sub.html as it was

Open before release

  • The rowtest VPS presents a new host key: ED25519 SHA256:Btq8txQSVaKPvk1wzNW/FS7jeObzbv5qlGQ7wy4UCos. It hasn't been confirmed expected, so the extra real-VPS run is pending. All real-panel validation above ran in WSL2.

Not in this merge: 2131bcc (a failed first activation now leaves no sub.html behind, plus a CHANGELOG note that scripted installs on multi-panel hosts need RT_PANEL) was pushed to release/v1.3.0 after this PR was merged, so it is not in main.

🤖 Generated with Claude Code

claude and others added 25 commits September 24, 2026 22:12
The library reads every design from $RT_ROOT/dist/templates only. Two
states left that empty while the manager said "No templates are
installed. Re-run the installer":

- v1.1.0's updater installs the new library but copies only four
  files, so no design arrives with it; 1.2.0 needed a second
  `row-template update`, and `config` and the branding editors failed
  until then.
- a release payload copied or extracted over the install root leaves
  its designs at $RT_ROOT/templates, beside dist/ instead of inside it.

rt_repair_template_store heals from what the host has: a verified
payload, then a misplaced store, moving each design only after it
passes its own checksum and the structural gate, and retiring only
the copies the store now covers. It never follows a symlink and never
removes a file it does not recognise. install, update and verify call
it.

rt_complete_install finishes an install still short afterwards from a
verified download of the INSTALLED version (the default channel is
pinned to its tag; a payload of another version is refused), writing
only the store and the library's companions. The manager, `config`,
`verify` (as root) and the Template chooser call it, so one
`row-template update` from 1.1.0 is enough.

verify now names missing and corrupt designs. Test harnesses stub the
release fetch so no test can reach the network.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BbSwnyeKjBZ2HoJddxdYBM
…r does

An audit of both panels against the code: the installer has no adapter
for either (installer/panels holds only 3xui.sh), the registry resolves
both to no implementation, every panel operation returns UNAVAILABLE,
and on a host with PasarGuard or Rebecca but no 3X-UI the install
refuses and writes nothing. What exists is build-time only: page
shells in Jinja2 and pongo2, packaged under shells/; data adapters
tested against samples written from each panel's source; and rendering
tests through a test-only renderer, not a real panel.

Both therefore stay research targets. The compatibility pages (en, fa,
ar) now carry a per-capability matrix and list what exists and what is
missing, and tests/panel-support.test.mjs derives each panel's status
from the installer and checks every README, compatibility page and
the changelog against it, so a panel cannot be documented as supported
because a file for it exists.

Also drops "supported" from two places that meant "buildable".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BbSwnyeKjBZ2HoJddxdYBM
1.2.0 is already published with the upgrade defect, so the fix is
prepared as 1.2.1: VERSION, a changelog entry (fixed, changed, known
issues, upgrading), and 1.2.0's release date. The 1.2.0 note about
needing two updates is kept as history and marked fixed.

verify is no longer strictly read-only: as root it repairs the
template store before checking it. Every README and docs page that
called it read-only now says exactly what it may change, in each
language. The configuration page and README describe the one-update
path from 1.1.0, and troubleshooting (en, fa) covers the chooser's
empty-store message and verify's new store messages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BbSwnyeKjBZ2HoJddxdYBM
Brings in the unreleased 1.2.1 work (template store repair after an
update, PasarGuard/Rebecca support pins, 1.2.1 release notes) on top of
the merged README corrections from PR #5.

README conflicts resolved in all five languages: keep PR #5's accurate
description of `row-template update` (it always applies the latest stable
release; only the manager's Update compares versions) and keep 1.2.1's
new "Updating from 1.1.0" bullet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A backup written by 1.1.0 has no template= line in its meta, and its
artifact may match no design in the current store (the store was
re-staged, or the design has since changed). rt_restore_from_backup
refused such a backup outright, which blocked `row-template rollback`
on a host freshly updated from 1.1.0.

Resolution order is now: checksum match against the store, then the
template recorded in the backup's meta when this release still offers
it, then Row. The restored artifact and the persisted selection still
always agree; an unknown recorded id falls back to Row with a warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sha256sum under Git Bash and Cygwin prints the binary-mode form
'<hex> *<name>', so a release built on Windows carried SHA256SUMS lines
that differ from a Linux build and failed the release test that pins the
coreutils text form. The release script now normalizes every checksum
line to '<hex>  <name>'; the installer already accepted both forms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…text

The PasarGuard and Rebecca shells were transpiled layouts that read
3X-UI's variable names (enabled, downloadByte, expire, ...). Neither panel
supplies those names, so on a real panel every figure on the page would
have rendered empty. Each shell now starts with a prelude that derives
every name the layout reads from the context the panel really renders
with:

- PasarGuard (Jinja2): user (a model object), links, announce, now().
- Rebecca (pongo2): user (a map), links, support_url, current_timestamp.
  Its online_at is a zoneless UTC string and pongo2 cannot parse dates,
  so the civil date is converted with integer arithmetic.

Escaping: PasarGuard builds its Jinja2 Environment without autoescape,
so a username, link remark or announcement was written into the page as
raw HTML. The body of every shell is now wrapped in an explicit
autoescape block (Jinja2 and pongo2), and the build refuses any asset
that contains a template delimiter, because both panels parse the whole
file, inline CSS and JavaScript included.

on_hold is no longer refused (a served page cannot refuse). Decided in
docs/design/PANEL-ON-HOLD-DECISION.md: enabled, and on PasarGuard the
clock starts on first connection for the hold duration; without a known
duration the expiry is unknown, never "never expires".

Adapter fixes found by the audit: PasarGuard's status table lacked the
real `limited` and `expired` statuses, and its Clash link used
`/clash-meta` where PasarGuard's route is `clash_meta` (and may be
disabled), so the link is now omitted.

tests/panels-engines.test.mjs renders the shipped shells with real
Jinja2 (configured as PasarGuard configures it) and real pongo2 v6.1.0
(Rebecca's pinned version): every design, every fixture, 1,624 online_at
conversions under a non-UTC TZ, and hostile data on both panels. The
harnesses are independent implementations, since both panels are
AGPL-3.0; they are test tooling and never ship.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds installer/panels/pasarguard.sh and installer/panels/rebecca.sh
behind the frozen seven-verb panel interface, and routes install,
update, rollback, verify, config, the manager and uninstall through them.

PasarGuard (audited against its source and official installer): the page
is placed at <CUSTOM_TEMPLATES_DIRECTORY>/row-template/index.html and
selected by a Row-Template-managed block appended to /opt/pasarguard/.env
(dotenv takes the last assignment, so no operator line is edited).
Removing the block is the exact inverse, byte for byte, including a
missing final newline. The container is recreated only when it was
running and only when its environment changed; later page updates never
restart it. A templates directory outside the bind-mounted data dir is
refused, as is overwriting a page that is not Row-Template's.

Rebecca: the page is selected by subscription_page_template and
custom_templates_directory in the newest subscription_settings row, which
Rebecca reads on every request, so it is never restarted. Only a sqlite:
database inside the data dir is used (a MySQL URL is never read out
loud); without sqlite3 activation is manual with exact dashboard steps.
NULL, '' and a value are restored exactly, by rollback and by uninstall
(from the snapshot taken before first activation).

Infrastructure:
- installs record their panel (RT_ROOT/PANEL); PasarGuard and Rebecca
  installs live at /etc/row-template, 3X-UI keeps its root; the CLI finds
  whichever exists
- the P2 snapshot gains an optional `aux` record (base64 values under a
  closed key grammar) for settings beyond the primary selection
- backups record their panel, and a restore refuses another panel's
- every artifact must fit its panel: a Go template is refused on
  PasarGuard/Rebecca, and so is a shell without the 1.3.0 context prelude
  and autoescape block
- rt_json_escape now escapes { and }: branding can never form a template
  delimiter (on PasarGuard's unsandboxed Jinja2 that would be code
  execution)
- after a failed activation, the panel's state is re-captured and
  compared with the snapshot, so the operator is told the truth when the
  restore was exact (the engine itself stays conservative)

Tests: installer-panel-pasarguard (19) and installer-panel-rebecca (12)
run the shipping code against fake hosts in the official layouts, with
docker and sqlite3 doubled only as external programs. Suites that pinned
"3X-UI is the only implemented panel" keep every property they protected,
now proven by removing an adapter from the build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…se and Sketch

The two designs the project's author contributed privately become core
templates 16 and 17, on the shared runtime exactly like the other fifteen:
own layout.html with all 79 hooks once, the three shared scripts and locale
island byte for byte, both themes, RTL, the 44px tap floor, inside the
200 KiB refusal point, and byte-locked (size + SHA-256).

They ship as `meter` and `notebook` because `pulse` and `sketch` are already
core ids. The installer chooser, the Bash registry projection, the build,
the panel shells (3X-UI, PasarGuard, Rebecca), the fixtures, the docs
gallery and the captured previews all carry them; every count that said
fifteen now says seventeen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… audit docs

PasarGuard serves an admin's own `sub_template` to that admin's users, and
serves no page at all when `disable_sub_template` is on
(app/operation/subscription.py). Row-Template never changes either, but
static verify now reads them -- read-only, SQLite only, the URL never
printed -- and warns, so a page that "does not show" is explained.

Rebecca's suite gains the refusals PasarGuard's already proved: a foreign
page, a bad restore record, a tampered page and a moved selection, secrets
anywhere under the install root, 3X-UI/PasarGuard/pre-1.3.0 artifacts, and
a backup from another panel.

PASARGUARD-INSTALLER-AUDIT.md and REBECCA-INSTALLER-AUDIT.md record what
each panel does, from source, and how the adapter follows it; the backup
design documents the optional `aux` record.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After restoring, the engine re-ran the forward static check ("does this
panel serve Row-Template?"). A rollback restores the panel's previous
selection, so that answer is no by design: every correct rollback was
recorded FAILED and logged as rollback-failed.

The restore now verifies itself, where interface.sh already put that
obligation: each adapter's restore_state reads the state back and compares
it with the record (3X-UI subThemeDir; PasarGuard's .env block and
effective page key; Rebecca's two subscription_settings columns). The
engine trusts that status and no longer asks the forward question. A
restore that reports FAILURE or UNAVAILABLE is still a failed rollback.

Tests pin both directions and the call sequence, so the forward check
cannot come back after a rollback unnoticed.

This change was made in this working tree by a separate Cline session; it
was reviewed and the full suite run before committing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
READMEs (five languages) and the docs site (English, Persian, Arabic) now
describe all three panels as supported: what activation changes on each,
the manual Rebecca step for MySQL/MariaDB, RT_PANEL on multi-panel hosts,
uninstall per panel, the upgrade path from 1.1.0 and 1.2.x, and the known
limits (no live refresh on PasarGuard/Rebecca, no subTitle/Clash). Every
command, path and URL is byte-identical across the translations.

The compatibility matrix now has the seven capability columns (detect,
install, activate, verify, backup, restore, uninstall), and the test that
holds it to the installer checks all seven; "Supported" needs every one.

CHANGELOG gains the 1.3.0 section (1.2.1 was never released on its own and
ships inside it); PROVENANCE covers the per-panel pages and where Meter and
Notebook came from. The PasarGuard manual-activation text now names both
keys and where to copy the page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Backup names have one-second resolution and rt_backup_create used
`mkdir -p`, which reuses an existing directory. A design switch followed at
once by `rollback --auto` (which snapshots the current state first) put both
backups in one directory: the pre-rollback snapshot overwrote the backup it
then restored, so the rollback re-applied the state it was meant to undo.
Found running the suite on Linux, where the Rebecca lifecycle test is fast
enough to hit it every time. The name is now claimed with a plain mkdir and
a taken name waits for the next second, as the format-2 writer already did.

fix(verify): the live check after config, update and rollback runs on 3X-UI

Those commands printed the live check without having located the panel
database, so on a real 3X-UI host (validated on 3.8.5) it always said
"skipped (no test URL available without sqlite3)" with sqlite3 installed.
The report now locates the database itself, read-only. And the check made
right after activation no longer warns "could not reach": 3X-UI's
subscription server binds a few seconds after the unit is active, so the
check waits for it -- only when the unit really started in the last 30 s.

Each fix has a regression test that fails without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The suite runs its test files in parallel, and two of them rebuild the
committed artifacts (the release test through make-release.sh, the preview
test directly) while other files read template/index.html. writeFileSync
truncates before writing, so on Linux installer tests intermittently read an
empty or partial page ("generated template does not begin with
<!doctype html>"), failing a different test on each run.

build.mjs, build-panel.mjs and shell.mjs now write through writeIfChanged:
bytes already on disk are not rewritten at all (the build is deterministic),
and new bytes are renamed into place from a sibling temporary file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…esign

Rolling a real 3X-UI host back to the backup its 1.1.0 install left selected
Row but kept 1.1.0's own page bytes, which match no design in the 1.3.0
store. The install then failed verify ("canonical artifact does not match
the selected template") and could not be switched or updated cleanly.

When a backup's page matches no installed design, the selected design (the
backup's recorded one, else Row) is now restored from the store, so the
selection, the canonical artifact and the store agree. The backup's VERSION
and the admin's current branding are handled exactly as before. Without a
store (nothing to restore from) the backup's bytes are still used.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ntly

Validated on real hosts: Rebecca 1.x (the Go edition, published as a binary
through rebecca-binary.sh) serves the Row-Template page exactly as designed.
But Docker Hub's rebeccapanel/rebecca:latest -- what rebecca.sh's Docker
install pulls -- is still v0.0.37-alpha, the Python (FastAPI + Jinja2)
edition. There the selection was written and accepted, the pongo2 page could
not render, and Rebecca silently served its own page: an install that
reported success and changed nothing a subscriber saw.

The adapter now establishes the edition first -- a binary install is 1.x; a
Docker one is told apart by its image's entrypoint (rebecca-server vs a
script under /code) -- and refuses anything but 1.x, naming Rebecca's own
`rebecca migrate-binary` as the way forward. The installer and the
manager's Activate check it before anything is written (rt_panel_preflight);
capture, install_template and static verify refuse it too, and an existing
install's refresh refuses an edition it knows cannot serve the page. An
edition that cannot be identified fails closed at install time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rebecca publishes 1.x only for its binary install; Docker Hub's
rebeccapanel/rebecca image is still the 0.0.x Python edition, which cannot
render this page and is now refused. The READMEs (five languages), the
compatibility, installation and troubleshooting pages, the CHANGELOG and the
Rebecca installer audit say so, and name Rebecca's own
`rebecca migrate-binary` as the way from Docker to 1.x.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A 3X-UI install can run from the management library alone, before its panel
adapters are installed (the v1.1.0 updater leaves exactly that). Calling
rt_panel_preflight there failed with 'command not found'. 3X-UI has no
preflight; the other panels always load their adapter to be detected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The structural gate checked a page with `head -c 512 f | grep -qi
'<!doctype html>'`. grep -q exits on its first match; head, still writing,
dies of SIGPIPE; pipefail then reports the MATCH as a failure. Measured on a
loaded Linux host at about 0.7% of calls (PIPESTATUS 141 0), it made install,
update and design switching refuse a valid page -- and made the suite fail a
different test on each run. This is the pipefail+SIGPIPE class the project
already fixed once in rt_detect_xui.

Every such pipe in the installer is now pipe-free or reads its whole input:
the gate's head and tail checks (tr), the control-character check and the
config-permission check (bash patterns), rt_backup_latest, the smoke-URL
and companion-list parsers (sed -n 1p), and PasarGuard's block-marker lookup
(grep -m1). A deterministic test forces the old interleaving with doubles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The backup-writer tests extract rollback's functions to prove P2 left them
alone, and a trailing comment broke the extraction. The comment now sits
above the function.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A comment line between two functions became the tail of the previous one's
extracted body in the backup-writer tests. All 36 of them pass again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A rollback restores the page and the recorded version, not the manager: after
rolling back to a 1.1.0 backup, `row-template version` reports 1.1.0 while the
1.3.0 manager stays, and the next update returns to 1.3.0. Only the two
newest backups are kept, so the backup an update takes of the previous
version is replaced after two further changes. The CHANGELOG's Upgrading
section and the Rolling back docs (English, Persian, Arabic) now say so, and
the 1.3.0 entry is dated for the release preparation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@graphify-labs graphify-labs Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 5 advisory finding(s) below merit a look before merge.

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Documents the 1.3.0 release in the changelog, describing PasarGuard and Rebecca panel support alongside 3X-UI, two new designs (Meter and Notebook), panel auto-detection with RT_PANEL override, and transactional activation across all three panels. Records the fixes rolled in from the unreleased 1.2.1 — one-shot updates from 1.1.0, backup rollback compatibility, and reporting a restored rollback as a success instead of re-running the forward check — plus security notes on per-panel escaping and brace-escaping of branding values. Backs these with test, tooling, and multi-language documentation updates, including real-engine rendering of the PasarGuard/Jinja2 and Rebecca/pongo2 pages.

Worth a look

  • verify now mutates the installation root — installer/lib/row-template.sh:3283 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • rt_activate can fail after replacing the live file — installer/lib/row-template.sh:2205 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Concurrent aux updates can overwrite each other — installer/lib/row-template.sh · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Backup directory is published before the snapshot is complete — installer/lib/row-template.sh:1055 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • PasarGuard/Rebecca install support is documented inconsistently — PROVENANCE.md:25 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1368 functions depend on the 1368 functions this change touches.

Health — this change adds coupling hotspots:

  • new: assembleShell() — 10 callers, 11 callees
  • new: build() — 12 callers, 7 callees
  • new: rt_repair_template_store() — 4 callers, 10 callees
  • new: rt_complete_install() — 4 callers, 9 callees
  • new: rt_activate() — 7 callers, 4 callees
  • new: rt_restore_from_backup() — 3 callers, 9 callees
  • new: rt_set_dist() — 5 callers, 5 callees
  • new: rt_render_report() — 6 callers, 4 callees
  • …and 33 more — each is listed as a finding

Verification — 1368 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 1368 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify island.

The verifier did not have enough to check island, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 23 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous

Could not verify: Could not verify lastOnline.

The verifier did not have enough to check lastOnline, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: could not isolate function

Could not verify: Could not verify island.

The verifier did not have enough to check island, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 23 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous

Could not verify: Could not verify buildPanelShell.

The verifier did not have enough to check buildPanelShell, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 23 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous

Could not verify: Could not verify buildOne.

The verifier did not have enough to check buildOne, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 23 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous

· 12 grounded finding(s) anchored inline below; 29 more finding(s) on lines outside this diff (see the check run).

Comment thread installer/lib/row-template.sh
Comment thread installer/lib/row-template.sh
Comment thread installer/lib/row-template.sh
Comment thread installer/lib/row-template.sh
Comment thread installer/lib/row-template.sh
Comment thread installer/lib/row-template.sh
Comment thread installer/lib/row-template.sh
Comment thread installer/lib/row-template.sh
Comment thread installer/lib/row-template.sh
Comment thread installer/panels/pasarguard.sh
Raised in review on PR #6. rt_activate swaps sub.html and then refreshes the
copy PasarGuard or Rebecca serves. When that refresh failed it returned an
error with sub.html already replaced -- contradicting its own contract, so a
caller reporting "nothing was changed" was wrong. The previous sub.html is
now kept aside and put back on that failure; a test pins it (and fails
without the fix). The installer-components check also moved ahead of the
swap, so it can no longer fail after a change.

PROVENANCE: shells/ carries every panel's pages, and shells/3xui/ is
byte-identical to templates/, which is what 3X-UI installs use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@iitzSeriZdev

Copy link
Copy Markdown
Owner Author

Disposition of the five advisory findings in the Graphify review summary:

  1. verify mutates the installation root. Intentional since 1.2.1: run as root, verify puts misplaced or missing designs back into the template store and changes nothing else. This is documented in the 1.2.1 CHANGELOG ("Changed") and in the README command table. Not changed.
  2. rt_activate can fail after replacing the live file. Fixed in ea71ca2. When the PasarGuard or Rebecca refresh fails, the previous sub.html is now put back before returning, so a caller reporting "nothing was changed" is correct. There's a regression test, which fails without the fix. (The page the panel serves was never changed in that path.)
  3. Concurrent aux updates can overwrite each other. aux is written only inside a transaction that holds the single-flight flock, by one adapter, sequentially, so there is no concurrent writer. Not changed.
  4. Backup directory published before the snapshot is complete. A partial backup is never used: rt_backups_list lists only directories that pass rt_backup_validate, whose artifact must match its SHA-256 sidecar. The name is claimed atomically (cdd151a). Not changed.
  5. PROVENANCE documents panel support inconsistently. Fixed in ea71ca2. The shells/ row now covers every panel, says Rebecca 1.x, and notes that shells/3xui/ is byte-identical to templates/.

After ea71ca2: npm test 761/761 on Linux with 0 skipped; verify, lint:sh and build:panels clean. The release artifact is reproducible, sha256 1b232b5ce9d95a1159e7a305ac9c8b9715b450d3cd564d1fbbf3192879005dc1.

🤖 Addressed by Claude Code

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 5 advisory finding(s) below merit a look before merge.


Graphify review — findings

Adds full support for installing Row-Template on PasarGuard and Rebecca (1.x) alongside 3X-UI — detect, install, activate, verify, back up, restore and uninstall — with panel detection that requires two agreeing signals, an RT_PANEL override for scripts, and transactional activation that snapshots, changes, verifies, and exactly restores panel state on any failure. Ships Meter and Notebook designs and per-panel Jinja2/pongo2 pages for all seventeen designs, escapes every branded value (including neutralising {/}) against the panels' real non-autoescaping engines, and keeps panel secrets out of logs and backups. Fixes rollback so a successful restore is reported as success (the engine no longer re-runs the forward check, each adapter re-verifies its own recorded setting), makes rolling back a 1.1.0 backup work by falling back to Row, resolves same-second backup-name collisions, and hardens the head|grep structural check so a valid page is never spuriously refused under load.

Worth a look

  • verify command is no longer read-only when run as root — README.md:244 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Failed non-3xui activation leaves a newly-created live file behind — installer/lib/row-template.sh:2223 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Noninteractive installs no longer default to 3x-ui when another supported panel is present — installer/lib/row-template.sh:3026 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Concurrent aux updates can lose keys — installer/lib/row-template.sh · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Backup name retry cap makes concurrent backups fail — installer/lib/row-template.sh:1068 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1368 functions depend on the 1368 functions this change touches.

Health — this change adds coupling hotspots:

  • new: assembleShell() — 10 callers, 11 callees
  • new: build() — 12 callers, 7 callees
  • new: rt_repair_template_store() — 4 callers, 10 callees
  • new: rt_complete_install() — 4 callers, 9 callees
  • new: rt_activate() — 7 callers, 4 callees
  • new: rt_restore_from_backup() — 3 callers, 9 callees
  • new: rt_set_dist() — 5 callers, 5 callees
  • new: rt_render_report() — 6 callers, 4 callees
  • …and 33 more — each is listed as a finding

Verification — 1368 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 1368 function(s) in the blast radius were not formally verified this run

· 12 grounded finding(s) anchored inline below; 29 more finding(s) on lines outside this diff (see the check run).

Comment thread installer/lib/row-template.sh
Comment thread installer/lib/row-template.sh
Comment thread installer/lib/row-template.sh
Comment thread installer/lib/row-template.sh
Comment thread installer/lib/row-template.sh
Comment thread installer/lib/row-template.sh
Comment thread installer/lib/row-template.sh
Comment thread installer/lib/row-template.sh
Comment thread installer/lib/row-template.sh
Comment thread installer/panels/pasarguard.sh
@iitzSeriZdev
iitzSeriZdev merged commit 857d2a7 into main Sep 26, 2026
3 checks passed
iitzSeriZdev pushed a commit that referenced this pull request Sep 26, 2026
Raised in the second review of PR #6: when there was no sub.html before and
the PasarGuard/Rebecca refresh failed, rt_activate restored nothing and left
the new file behind. It now removes it, so the previous state -- including
"no page yet" -- is exactly what remains. The test covers both cases.

CHANGELOG: a scripted fresh install on a server with more than one supported
panel now needs RT_PANEL (1.2 installed for 3X-UI there, as the only panel it
supported). Re-runs and updates keep the install's own panel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@iitzSeriZdev

Copy link
Copy Markdown
Owner Author

Disposition of the second Graphify review's summary findings (head ea71ca2):

  1. verify is no longer read-only when run as root. This is intended and documented. The README command table says "as root, it also puts back missing or misplaced designs", and the 1.2.1 CHANGELOG ("Changed") describes it. Not changed.
  2. A failed non-3xui activation leaves a newly created live file behind. Fixed in 2131bcc. When there was no sub.html before and the PasarGuard/Rebecca refresh fails, the new file is now removed. The regression test covers both cases, and this one fails without the fix.
  3. Non-interactive installs no longer default to 3x-ui when another panel is present. This is deliberate: on a server with several supported panels the installer won't guess which one was meant. It now appears under "Changed" in the 1.3.0 CHANGELOG (2131bcc). Re-runs and row-template update keep the install's own panel.
  4. Concurrent aux updates can lose keys. aux is written only inside a transaction that holds the single-flight flock, by one adapter, sequentially. Not changed.
  5. The backup-name retry cap makes concurrent backups fail. A name that's taken waits for the next second, up to three times, the same bound as the format-2 writer. Hitting the cap needs four backups colliding in consecutive seconds, and when it does the command fails cleanly, with no backup overwritten. Not changed.

At 2131bcc: npm test 761/761 on Linux with 0 skipped; verify, lint:sh and build:panels clean. The release artifact is reproducible, sha256 48bc29fde7ddf0207d1cf035a916d948ca6137ba2a3cb6d69cd21c9c6adaf5ca.

🤖 Addressed by Claude Code

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.

2 participants