Trust pages answer the buyer first: /about, /security, /trust - #335
Merged
Conversation
Apolloccrypt
force-pushed
the
feat/fe-vertrouwen
branch
2 times, most recently
from
September 2, 2026 17:42
9b53c72 to
fd6ad3f
Compare
Apolloccrypt
force-pushed
the
feat/fe-vertrouwen
branch
from
September 2, 2026 17:50
fd6ad3f to
dad08eb
Compare
Apolloccrypt
force-pushed
the
feat/fe-vertrouwen
branch
6 times, most recently
from
September 2, 2026 18:39
777de99 to
42088f6
Compare
Apolloccrypt
force-pushed
the
feat/fe-vertrouwen
branch
from
September 2, 2026 18:58
42088f6 to
0c5df82
Compare
The three pages a buyer reaches after the homepage were written for someone
who already believes the product. /about opened on the mission and put the
founder in section 03, below the cryptography. /security opened on defence in
depth and kept the one row a buyer came for, the jurisdiction table, seven
screens down. /trust never said who it was for.
They now run the order docs/brand/messaging.md fixes: what this is, who it is
for, who is behind it, a next step, then the proof including the honest
limits. Copy, hierarchy and order only. No restyle. index.html, apply-nav.py
and js/nav-auth.js are untouched.
/about is the founder page.
The lede is plain language: sign and send documents so only you and the
recipient can read them, so anyone can check later that the document is
genuine, on servers in Germany under EU law. Under it, in the first phone
screen, who it is for, then Mick Beer with the title the guide fixes, then
two buttons. Measured at 390x844: the founder sits at y=463 and the second
button at y=608. The section number "00" is hidden in the mobile override
instead of landing under the H1 as a stray number.
Two sentences are gone. "The cryptography is post-quantum, which is the
proof that it still holds up in ten years" was not a proof and not
checkable, on the page whose argument is that everything on it is
checkable. The founder paragraph explained the free plan with a
jurisdiction claim ("should not depend on a US subscription"), which the
guide forbids beside his name. In its place stands the paragraph #332
landed on main while this branch was in review: the Community plan is his
way of giving something back to society, the business plans pay for it,
that is the whole arrangement, and it is why the Community plan is not a
trial and has no end date. Taken from main verbatim, moved with the section
into the top half, and pinned so the two copies cannot drift.
The tier block names the free plan Community, which is what /pricing prints
on the card since #328.
/security answers "why would I trust you" before it answers "how it works".
The promise carries its own scope. It read "even if our own server is
broken into, nobody can read your documents" flat out, while ten screens
lower the page says the Chromium and Outlook extensions take a server-side
encryption path. For an extension user the flat version is untrue today, so
the exception now travels with the promise, in the hero.
It also says what that exception costs the reader, in words rather than in
ours: the extensions encrypt on our server, which means we can read what
you upload through them until that is changed. "Treat those uploads as
relay-side" was the internal phrasing, and relay is exactly the word this
branch removed from the /trust hero. Neither hero uses it now, and a test
says so.
The first screen also carries who is behind the page (Paramantis Solutions
B.V., Harderwijk, KvK 42115132, and the founder line) and the next step.
The buttons used to sit at roughly y=11400 on a phone; "See pricing" is now
at y=656 and "Verify a document" at y=716.
The free plan is Community here too. A first version of this branch left
"ParaSign Free and ParaSend Free cost EUR 0" standing at r.448, because the
only check that forbade the old name was scoped to /about. That check now
covers all three pages, and the sitewide sweep in ui-truthfulness gained the
shape it was missing, so any page using it fails. The page description no
longer sells "relay architecture" either.
The audit block says what /docs#audits actually adds up to: three external
audits in April 2026, two by R. Zwarts and one by Ryan Williams of Smart
Cyber Solutions, forty findings, four of them critical, with the resolving
commits in the table. The previous round claimed "the audit reports
themselves are not published" and pinned it. That was false:
docs/security-audit-2026-04.md is the full Smart Cyber Solutions writeup
and it ships in the site tree. Only the raw pentest output is missing,
which is what that document itself calls the raw report. Both pages now say
that, and link the report.
The CLOUD Act row and the EU-law card move to the data-path wording, as
section 9.2 of the guide requires: no US provider in the data path, with
Resend named as the one exception in the same breath. The old row read "not
applicable: no US infrastructure, no US company", which is broader than
/privacy allows.
/trust names its reader, then gives that reader somewhere to go.
The hero addresses organisations running their own relay and anyone
checking a supplier, and now offers them two buttons instead of one text
link mid-paragraph (y=416 and y=476). The first sentence under the hero no
longer says "the operator who runs the relay": relay is not a word a
supplier reviewer knows.
The page called itself Trust & Verification in its title and H1 while the
social card and the structured data still said Trust & Transparency. #334
then rewrote every head on the site, so the literal strings belong there and
tests/seo-contract.test.mjs pins them. What this branch pins is the
relation: the title, og:title, twitter:title and the JSON-LD name must name
the page the same, and that name must contain the words the H1 uses.
Punctuation and case are not the point; Transparency versus Verification
was. Its plan sentence named two of the three paid
ParaSign tiers; it names all three, and both free tiers as Community.
Tests
tests/ui-truthfulness.test.mjs pins each of the above, and each one was
sabotaged in place to confirm it goes red: the ten-year promise, the
give-back sentence, the US-subscription framing, the Community rename, the
CLOUD Act row, the Resend exception dropped from the card, the finding
counts, the "reports not published" sentence, the auditor names on
/security as well as /trust, the two hero buttons on each page, the
who-is-behind line, the bounded promise, the /trust og:title and JSON-LD
name, the jargon in the first screen, and the paid tiers. Twenty-seven
sabotages, twenty-seven red, no gaps.
tests/site-claims.test.mjs block 11 reads the numbers out of the /pricing
tier card that owns them and requires /about to repeat them; it follows the
Community rename and now also forbids "ParaSign Free" on /about.
node --test over the CI glob: 173 pass, 0 fail. check-csp-inline,
check-cache-bust and eslint exit 0, bron-seo/apply_seo_head.py --check
reports 0 pages, and tests/static-sanity.sh is PASS on all eleven checks,
including #362's new test-scope guard. At 390px all three pages have
scrollWidth === clientWidth === 390.
Following main through four parallel merges
#359 landed the deduplication this branch was carrying (the second const
tiers in relay/test/pricing-page.test.js, and pricingVisible renamed to
pricingText), so both local fixes are dropped in favour of main's.
This branch's own block in tests/ui-truthfulness.test.mjs now sits inside a
bare block scope and declares nothing at module level. Four PRs merged into
that file in parallel on 2 September and two of them collided on a top-level
const, which is a SyntaxError: not one assertion in the file runs, on any
branch. A block that declares nothing at module level cannot do that to the
next branch.
tests/site-claims.test.mjs block 12 stopped spelling the units itself. #336
renamed both /pricing section headings and put ParaSign first, and #359
reworded "2 signatures per month" to "2 signatures a month". The block now
finds the headings by product prefix, orders them by position, and lifts each
fact out of the card as a whole phrase, so /about repeats what /pricing says
rather than what this file guesses /pricing says. /about follows the new
wording: "2 signatures a month", "24 hour link expiry", "up to 10 reads per
link".
Still open, deliberately
The eIDAS level (SES on /about, advanced (AES) in the /pricing FAQ) is a
real contradiction and needs a decision, not a copy edit. This branch stops
it spreading and pins that it stays on the one page that has always carried
it.
Apolloccrypt
force-pushed
the
feat/fe-vertrouwen
branch
from
September 2, 2026 19:07
b9feacd to
e9bf1fa
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rewrites the three pages a buyer reaches after the homepage, in the order
docs/brand/messaging.mdfixes (merged as #331): what this is, who it is for,who is behind it, a next step, then the proof including the honest limits.
Copy, hierarchy and order only. No restyle.
frontend/index.html,frontend/apply-nav.pyandfrontend/js/nav-auth.jsare untouched. Scope is five files: the three pages and the two test files
that pin them.
Third round. The second was rejected on a stale base plus twelve findings
across two reviews. Every one is answered below, with the pin that now fails
when it comes back.
Rebased, and main's corrections kept
Base is current
main(56694a1, #362). Since the previous head the branchhas picked up #327, #328, #331, #332, #333, #334, #336, #337, #339, #340,
#342, #352 to #359 and #362, plus the dependency bumps. What main fixed on these pages survives:
ships 3 KEMs and 17 signature schemesplus the core/extended caveataccess_log offindeploy/nginx-paramant-live.conf/docs#auditsResend, Hetzner, Bunny, Mollie)about.htmlr.169)Two files conflicted. In
tests/ui-truthfulness.test.mjsmain's block is keptwhole; this branch's block is appended after it and reuses main's
aboutandpricinghandles instead of redeclaring them. Infrontend/about.htmltheconflict is the section this branch moves out of position 03 and #332 edits in
place: main's three paragraphs win word for word, including both sentences
#332 added, and they travel with the section into the top half. The section
appears once, at 02, and the numbering below it is consecutive.
The blocking findings
1. "The audit reports themselves are not published" was untrue, and pinned.
docs/security-audit-2026-04.mdis the full Smart Cyber Solutions writeup andit ships in the site tree as
frontend/docs/security-audit-2026-04.md. Onlythe raw pentest output is missing, which is what that document itself calls
the raw report. Both pages now say what
/docs#auditsadds up to: threeaudits in April 2026, two by R. Zwarts and one by Ryan Williams of Smart Cyber
Solutions, forty findings, four of them critical, resolving commits in the
table. The published report is linked. The false sentence is now forbidden by
assertion on both pages.
2. The absolute promise above the fold was contradicted lower down. The
hero read "Even if our own server is broken into, nobody can read your
documents" flat out, while the page says ten screens lower that the Chromium
and Outlook extensions take a server-side encryption path. The exception now
travels with the promise: it holds for the web app, ParaShare and the official
SDKs, and the extensions are named in the same paragraph.
3. /trust called itself two different things.
<title>and<h1>saidTrust & Verification;
og:title,twitter:titleand the JSON-LDWebPagename still said Trust & Transparency. #334 has since rewritten every head on
the site, so the literal strings belong there and
tests/seo-contract.test.mjspins them in its
PINNEDtable. On rebase the head of all three pages wasresolved in main's favour, without exception, and
python3 bron-seo/apply_seo_head.py --checkreports would change: 0 pages.What this branch pins instead is the relation, which no test in
tests/covered and which #334 does not cover either:
<title>,og:title,twitter:titleand the JSON-LD name must name the page the same, and that namemust contain the words the
<h1>uses. Punctuation and case are normalisedaway; Transparency versus Verification was the defect.
4. The auditor names were pinned on /trust only. Replacing them on
/security with "an independent firm" left the suite green. Pinned on both.
/about
proof that it still holds up in ten years". That is not a proof and not
checkable, on the page whose whole argument is that everything on it is
checkable.
Community plan is his way of giving something back to society; the business
plans pay for it. That is the whole arrangement, and it is why the Community
plan is not a trial and has no end date." It replaces a version that
explained the free plan with a jurisdiction claim ("should not depend on a
US subscription"), which the guide forbids beside his name because the
jurisdiction claim needs its Resend exception with it. The pin is exact, so
the copy here and the copy Signed in, the free plan is called Community, and the page answers what a buyer asks #332 landed cannot drift.
/pricingprints it on the card.pricing" at y=549, "See the evidence" at y=608. The stray
00under the H1is hidden by
#main-content .sec-head .num{display:none}, confirmed in thebrowser as
display: none, not merely reordered./security
(Paramantis Solutions B.V. in Harderwijk, KvK 42115132, and the founder
line) and both next steps. The buttons used to sit at roughly y=11400, some
fourteen phone screens down. Measured: "See pricing" y=629, "Verify a
document" y=689.
section 9.2 of the guide requires: no US provider in the data path, with
Resend named as the one exception in the same breath. The old row, "Not
applicable: no US infrastructure, no US company", is broader than /privacy
allows. The pin measures the exception as a window of 420 characters around
the claim, not as "somewhere on the page", after a first version of that
check let a card drop Resend while the table lower down still carried the
word.
not appear above the fold undefined.
/trust
checking a supplier, and gives that reader two buttons instead of one text
link mid-paragraph (y=416 and y=476).
not a word a supplier reviewer knows.
three, and both free tiers as Community.
The fourth-round finding
security.htmlr.448 still read "ParaSign Free and ParaSend Free cost€0" after three rounds. The reason is worth writing down: the check this
branch added forbidding the old plan name was scoped to
/about, so the pagethat repeated it was never looked at. Two changes, not one:
site-claimsassertion now runs overabout,securityandtrust;TIER_NAME_SHAPESinui-truthfulnessgains/\bPara(Sign|Send) Free\b/,so the sitewide sweep over every
frontend/**/*.htmlcatches it on any page,including pages no round of this work touches. Sabotage on
pricing.htmlconfirms that.
Two more in the same round:
branch removed from the /trust hero, and it tells a reader who does not know
what a relay is nothing at all. It now says what the exception costs them:
the extensions encrypt on our server, which means we can read what you
upload through them until that is changed. Neither hero uses the word relay
now, and a test says so on both pages. The /security meta description, which
sold "relay architecture" in the one line a search result shows, was rewritten
with it.
the test message and the commit.
Tests
tests/ui-truthfulness.test.mjsgains a block for the findings above.tests/site-claims.test.mjsblock 11 reads each number out of the/pricingtier card that owns it and requires
/aboutto repeat it; it follows theCommunity rename and now also forbids
ParaSign Freeon/about.Twenty-seven sabotages were applied in place and reverted, one per new pin: the
ten-year promise, the give-back sentence, the US-subscription framing, the
Community rename, the CLOUD Act row, the Resend exception dropped from the
card, the finding counts on each page, the "reports not published" sentence,
the auditor names on /security, the hero buttons on all three pages, the
who-is-behind line, the bounded promise, the /trust
og:titleand JSON-LDname, the jargon in each hero, the plan name on each of the three pages plus
one page outside this PR, the "relay" word in both heroes, the /trust head naming the page differently from its own H1, and the paid tiers.
Twenty-seven red, no gaps.
Following main through parallel merges
mainmoved nine times while this branch was in review. Three things came outof that and are worth naming:
tests/ui-truthfulness.test.mjsnow sits in a bareblock scope and declares nothing at module level. Four PRs merged into that
file in parallel on 2 September and two of them collided on a top-level
const, which is a SyntaxError: not one assertion in the file runs, on anybranch. Link the product pages, state the hourly ceiling, and write one monthly form #359 fixed the collision and Gate the test files against duplicate top-level declarations #362 added a gate for it; this makes the
block unable to cause the next one.
scripts/check-test-declarations.shreports 110 suites parse and declare each top-level name once.
tests/site-claims.test.mjsblock 12 stopped spelling the units itself.Pricing says what it sells, and what free actually gives, in the first screen #336 renamed both /pricing section headings and put ParaSign first; Link the product pages, state the hourly ceiling, and write one monthly form #359
reworded "2 signatures per month" to "2 signatures a month". The block now
finds the headings by product prefix, orders them by position, and lifts each
fact out of the card as a whole phrase, so /about repeats what /pricing says
rather than what the test guesses it says. /about follows the new wording.
exception, when A shared link says what we sell, and names the company behind it #334 rewrote every head on the site.
python3 bron-seo/apply_seo_head.py --checkreports would change: 0pages.
Two assertions #339 added were adjusted rather than kept, and both are
deliberate:
security.html must keep its Jurisdiction and privacy tablematched<h2>Jurisdiction. This branch gives that heading anidso the EU-law cardcan link to
#jurisdiction, so the selector became<h2[^>]*>. The checkitself (Bunny is not in that table) is unchanged.
security.html must keep the US CLOUD Act row that proof 1 is measured againstpinnedNot applicable: no US infrastructure, no US company.Section 9.2 of the guide names that row as the one that has to move to the
data-path wording "in its own PR with its own test". This is that PR, so the
assertion now requires the data-path wording and forbids
no US company.Green locally
node --testover the CI glob (.github/workflows/test.ymlr.200): 173pass, 0 fail, including
links,seo-contract,site-claims,ui-truthfulness,frontend-loading-contract,navigation-shell,code-manifest,version-consistency,env-documented.scripts/check-csp-inline.shandscripts/check-cache-bust.shexit 0.eslint@9exits 0 over the whole repository, andtests/static-sanity.shisPASS on all eleven checks, check 10 (commit style) and #362's new check 11
(test scope) included.
scripts/check-commit-style.shis clean.At 390x844,
scrollWidth === clientWidth === 390on all three pages.Still open, deliberately
The eIDAS level (SES on
/about, advanced (AES) in the/pricingFAQ) is areal contradiction and needs a decision, not a copy edit. This branch stops it
spreading and pins that it stays on the one page that has always carried it.