Skip to content

/docs and /help answer a buyer before they answer a developer - #333

Merged
Apolloccrypt merged 4 commits into
mainfrom
feat/fe-docs
Sep 2, 2026
Merged

/docs and /help answer a buyer before they answer a developer#333
Apolloccrypt merged 4 commits into
mainfrom
feat/fe-docs

Conversation

@Apolloccrypt

@Apolloccrypt Apolloccrypt commented Sep 2, 2026

Copy link
Copy Markdown
Owner

What this does

/docs, /developer and /help, rebased on main and reworked after two rounds of review. The rule from docs/brand/messaging.md: a sentence ships only when it already ships somewhere else and a test fails the moment it stops shipping. Every claim below is quoted from the page that owns it, and every quote is pinned to the markup a visitor actually sees.

The review points, one by one

1. The buyer line on /docs was not pinned. The old assertion matched Your IT can check everything here against the whole file, and the same words sit in four meta tags. Deleting the visible paragraph left the test green. The assertion is now anchored to <p class="docs-buyer">Evaluating Paramant? .... Sabotaged both ways: removing the paragraph is red, and leaving it while breaking only the meta copy is red too.

2. The buyer had no button on /docs. Quick start was the only real action and Pricing was a text link. There are now two buttons of the same size in the same row: Quick start (filled, still the primary CTA the guide asks for) and See plans and prices (outlined, straight to /pricing). Create account stays as a text link below them. On 390px both buttons go full width and stack. Pinned on both href and class, so demoting either one back to a text link fails.

3. "What does it cost?" on /help named no amount. It now names what /pricing prints: ParaSign Community is free, forever, no card required, 2 signatures per month; the first paid plan is ParaSign Pro at €49 a month excl. btw (€59.29 incl.) with 100 signatures a month. Each half is pinned against the card it came from, and the Community pin is scoped to the ParaSign grid because /pricing carries a second Community card for ParaSend.

4. "Pay for volume, never for security" was a sales line on a support page. It is gone from /help and stays on /pricing, which is the page that sells. The fact behind it is quoted instead, word for word: "Every plan gets the same encryption, the same post-quantum signatures and the same public proof log." A doesNotMatch fails if the slogan comes back. That gate, and the sales-vocabulary gate beside it, run on the whole /help body below </head>, not on the three answers alone, so the line cannot return through the lede, an article card or the footer.

5. "Where does my data live?" was a compliance table flattened into prose, with two colons in one sentence. It is four short sentences now, and the second round of review caught a real error in the first draft of them: it said the documents and keys live on servers at Hetzner. The keys do not. Five places on the site promise the opposite: "Generated on your device, never sent" (index.html), "relay holds only ciphertext, never keys" and "No plaintext, no keys" (security.html), "we never hold decryption keys, anywhere" (trust.html), "The relay never sees plaintext and never holds a private key" (docs.html). The answer now reads:

Your documents live on servers at Hetzner Nuremberg, Germany, and they sit there as ciphertext. The relay never sees plaintext and never holds a private key. Your files stay under EU law and the GDPR, and no US provider is in the data path. Email goes out via Resend, as /privacy sets out.

That is proof 1 of the messaging guide, scoped to the data path and naming the Resend exception in the same breath, plus the key sentence quoted from docs.html. The unqualified "no US company" row on /security is an open contradiction (guide section 9) and is deliberately not repeated here; a test fails if it appears.

Pinned in both directions. The positive half is a pair, the quote and docs.html. The negative half is a loop over every sentence in the /help body: a sentence naming a key and a piece of infrastructure in the same breath must be denying it, and may never put a locative verb between the two. Sabotage: putting "and keys" back is red, "your keys are stored on the relay" is red, and "Your keys are kept on servers at Hetzner Nuremberg, and there is no US provider" is red as well, so a stray "no" elsewhere in the sentence buys no pass.

6. The em-dash sweep stopped at the lede. The H1, the tab title, the meta description and the body of /help/api-key-vs-totp and /help/lost-authenticator still had them: eleven and six. All gone, plus an en-dash range in each of the two pages and one on /docs. A loop over the three help pages and /docs pins it.

7. /developer spoke to the wrong audience. The page is noindex and only reachable once signed in, so "so your IT can check everything before you connect anything" was addressing someone who is not there. It now reads "so you can read the whole surface before you connect anything." The plan boundary for API access stays, and a test forbids the buyer phrasing from returning.

Also fixed while here: two sentences on /docs that ran on double colons (the "Version 3.0.0 keeps ..." lede and the ADR line) now read as sentences.

Rebase

Rebased onto main twice. The first pass resolved a conflict in tests/ui-truthfulness.test.mjs where main's homepage, dashboard and Community-rename assertions (#327, #328) landed in the same place; both sides kept, the duplicate pricing.html read folded into the existing one. main's rename of the free tier to Community also changed the source sentence on /sign, so the /help quote follows it. The second pass took #332, which appends two console.log lines at the end of the same file: no name collision, both sides kept. Now current with #352 and #353. frontend/apply-nav.py ran after each rebase and produced no further changes.

Proof

Green: links, seo-contract, ui-truthfulness, site-claims, frontend-loading-contract, navigation-shell, check-csp-inline.sh, check-cache-bust.sh, eslint@9 ., tests/static-sanity.sh (PASS, all hard checks clear, 10/10), scripts/check-commit-style.sh over the whole range.

Sabotage: 37 mutations, one at a time, each reverted after the run. Every new pin went red, including the ones on the source pages (pricing.html, security.html, index.html, docs.html) and the four negative pins. Screenshots at 390x844: no horizontal scroll on either page, scrollWidth 390 = clientWidth 390, and all three /help answers still fit one screen.

Known gap, outside this PR: renaming the ParaSend Community card on /pricing stays green, because main's assertion from #328 is satisfied by the remaining ParaSign card. This branch's own pin is scoped and does catch the ParaSign one. That is a pin on main, not a regression here.

Scope: six files, all /docs, /developer, /help and their test. frontend/apply-nav.py, frontend/js/nav-auth.js and frontend/index.html are not edited.

@Apolloccrypt

Copy link
Copy Markdown
Owner Author

Follow-up commit 1611bc3: tests/developer-parasign-dashboard.test.mjs caught something the first push got wrong, and it was right to.

The test asserts the rendered text of /developer contains "ParaSign API", which is how it proves the page is the ParaSign surface and not the old transfer tool catalogue. The eyebrow above the h1 is text-transform:uppercase, so innerText renders it as "PARASIGN API" and never satisfies that check. The only lowercase occurrence sat inside the sentence this branch rewrote out.

The lede now reads "Every ParaSign API endpoint is in the API reference". Same meaning, and the phrase is back where the test can see it. The test itself is unchanged — it caught a real thinning of the page's own name, which is exactly the job.

The browser suites were run locally this time (all 18 subtests across the playwright-importing files in tests/), not just the non-browser ones.

Apolloccrypt added a commit that referenced this pull request Sep 2, 2026
Second pass on the review of PR #333, rebased on main (#324, #327, #328,
#331, #342).

/docs
- The buyer line is pinned to the visible paragraph. The old assertion
  matched the same words in four meta tags, so deleting the paragraph
  left the test green. Proven by sabotage, both ways.
- The buyer gets a real button to /pricing beside Quick start, same size,
  same row. Quick start stays the primary CTA the messaging guide asks
  for; the pricing button is the outlined one.
- Two sentences that ran on double colons now read as sentences, and the
  ADR range no longer uses an en-dash.

/help
- "What does it cost?" names the numbers /pricing prints: ParaSign
  Community free forever, no card, 2 signatures per month, and ParaSign
  Pro at 49 euro a month excl. btw (59.29 incl.) with 100 signatures.
  Each half is pinned to the card it was quoted from.
- "Pay for volume, never for security" is off the support page. The fact
  behind it stays, quoted from /pricing: every plan gets the same
  encryption, the same post-quantum signatures and the same public proof
  log. A test now fails if the sales line comes back.
- "Where does my data live?" is three readable sentences instead of a
  flattened compliance table: Hetzner Nuremberg, EU law and the GDPR, no
  US provider in the data path, and email via Resend as /privacy sets
  out. Scoped to the data path per proof 1 of the messaging guide, so
  the unqualified "no US company" row from /security is not repeated
  here while that contradiction is open (guide section 9).
- The em-dash sweep on api-key-vs-totp and lost-authenticator now covers
  the H1, the tab title, the meta description and the body, not only the
  lede. A test pins all three help pages.

/developer
- The page is noindex and only reachable once signed in, so the lede
  addresses the developer reading it, not the buyer who sent them. The
  plan boundary for API access stays, and a test forbids the buyer
  phrasing coming back.

Tests: links, seo-contract, ui-truthfulness, site-claims,
frontend-loading-contract, navigation-shell, csp-inline, cache-bust,
eslint and static-sanity all pass. Every new pin was sabotaged one at a
time and went red.
Apolloccrypt added a commit that referenced this pull request Sep 2, 2026
Second pass on the review of PR #333, rebased on main (#324, #327, #328,
#331, #342).

/docs
- The buyer line is pinned to the visible paragraph. The old assertion
  matched the same words in four meta tags, so deleting the paragraph
  left the test green. Proven by sabotage, both ways.
- The buyer gets a real button to /pricing beside Quick start, same size,
  same row. Quick start stays the primary CTA the messaging guide asks
  for; the pricing button is the outlined one.
- Two sentences that ran on double colons now read as sentences, and the
  ADR range no longer uses an en-dash.

/help
- "What does it cost?" names the numbers /pricing prints: ParaSign
  Community free forever, no card, 2 signatures per month, and ParaSign
  Pro at 49 euro a month excl. btw (59.29 incl.) with 100 signatures.
  Each half is pinned to the card it was quoted from.
- "Pay for volume, never for security" is off the support page. The fact
  behind it stays, quoted from /pricing: every plan gets the same
  encryption, the same post-quantum signatures and the same public proof
  log. A test now fails if the sales line comes back.
- "Where does my data live?" is three readable sentences instead of a
  flattened compliance table: Hetzner Nuremberg, EU law and the GDPR, no
  US provider in the data path, and email via Resend as /privacy sets
  out. Scoped to the data path per proof 1 of the messaging guide, so
  the unqualified "no US company" row from /security is not repeated
  here while that contradiction is open (guide section 9).
- The em-dash sweep on api-key-vs-totp and lost-authenticator now covers
  the H1, the tab title, the meta description and the body, not only the
  lede. A test pins all three help pages.

/developer
- The page is noindex and only reachable once signed in, so the lede
  addresses the developer reading it, not the buyer who sent them. The
  plan boundary for API access stays, and a test forbids the buyer
  phrasing coming back.

Tests: links, seo-contract, ui-truthfulness, site-claims,
frontend-loading-contract, navigation-shell, csp-inline, cache-bust,
eslint and static-sanity all pass. Every new pin was sabotaged one at a
time and went red.
Apolloccrypt added a commit that referenced this pull request Sep 2, 2026
Third pass on the review of PR #333.

Blocker. The data-location answer said "Your documents and keys live on
servers at Hetzner Nuremberg, Germany". The keys do not. Five places on
the site promise the opposite: "Generated on your device, never sent"
(index.html), "relay holds only ciphertext, never keys" and "No plaintext,
no keys" (security.html), "we never hold decryption keys, anywhere"
(trust.html), "The relay never sees plaintext and never holds a private
key" (docs.html). Flattening a table into prose is how the word "keys"
got in there, and it is the sentence a security reviewer would quote back
at us.

The answer now says the documents are there and that they are ciphertext,
and it quotes docs.html for where the keys are not:

  Your documents live on servers at Hetzner Nuremberg, Germany, and they
  sit there as ciphertext. The relay never sees plaintext and never holds
  a private key. Your files stay under EU law and the GDPR, and no US
  provider is in the data path. Email goes out via Resend, as /privacy
  sets out.

Pinned in both directions. The positive half is a pair, the quote and
docs.html. The negative half is a loop over every sentence in the /help
body: a sentence that mentions a key and a piece of infrastructure in the
same breath must be denying it, and may never use a locative verb between
the two. Sabotage: putting "and keys" back is red, "your keys are stored
on the relay" is red, and "Your keys are kept on servers at Hetzner
Nuremberg, and there is no US provider" is red as well, so a stray "no"
elsewhere in the sentence does not buy a pass.

Also in this commit, from the same review:

- The gate against the pricing slogan and the one against sales
  vocabulary ran on the three answers only. Both now run on the whole
  /help body below </head>, so the line cannot come back through the
  lede, an article card or the footer. Sabotage on both: red.
- Rebased on main for #332, which added two console.log lines at the end
  of tests/ui-truthfulness.test.mjs. No name collision; both sides kept.

Tests: links, seo-contract, ui-truthfulness, site-claims,
frontend-loading-contract, navigation-shell, csp-inline, cache-bust,
eslint, static-sanity. All pass. 37 sabotage mutations, one at a time,
all red.
/docs is busier than /pricing. That is not an accident: it was the page a
navigation of forty links pointed at, and it opens on pip install. Someone
evaluating Paramant lands there because their IT asked to see the API, and
the page never says what it is for or where the price boundary sits.

The messaging guide (docs/brand/messaging.md, section 8) settles the order:
one line for the buyer, then the developer entry, and no marketing beyond
one line naming which plan the API needs.

What changed, per page.

/docs
  A single buyer line above the lede: "Evaluating Paramant? Your IT can
  check everything here. Plans and prices are on Pricing." The existing
  lede is unchanged and gains one sentence: the ParaSign API is available
  from ParaSign Pro, with a link to /pricing. The hero CTA is now Quick
  start, with Create account beside it, because a developer who arrives
  here wants the first request, not a signup form.

  The meta description said "PARAMANT Ghost Pipe v2" while the page body
  describes 3.0.0. It now matches the page and says what the page is for.

/developer
  Same shape for the person already signed in. The lede loses "Everything
  needed to connect and operate the ParaSign API is here", which said
  nothing, and gains a link to the API reference and the plan line: API
  access is included from ParaSign Pro. Quick start joins the Build and
  operate links.

/help
  The three questions a buyer asks, at the top, above the article grid,
  each answered in the words the source page already uses and each linking
  to that page:

    Can I sign without an account?   -> /sign
    What does it cost?               -> /pricing
    Where does my data live?         -> /security

  H1 and lead are unchanged, the support voice is unchanged, and nothing
  is sold. The meta description now names those three answers instead of
  repeating the h1.

  Two article ledes lost an em-dash (api-key-vs-totp, lost-authenticator).

A mobile fix that /docs needed to be readable at all: .content is a flex
item, so its default min-width:auto let the widest table (first column is
white-space:nowrap) push it to 840px inside a 390px viewport. The body
clipped the overflow with no scrollbar, so the documentation text simply
ran off the right of the screen on a phone. min-width:0 plus scrollable
tables below 900px. No other styling changed; the palette and the mono
labels are untouched.

Tests. The guide's rule is that a sentence may only ship when a test fails
if it stops shipping, and a quote is worse than a paraphrase if it can
drift from its source in silence. So tests/ui-truthfulness.test.mjs now
asserts each new sentence AND the page it was quoted from, as a pair:
"Pay for volume, never for security" on /help and on /pricing, the two
jurisdiction rows on /help and on /security, the account requirement on
/help and on /sign, and "API access" on the ParaSign Pro tier, which is
what makes the /docs and /developer plan line true. Plus: no sales
vocabulary on /help.

Not touched: index.html, apply-nav.py, js/nav-auth.js, the API reference
itself, and the two open contradictions the guide names in section 9.

Green locally: seo-contract, ui-truthfulness, links,
frontend-loading-contract, navigation-shell (24 checks),
check-csp-inline.sh, check-cache-bust.sh, eslint.
tests/developer-parasign-dashboard.test.mjs asserts that the rendered text
of the developer page contains "ParaSign API", which is how it proves the
page is the ParaSign surface and not the old transfer tool catalogue. The
eyebrow above the h1 is text-transform:uppercase, so innerText renders it
as "PARASIGN API" and never satisfies that check. The only lowercase
occurrence sat in the sentence this branch rewrote.

The lede now reads "Every ParaSign API endpoint is in the API reference",
which says the same thing and puts the phrase back where the test can see
it. The test is unchanged: it was right, and it caught a real thinning of
the page's own name.

Ran locally this time: all 18 browser subtests in tests/*.mjs that import
playwright, plus the 110 non-browser subtests.
Second pass on the review of PR #333, rebased on main (#324, #327, #328,
#331, #342).

/docs
- The buyer line is pinned to the visible paragraph. The old assertion
  matched the same words in four meta tags, so deleting the paragraph
  left the test green. Proven by sabotage, both ways.
- The buyer gets a real button to /pricing beside Quick start, same size,
  same row. Quick start stays the primary CTA the messaging guide asks
  for; the pricing button is the outlined one.
- Two sentences that ran on double colons now read as sentences, and the
  ADR range no longer uses an en-dash.

/help
- "What does it cost?" names the numbers /pricing prints: ParaSign
  Community free forever, no card, 2 signatures per month, and ParaSign
  Pro at 49 euro a month excl. btw (59.29 incl.) with 100 signatures.
  Each half is pinned to the card it was quoted from.
- "Pay for volume, never for security" is off the support page. The fact
  behind it stays, quoted from /pricing: every plan gets the same
  encryption, the same post-quantum signatures and the same public proof
  log. A test now fails if the sales line comes back.
- "Where does my data live?" is three readable sentences instead of a
  flattened compliance table: Hetzner Nuremberg, EU law and the GDPR, no
  US provider in the data path, and email via Resend as /privacy sets
  out. Scoped to the data path per proof 1 of the messaging guide, so
  the unqualified "no US company" row from /security is not repeated
  here while that contradiction is open (guide section 9).
- The em-dash sweep on api-key-vs-totp and lost-authenticator now covers
  the H1, the tab title, the meta description and the body, not only the
  lede. A test pins all three help pages.

/developer
- The page is noindex and only reachable once signed in, so the lede
  addresses the developer reading it, not the buyer who sent them. The
  plan boundary for API access stays, and a test forbids the buyer
  phrasing coming back.

Tests: links, seo-contract, ui-truthfulness, site-claims,
frontend-loading-contract, navigation-shell, csp-inline, cache-bust,
eslint and static-sanity all pass. Every new pin was sabotaged one at a
time and went red.
Third pass on the review of PR #333.

Blocker. The data-location answer said "Your documents and keys live on
servers at Hetzner Nuremberg, Germany". The keys do not. Five places on
the site promise the opposite: "Generated on your device, never sent"
(index.html), "relay holds only ciphertext, never keys" and "No plaintext,
no keys" (security.html), "we never hold decryption keys, anywhere"
(trust.html), "The relay never sees plaintext and never holds a private
key" (docs.html). Flattening a table into prose is how the word "keys"
got in there, and it is the sentence a security reviewer would quote back
at us.

The answer now says the documents are there and that they are ciphertext,
and it quotes docs.html for where the keys are not:

  Your documents live on servers at Hetzner Nuremberg, Germany, and they
  sit there as ciphertext. The relay never sees plaintext and never holds
  a private key. Your files stay under EU law and the GDPR, and no US
  provider is in the data path. Email goes out via Resend, as /privacy
  sets out.

Pinned in both directions. The positive half is a pair, the quote and
docs.html. The negative half is a loop over every sentence in the /help
body: a sentence that mentions a key and a piece of infrastructure in the
same breath must be denying it, and may never use a locative verb between
the two. Sabotage: putting "and keys" back is red, "your keys are stored
on the relay" is red, and "Your keys are kept on servers at Hetzner
Nuremberg, and there is no US provider" is red as well, so a stray "no"
elsewhere in the sentence does not buy a pass.

Also in this commit, from the same review:

- The gate against the pricing slogan and the one against sales
  vocabulary ran on the three answers only. Both now run on the whole
  /help body below </head>, so the line cannot come back through the
  lede, an article card or the footer. Sabotage on both: red.
- Rebased on main for #332, which added two console.log lines at the end
  of tests/ui-truthfulness.test.mjs. No name collision; both sides kept.

Tests: links, seo-contract, ui-truthfulness, site-claims,
frontend-loading-contract, navigation-shell, csp-inline, cache-bust,
eslint, static-sanity. All pass. 37 sabotage mutations, one at a time,
all red.
@Apolloccrypt
Apolloccrypt merged commit d946752 into main Sep 2, 2026
10 checks passed
@Apolloccrypt
Apolloccrypt deleted the feat/fe-docs 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