From 9f38a02be149bfd83e3f7179054fe24cfa5ddb3e Mon Sep 17 00:00:00 2001 From: Michal Ferber Date: Thu, 3 Sep 2026 16:47:09 -0400 Subject: [PATCH 1/3] Ship the required pages, which all four returned 404 for MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DS §9 requires every public project to carry a Privacy Policy, Terms of Use, and — for an end-user product — a Docs page. markdownwizard.app had none of them: /privacy, /terms, /docs, and /support all returned 404, verified against a negative control so the result is a real absence and not a host answering everything. The pages are written rather than inherited. markdownwizard is not in the wizard-web monorepo and is not migrating into it — the one branch that looked like a migration, claude/bootstrap-markdownwizard, ends at "Point the markdownwizard bootstrap at its own repo" — so none of the shared packages/ui page components are reachable from here. This is a hand-rolled static site and these are hand-rolled static pages that match it. They are also written to the facts of THIS product rather than copied from the family template, because the family template would have said things that are not true here. It routes contact through a mailer-backed form, which this site's CSP (`form-action 'none'`) forbids and which has no backend to reach. Its Terms declare an MIT open-source product, which is true today but is exactly what REGISTRY line 32 records as owed a change. Neither claim is made. One disclosure the template would not have produced at all: `img-src https:` means a remote image referenced in your Markdown really is fetched, which tells that host your IP. §9 requires the policy to say what leaves and where it goes where it cannot say "nothing", so the Privacy Policy says it plainly instead of claiming an absolute the code contradicts. Reachability was the other half. The editor is a single-page app shell whose
diff --git a/privacy.html b/privacy.html new file mode 100644 index 0000000..0bad0a9 --- /dev/null +++ b/privacy.html @@ -0,0 +1,101 @@ + + + + + + Privacy Policy — Markdown Wizard + + + + + + + + + +
+
Markdown Wizard
+ +
+ +
+
+

Privacy Policy

+

Last updated: September 3, 2026

+ +

The short version

+

Markdown Wizard runs entirely in your browser. The text you type, paste, or open is + never sent to a server — there is no backend to send it to. It is rendered, + converted, and exported on your own device, and it is saved only in your browser's local + storage so your work survives a reload. There are no accounts, no logins, and no record + anywhere of what you wrote.

+ +

Your document

+
    +
  • Processed entirely on your device — there is no upload and no server-side conversion. + Every download format, including PDF and Word, is generated in the browser.
  • +
  • Autosaved to your browser's local storage under keys beginning mdw:, so + reloading the page does not lose your work.
  • +
  • Deleted when you clear it, use New, or clear your browser data. Because + it never leaves the device, there is no copy for us to delete.
  • +
  • Still available offline: the page keeps working once loaded, and the whole app can be + saved and opened from a local file:// copy, where it makes no network + requests at all.
  • +
+ +

Images you reference

+

This is the one case where using the editor can cause a request to leave your device, so it + is stated plainly rather than buried. If your Markdown references a remote image — + ![](https://example.com/photo.png) — the preview loads that image, which tells + the server hosting it your IP address and browser user-agent, exactly as it would on any web + page. That request goes to whatever host you referenced; it never passes through us, + and the text of your document is not part of it. Referencing no remote images means no such + request happens.

+ +

This website

+

markdownwizard.app is served by Cloudflare Pages, which receives standard web request + metadata (IP address, user agent, requested URL, timestamp) in order to deliver the page and + protect against abuse.

+

For analytics we use self-hosted, cookieless + Plausible, running on our + own server rather than a third-party service. It records that a page was visited — never what + you typed. There are no cookies, no cross-site tracking, no personal identifiers, and nothing + is shared with ad networks. It does not load at all when the app is opened offline from + file://.

+ +

What we never collect

+
    +
  • The content of your documents, in whole or in part
  • +
  • Document titles or file names
  • +
  • User accounts, email addresses, or passwords — there are none to collect
  • +
  • Payment information — Markdown Wizard is free to use and has no checkout
  • +
+ +

Data sharing

+

We do not sell, rent, or share your personal information. The only third parties involved + are Cloudflare, which serves this site, and our own self-hosted Plausible instance.

+ +

Children's privacy

+

Markdown Wizard does not knowingly collect any personal information from children under 13 + years of age.

+ +

Changes to this policy

+

We may update this policy from time to time; material changes are reflected in the + "Last updated" date above.

+ +

Contact

+

Questions about this policy? See the support page for how to reach + us.

+
+ + +
+ + + diff --git a/sitemap-0.xml b/sitemap-0.xml index 86b0874..b6b99ad 100644 --- a/sitemap-0.xml +++ b/sitemap-0.xml @@ -3,4 +3,16 @@ https://markdownwizard.app/ + + https://markdownwizard.app/docs + + + https://markdownwizard.app/privacy + + + https://markdownwizard.app/support + + + https://markdownwizard.app/terms + diff --git a/support.html b/support.html new file mode 100644 index 0000000..3e7a64f --- /dev/null +++ b/support.html @@ -0,0 +1,68 @@ + + + + + + Support — Markdown Wizard + + + + + + + + + +
+
Markdown Wizard
+ +
+ +
+
+

Support

+ +

Need help? Here is how to get it.

+ +

Before you write

+

Most questions are answered in the documentation — how the editor works, + every download format, the keyboard shortcuts, and what to do when an export does not look + the way you expected.

+

Three of the most common ones, answered here:

+
    +
  • "My document vanished." Autosave lives in one browser on one device. A + private window, a different browser, or clearing site data all start empty. Download + anything you need to keep.
  • +
  • "The PDF does not match the preview." Use + Download → Print… and choose "Save as PDF" — that route renders through + the browser's own engine and matches most closely.
  • +
  • "Is my writing being uploaded?" No. There is no server to upload it to. + The Privacy Policy spells out the details.
  • +
+ +

Report a bug or ask for a feature

+

Markdown Wizard is developed in the open. The fastest way to reach us is to + open + an issue on GitHub — bug reports, format problems, and feature requests all belong there, + and you can see what has already been reported.

+

A good bug report says which browser and operating system you are on, which download format + misbehaved, and what you expected instead. If a specific document triggers it, a small + example that reproduces the problem is worth more than anything else you can send.

+ +

Security

+

If you have found a security issue, please report it privately through + GitHub's + private vulnerability reporting rather than opening a public issue.

+
+ + +
+ + + diff --git a/terms.html b/terms.html new file mode 100644 index 0000000..80d3c7f --- /dev/null +++ b/terms.html @@ -0,0 +1,112 @@ + + + + + + Terms of Use — Markdown Wizard + + + + + + + + + +
+
Markdown Wizard
+ +
+ +
+
+

Terms of Use

+

Last updated: September 3, 2026

+ +

These Terms govern your use of Markdown Wizard (the "Service"). By using the Service, you + agree to them. This summary is provided for convenience and is not legal advice.

+ +

The product

+

Markdown Wizard is a Markdown editor and document converter that runs entirely in your + browser. There is no account, no upload, and no backend — your text is processed on your own + device, as described in the Privacy Policy. The Service is currently + free to use; there is no paid tier, no checkout, and no payment information is ever + collected.

+ +

Your content and your responsibility

+

The documents you write, open, and export belong to you and stay on your device — they are + never in our possession. You are responsible for how you use the Service and its output, and + for complying with the laws that apply to you.

+ +

Acceptable use

+

You agree not to use the Service to violate the law or the rights of others, and not to + attempt to disrupt, or gain unauthorized access to, the Service or its users.

+ +

Disclaimer of warranties

+

The Service is provided "as is" and "as available," without warranties of any kind, express + or implied. We do not guarantee uninterrupted or error-free operation. Document conversion in + particular is best-effort: exported files are generated by third-party libraries in your + browser, and fidelity varies by format and by the application you open them in. Keep your own + copy of anything important.

+ +

Limitation of liability

+

To the maximum extent permitted by law, Markdown Wizard and its developer are not liable for + indirect, incidental, or consequential damages, or for any loss of data or documents, or for + amounts exceeding the fees you paid in the 12 months before the claim (which, while the + Service is free, is $0).

+ +

Termination

+

You may stop using the Service at any time — closing the site abandons any locally stored + data, and clearing your browser data removes it. We may suspend or terminate access to + protect the Service and its users.

+ +

Changes

+

We may update these Terms; material changes are reflected in the "Last updated" date above. + Continued use after changes constitutes acceptance.

+ +

Governing law

+

These Terms are governed by and construed in accordance with the laws of the jurisdiction in + which the developer resides, without regard to conflict-of-law provisions.

+ +

Severability

+

If any provision of these Terms is found unenforceable or invalid, it shall be limited or + eliminated to the minimum extent necessary so that the remaining Terms stay in full force and + effect.

+ +

Third-party notices

+

Markdown Wizard is built with open-source components, each used under its own license. Full + license texts for the bundled libraries ship with the app under + js/vendor/licenses/.

+
    +
  • marked — Markdown + parser, MIT License
  • +
  • DOMPurify + — HTML sanitizer, Apache License 2.0 or Mozilla Public License 2.0
  • +
  • docx — Word + .docx generation, MIT License
  • +
  • pdfmake — + PDF generation, MIT License, bundling the + Roboto + typeface under the Apache License 2.0
  • +
  • markdownwizard-tools + — the conversion and export library behind the download menu, MIT License
  • +
  • Plausible Analytics — + AGPL-3.0, self-hosted
  • +
+ +

Contact

+

Questions about these Terms? See the support page for how to reach + us.

+
+ + +
+ + + diff --git a/tests/e2e.mjs b/tests/e2e.mjs index df74cdb..dd2cfd0 100644 --- a/tests/e2e.mjs +++ b/tests/e2e.mjs @@ -243,6 +243,57 @@ await page.click('#btnTheme'); await page.screenshot({ path: path.join(dlDir, 'screenshot-http.png') }); +console.log('\n== required pages (DS \u00a79) =='); + +// These four pages all returned 404 on the live site until 2026-09-03. A +// missing required page is invisible from the editor -- nothing links to it +// from the app shell by default and nothing else in this suite loads it -- so +// it is checked here rather than trusted. The canonical assertion is the half +// that catches a copy-paste: four pages built from one template will happily +// ship four identical canonical URLs, which is worse than none. +const REQUIRED = [ + { path: 'docs.html', route: '/docs', h1: 'Documentation' }, + { path: 'privacy.html', route: '/privacy', h1: 'Privacy Policy' }, + { path: 'support.html', route: '/support', h1: 'Support' }, + { path: 'terms.html', route: '/terms', h1: 'Terms of Use' }, +]; + +for (const pg of REQUIRED) { + await page.goto(`http://127.0.0.1:${port}/${pg.path}`); + const h1 = (await page.textContent('h1').catch(() => '')) || ''; + check(`${pg.route} renders its heading`, h1.trim() === pg.h1, h1.trim()); + + const canonical = await page.getAttribute('link[rel=canonical]', 'href').catch(() => null); + check(`${pg.route} declares its own canonical`, + canonical === `https://markdownwizard.app${pg.route}`, String(canonical)); + + // The app shell is a fixed-height flex column with overflow:hidden, so a long + // document only scrolls if .page supplies its own scroll container. Without it + // the page renders and silently truncates -- it looks fine in a screenshot. + const scrollable = await page.evaluate(() => { + const el = document.querySelector('.page'); + return !!el && getComputedStyle(el).overflowY === 'auto'; + }); + check(`${pg.route} content can scroll`, scrollable); + + const linked = await page.evaluate(() => + [...document.querySelectorAll('.pagefoot a')].map((a) => a.getAttribute('href'))); + check(`${pg.route} carries the site nav`, REQUIRED.every((r) => linked.includes(r.route)), + linked.join(' ')); +} + +// Reachability: a required page nothing links to is a page nobody finds. The +// editor has no site footer, so these live on the toolbar's trailing edge. +await page.goto(`http://127.0.0.1:${port}/`); +const editorLinks = await page.evaluate(() => + [...document.querySelectorAll('.tlinks a')].map((a) => a.getAttribute('href'))); +check('editor links every required page', + REQUIRED.every((r) => editorLinks.includes(r.route)), editorLinks.join(' ')); + +const sitemap = fs.readFileSync(path.join(appDir, 'sitemap-0.xml'), 'utf8'); +check('sitemap lists every required page', + REQUIRED.every((r) => sitemap.includes(`https://markdownwizard.app${r.route}<`))); + console.log('\n== file:// operation (offline / double-click use) =='); const page2 = await browser.newPage({ viewport: { width: 1440, height: 900 } }); const errors2 = []; From 1d87410007f90ed1690c6ce3b9c03f56ebe4db6b Mon Sep 17 00:00:00 2001 From: Michal Ferber Date: Thu, 3 Sep 2026 17:00:34 -0400 Subject: [PATCH 2/3] =?UTF-8?q?TEMPORARY:=20induce=20a=20duplicated=20cano?= =?UTF-8?q?nical=20to=20calibrate=20the=20=C2=A79=20guard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not for merge; reverted in the next commit. The guard added in this branch has never been watched failing, which makes its green indistinguishable from a green it would return no matter what. This is the realistic defect it exists to catch: four pages built from one template, one of them keeping the canonical it was copied from. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_0126UXfnHngrTg7kqmw87ipV --- terms.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terms.html b/terms.html index 80d3c7f..9b22190 100644 --- a/terms.html +++ b/terms.html @@ -5,7 +5,7 @@ Terms of Use — Markdown Wizard - + From 44d23cec6a876c2dc8406bf6720550dd8101d3cf Mon Sep 17 00:00:00 2001 From: Michal Ferber Date: Thu, 3 Sep 2026 17:01:46 -0400 Subject: [PATCH 3/3] Revert the induced canonical, the guard having been seen to fail Calibration run 33805551669 went red with "FAIL - /terms declares its own canonical [https://markdownwizard.app/privacy]" and the other seventeen checks stayed green, so the guard is both wired up and specific -- it names the wrong value rather than reporting a blanket failure. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_0126UXfnHngrTg7kqmw87ipV --- terms.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terms.html b/terms.html index 9b22190..80d3c7f 100644 --- a/terms.html +++ b/terms.html @@ -5,7 +5,7 @@ Terms of Use — Markdown Wizard - +