Skip to content

Say the desktop app is not maintained, instead of selling a March build - #358

Merged
Apolloccrypt merged 2 commits into
mainfrom
feat/download-page
Sep 2, 2026
Merged

Say the desktop app is not maintained, instead of selling a March build#358
Apolloccrypt merged 2 commits into
mainfrom
feat/download-page

Conversation

@Apolloccrypt

@Apolloccrypt Apolloccrypt commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Two reviewers rejected /download on 2026-09-02, outside the auth PR. The page
was internally contradictory: it opened with "THE DESKTOP APP IS OUT OF DATE.
USE THE WEB APP." and then ran a full product page with four download buttons
underneath it. It also opened on jargon, listed detail without meaning, and made
a signing claim about the installers that could not be checked.

The facts first, because they decide the message

I measured before writing, against the artifacts rather than the release notes.

Last release v0.2.1, 28 March 2026. Two assets: the Linux .deb and an Android .apk. None has followed it.
Last commit 28 March 2026, on the default branch.
Build pipeline None. Apolloccrypt/paramant-app has no .github/workflows at all, so nothing builds or signs automatically.
Versions on the page The .deb is v0.2.1. The .rpm, AppImage and .exe are still v0.2.0 (26 March 2026). No single version number covers the list.
What the app is The Paramant messenger (Tauri 2 + Rust), not ParaSign or ParaSend. It is not part of the two-product line the site sells.
Reachability No page on the site links to /download. It is in sitemap.xml:45, so it is reached from search or a bookmark.

Signing, measured on the published files (all four are byte-identical to the
release artifacts, verified by sha256):

Installer Signature How it was measured
paramant_0.2.1_amd64.deb none, and it used to have one ar t lists only debian-binary, control.tar.gz, data.tar.gz. A signed Debian package carries a fourth member: _gpgbuilder from dpkg-sig, or _gpgorigin from debsigs. Neither is there. The v0.2.0 package does carry _gpgbuilder.
PARAMANT-0.2.0-1.x86_64.rpm none rpm -qpi reports Signature : (none); digests only.
PARAMANT_0.2.0_amd64.AppImage none .sha256_sig (1024 B) and .sig_key (8192 B) exist but are all zeroes, the placeholders appimagetool leaves when nothing signs.
PARAMANT-0.2.0-windows-signed.exe Authenticode, self-signed Certificate table 1704 B. Subject and issuer are both CN=Mick Beer, O=PARAMANT, C=NL.

So one of four carries a signature and it vouches for itself, which proves
nothing to someone who does not already hold the certificate.

A regression, not a false claim, and I had this backwards at first. The
v0.2.0 release notes say [DEB] GPG signed - GOODSIG 6EF8E5AC.... That is
accurate: the v0.2.0 package carries a _gpgbuilder member holding a clearsigned
manifest whose Signer: is exactly 6EF8E5ACC444949E5A2EAA65CCE2378929A49B97.
The signature disappeared in v0.2.1, which is the package this page serves.

My first pass checked only for _gpgorigin and concluded the notes had
overclaimed. That was wrong, and a reviewer caught it with the evidence. There
are two tools and they write different members: dpkg-sig writes _gpgbuilder,
debsigs writes _gpgorigin.
Checking one and declaring a package unsigned is
the mistake, so the page and the test now name both.

The wider lesson, which is why the test is shaped the way it is: for a negative
finding you first have to know how many shapes the positive case has. One
command that returns nothing is not proof of absence.

The message I chose, and why

"The desktop app is not maintained, use the web app." Not a product page.

Five months without a release, no pipeline that could produce one, four
installers across two versions, one self-signed signature, and a product that is
not in the current line. A product page would have to imply a maintained thing.
The honest version is also the shorter one, and it is the only one that survives
docs/brand/messaging.md: a sentence may go on the site only if it is already
true and a test fails when it stops being true.

The installers stay, but demoted: a collapsed archive with a warning, the
measurement, and the checksum beside each file. Someone who has a reason for the
old build can still get it and still verify it. Nobody is sold it.

Dropped: "missing 21 protections the web app has." The count was internally
consistent with its own list, but nothing in either repo can check it, and
docs/site-claims.md:78 already recorded it as unpinnable. It is replaced by
facts that can be checked: the release date, the version split, and the
signature measurements above. Also gone, as the reviewers asked: the kicker
// NATIVE_APP_DOWNLOAD, "ML-KEM-768 and AES-256-GCM" above the fold, the
nickname entropy line, and x-forwarded-for read from the last hop.

The page now

Following messaging.md sections 6 and 7. First screenful, confirmed by
screenshot at 390x844: kicker, H1, a two sentence sub, two buttons, nothing
else.

The Paramant desktop app is no longer maintained
The last build is from March 2026. Paramant runs in your browser instead, with
nothing to install and nothing to keep up to date.
[Open the web app] [See pricing]

Then, in the guide's fixed order: what happened, the free/paid split quoted from
/pricing, the founder line, and the proof. All technical material sits in two
collapsed blocks at the foot, with the commands a reader can run themselves.
Since the source repo is private there is no public commit log to link, so the
control links are the artifacts and /dl/SHA256SUMS, which anyone can check.

Three defects fixed along the way

  1. The file was truncated mid-word. It ended at geen installatie verei
    with no </main>, </body> or </html>. apply-nav.py bails on a missing
    </body>, so /download shipped without nav.js and nav-auth.js: the
    hamburger did nothing and the signed-in state never resolved. No test caught
    this, so one was added.
  2. A second sticky nav. nav.p-nav with stale Dutch-era links
    (/#sectoren, /#integratie) and its own inline CSS, unique to this page,
    stacked under the real nav. Removed.
  3. Class collisions. .hero, .lede, .cta-row and .warn all exist in
    design-system.css, which loads after the page block and silently won:
    .hero forced 128px of top padding on mobile, and inline links rendered in
    body colour so they were invisible as links. Page local classes are prefixed
    dl- now.

The page was also half Dutch under <html lang="en">. It is English throughout.

Tests

New section at the foot of tests/ui-truthfulness.test.mjs pinning:

  • the status sentence, with v0.2.1 and 28 March 2026
  • the four installers as filename plus version plus measured sha256, with a
    deepEqual that rejects a fifth /dl/ link or a missing one, and a ban on
    linking the .apk
  • the signing truth: the yardstick must name both _gpgbuilder and
    _gpgorigin; every sentence claiming a signature must name the release it
    holds for; the served v0.2.1 .deb must be described as unsigned; the
    self-signed disclosure is required; no "verified publisher" implication
  • a first screenful jargon gate, which did not exist before. The task
    assumed one; grep -ril jargon returns nothing repo-wide. The nearest thing
    was seo-contract.test.mjs:483, which only reads the title and the first
    sentence of the meta description. The new gate slices <main> to the end of
    <header class="dl-lead"> and rejects crypto names, standard numbers, build
    vocabulary, internal header names and the words messaging.md bans outright.
  • that the document is complete and references its nav scripts

Eight sabotage cases were verified to fail: reverting the H1, putting
ML-KEM-768 back in the hero, inventing a 0.3.0 rpm, truncating the file,
narrowing the yardstick to one member name, detaching a signing claim from its
release, erasing the regression sentence, and marking the served .deb signed.

tests/seo-contract.test.mjs pins the title and description word for word, so
the download entry in PINNED moves with the page.

All green: links, seo-contract (14), ui-truthfulness, site-claims (11),
frontend-loading-contract (7), navigation-shell (25 checks), csp-inline,
cache-bust, eslint, static-sanity (10/10, no warnings).
python3 bron-seo/apply_seo_head.py --check reports would change: 0 pages.

@Apolloccrypt
Apolloccrypt force-pushed the feat/download-page branch 2 times, most recently from 09fede0 to 919aadc Compare September 2, 2026 18:44
@Apolloccrypt

Copy link
Copy Markdown
Owner Author

Rebased on main (7c07b99).

Two things worth flagging for review.

1. The two "already been declared" errors on this branch are not from this PR. They arrived on main through parallel merges and a hotfix is on the way:

  • relay/test/pricing-page.test.js:498 , const tiers (also at line 18)
  • tests/ui-truthfulness.test.mjs:821 , const pricingVisible (also at line 520)

Line 821 sits in the messaging-guide block that came from main, not in the /download section this PR adds. With only that pre-existing duplicate resolved locally, every section of the file passes, including main’s messaging-guide block and the new one:

ui-truthfulness: the messaging guide claims are pinned to the pages that make them true
ui-truthfulness: /download says the desktop app is unmaintained, and the archive matches the artifacts

2. This PR’s new test block is wrapped in a bare block scope on purpose, so it can never contribute to that class of collision. tests/ui-truthfulness.test.mjs is a flat script, so every const in it is a top-level binding and two sections landing from parallel branches clash by name. Nothing in the /download section leaks a binding.

Rest of the suite on the rebased branch: links, seo-contract 14, site-claims 11, frontend-loading-contract 7, navigation-shell 25 checks, csp-inline, cache-bust, static-sanity 10/10 with no warnings, and apply_seo_head.py --check reporting would change: 0 pages. eslint reports only the two parsing errors above.

@Apolloccrypt

Copy link
Copy Markdown
Owner Author

Update: rebased on main (faf599e) now that #359 landed the deduplication. Both "already been declared" errors are gone, so the note above is resolved and nothing in it is outstanding.

The new test block is now a named IIFE, (function pinDownloadPage() { ... })();, matching the convention #359 settled for this file.

All ten checks pass on head 3b38478. Local battery alongside CI: links 2, seo-contract 14, ui-truthfulness, site-claims 11, frontend-loading-contract 7, navigation-shell 26 checks, csp-inline, cache-bust, eslint clean, static-sanity 10/10 with no warnings, and apply_seo_head.py --check reporting would change: 0 pages.

The five sabotage cases were re-run after the rebase and all still fail as intended: reverting the H1, putting ML-KEM-768 back in the hero, inventing a 0.3.0 rpm, restoring the GPG wording, and truncating the file.

@Apolloccrypt

Copy link
Copy Markdown
Owner Author

The reviewer is right and I had it backwards. Corrected in cf7f10f.

I reproduced the evidence before changing anything:

$ ar t PARAMANT_0.2.0_amd64.deb      $ ar t paramant_0.2.1_amd64.deb
debian-binary                        debian-binary
control.tar.gz                       control.tar.gz
data.tar.gz                          data.tar.gz
_gpgbuilder                          <- gone

$ ar p PARAMANT_0.2.0_amd64.deb _gpgbuilder
-----BEGIN PGP SIGNED MESSAGE-----
Version: 4
Signer: 6EF8E5ACC444949E5A2EAA65CCE2378929A49B97
Date: Thu Mar 26 04:17:06 2026
Role: builder

That fingerprint is exactly the GOODSIG in the v0.2.0 release notes, so the notes were accurate. The signature vanished between v0.2.0 and v0.2.1, and v0.2.1 is what /dl/ serves. It is a build regression, not an overclaim.

My error was the yardstick: I checked only for _gpgorigin, which is what debsigs writes, while dpkg-sig writes _gpgbuilder. The page and the test now name both, precisely so the next reader cannot repeat it.

Changed

  • The .deb signature row now reads "None, and it used to have one", names both members as the yardstick, and keeps the ar t command.
  • The correction paragraph is rewritten as the regression: v0.2.0 signed with dpkg-sig, signer quoted, notes accurate, v0.2.1 has no such member, and it went unnoticed because nothing checked it.
  • Test comment (now around line 1104) rewritten with the same account, including why the first pass was wrong.
  • The blunt GPG signed ban is gone; it would have forbidden the true sentence. Replaced by three sharper rules: the yardstick must name both members, every sentence claiming a signature must name the release it holds for, and the served v0.2.1 .deb must be described as unsigned.
  • Commit body and the PR description above corrected.

Re-measured the other three with the same care, since one wrong yardstick is reason to distrust the rest. All unchanged:

  • .rpm , rpm -qpi gives Signature : (none), and rpm -Kv lists only Header and Payload SHA256 digests, no PGP line.
  • .AppImage , .sha256_sig (1024 B) and .sig_key (8192 B) are all zeroes.
  • .exe , Authenticode, certificate table 1704 B, subject and issuer both CN=Mick Beer, O=PARAMANT, C=NL.
  • All four served files still byte-identical to the release artifacts by sha256.

Rebased on main (6e4b73b). Sabotage battery is now eight cases, all failing as intended, including the four new signing ones. Local: links 2, seo-contract 14, ui-truthfulness, site-claims 11, frontend-loading-contract 7, navigation-shell 26, csp-inline, cache-bust, eslint clean, static-sanity 10/10, apply_seo_head.py --check reporting would change: 0 pages.

Two reviewers rejected /download on 2026-09-02. The page opened with "THE
DESKTOP APP IS OUT OF DATE. USE THE WEB APP." and then ran a full product
page with four download buttons underneath, so it argued with itself. It
also led on jargon, and it made a signing claim that did not hold.

The facts settle which of the two messages the page keeps. The last
release of the app is v0.2.1, published 28 March 2026; none has followed
it, and the repository has no workflow that would build one. The .deb on
the page is v0.2.1 and the other three installers are still v0.2.0, so
there is no single version number for the list. The app is also a
messenger, not ParaSign or ParaSend, so it is not part of what the site
sells today. It is an archive, and the page now says so once, at the top.

Signing, measured on the published artifacts:

  .deb 0.2.0  dpkg-sig signature present: a _gpgbuilder member holding a
              clearsigned manifest, signer
              6EF8E5ACC444949E5A2EAA65CCE2378929A49B97.
  .deb 0.2.1  no _gpgbuilder and no _gpgorigin. Not signed.
  .rpm        rpm -qpi reports "Signature : (none)"; rpm -Kv shows only
              header and payload digests.
  .AppImage   .sha256_sig and .sig_key exist but are all zeroes.
  .exe        Authenticode, certificate table 1704 bytes, but subject and
              issuer are both CN=Mick Beer, O=PARAMANT, C=NL.

So the Debian package lost its signature between v0.2.0 and v0.2.1, and
v0.2.1 is the one this page serves. That is a regression in the build,
not a release note that overclaimed: the fingerprint in the v0.2.0 notes
is exactly the one in the artifact. A first version of this commit had it
the other way round, because it checked only for _gpgorigin, the member
debsigs writes, and dpkg-sig writes _gpgbuilder instead. A reviewer caught
it with the evidence. The yardstick on the page and in the test names both
members now, so the next reader cannot repeat it.

What the page does now, following docs/brand/messaging.md: the first
screenful says what this is, who it is for and what to do next, and it
carries only an H1, a two sentence sub and two buttons at 390px. The
split, the founder line and the proof follow in that order. The
installers are still reachable, collapsed into an archive with the
measurement and the checksums beside them, so anyone who needs the old
build can still verify what they downloaded.

Three defects fixed along the way. The file was truncated mid-word with
no closing main, body or html tag, which is why apply-nav.py could never
stamp nav.js or nav-auth.js on it and the hamburger on /download did
nothing. A second sticky nav, unique to this page, sat under the real
one. And .hero, .lede, .cta-row and .warn all collide with
design-system.css, which silently overrode the mobile spacing and
flattened inline links to body colour; the page local classes are
prefixed now.

Pinned in tests/ui-truthfulness.test.mjs: the status sentence with the
release and its date, the four installers as filename plus version plus
measured sha256 with no fifth link allowed, a yardstick that must name
both _gpgbuilder and _gpgorigin, a rule that every signing claim names
the release it holds for, the served .deb described as unsigned, a first
screenful jargon gate that did not exist before, and a check that the
document is complete and loads its nav scripts. Eight sabotage cases were
verified to fail.
@Apolloccrypt
Apolloccrypt merged commit 1b73ec9 into main Sep 2, 2026
10 checks passed
@Apolloccrypt
Apolloccrypt deleted the feat/download-page branch September 5, 2026 18:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant