morphit/docs/adr/0003-seo-policy.md

152 lines
6.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ADR-0003: SEO and crawler policy
**Status:** Accepted
**Date:** 2026-04-17
**Deciders:** project maintainer
## Context
Morphit is built for privacy-minded users, but the marketplace only works
if people can find it. Word-of-mouth and Blurt-community backlinks drive
some traffic, but organic search is the main acquisition channel for most
non-technical users — exactly the audience most underserved by existing
P2P crypto markets since LocalBitcoins and LocalMonero shut down.
At the same time, Morphit has anti-commitments that are unusual in
SEO practice:
- **No user tracking.** Google Analytics, Meta Pixel, Hotjar, Clarity —
all forbidden. Any SEO plan that relies on engagement signals
originating from tracking scripts is unavailable to us.
- **No content farming.** We don't publish SEO-bait blog posts.
What we have is the landing page, the FAQ, and a few reference
routes. That's it.
- **No per-user personalization.** Every user sees the same rendered
HTML for the same path. This actually *helps* SEO (clean, static
pages are easy to crawl) but it means no dynamic recommendations or
"trending" tricks.
- **Privacy-focused users may visit via Tor Browser or with
JS disabled.** The first-paint HTML must be genuinely useful with
zero JavaScript.
The question is what SEO investment makes sense given these constraints.
## Decision
Implement **classical, fully on-page SEO** — everything a well-indexed
2010-era documentation site would do, and nothing more:
1. Per-route `<title>` + `<meta name="description">`, localized in
every supported language.
2. Open Graph + Twitter Card metadata with a branded hero image.
3. A static `robots.txt` that allows all crawlers, and a static
`sitemap.xml` generated at build time with `hreflang` alternates
for all 10 languages (the ADR was originally written when the
supported set was 8; zh-CN and zh-HK were added later).
4. JSON-LD structured data:
- `Organization` schema on the home page (WebSite + Organization).
- `FAQPage` schema on `/faq`, with every entry expressed as a
`Question` node. This is the lever that gets Google to show FAQ
entries as rich snippets in search results — direct, measurable
value for the "how do I buy Monero with cash" query class.
5. Canonical URLs on every page, so `morphit.io/faq?q=foo&lang=es`
canonicalizes cleanly against `morphit.io/faq` (FAQ query-string
params are presentation, not new pages).
6. `<link rel="alternate" hreflang="x">` on every localized page
pointing at the same path in every other locale.
7. Semantic HTML — one `<h1>` per page, proper `<header>`/`<main>`/
`<nav>`/`<footer>`, descriptive link text, no "click here".
8. Accurate `lang` attribute on `<html>` matching the active locale,
kept in sync on locale switch (already done in `setLocale`; the
static SSG render also needs to emit `en` as the default).
**What we do NOT do:**
- No dynamically rendered / JS-required SEO content. Crawlers that run
JS (Googlebot, Bingbot, DuckDuckGo) will see the same content
non-JS crawlers see, because our SSG output is complete HTML.
- No `ai.txt`, no special robots rules, no scraper-class allowlists or
denylists. Our content is public, our license is AGPL-3, anyone can
quote us. Playing gatekeeper games with downstream consumers
achieves nothing and costs legitimacy with readers who consume
through aggregators. The robots policy is uniformly permissive
for everyone.
- No keyword stuffing. Meta descriptions are written as sentences a
human would read, with relevant phrases woven in naturally.
- No schema markup for orders or trades. The on-chain data lives on
Blurt; indexing it into Google's knowledge graph isn't useful and
raises trade-privacy issues (see ADR-0002 corollary: everything on
chain is public, but *surfacing* it through mainstream search is a
separate choice we decline to make in Phase 2).
## Target keyword clusters
Meta descriptions are written around, but do not stuff, these clusters
(validated informally as high-intent for the Morphit value proposition):
- `peer-to-peer bitcoin exchange no kyc`
- `non-custodial monero trading` / `buy monero with cash`
- `private crypto marketplace` / `decentralized fiat to bitcoin`
- `localbitcoins alternative` / `localmonero alternative` /
`haveno alternative`
- `censorship-resistant bitcoin exchange`
- `sell bitcoin without ID` / `sell monero anonymously`
These drive the copy in meta descriptions and OG tags. None appear as
invisible keyword dumps in the body — they all appear in natural prose
on the home page or relevant FAQ entry.
## Alternatives considered
- **Paid search / display ads.** Rejected — no budget and no
tracking means we can't attribute, and ads on privacy-focused
products attract exactly the wrong attention from ad networks.
- **Build an SEO content blog.** Rejected for Phase 2 — scope
creep; content marketing is a full project of its own and
the existing FAQ covers most high-intent queries.
- **Aggressive JSON-LD** (BreadcrumbList, Article schema on every
page, etc.). Rejected as diminishing returns. FAQPage and
Organization are the two that produce visible SERP changes; the
rest is noise.
- **Serve a `/llms.txt` manifest.** Considered; the standard is
emerging but not widely respected. If it becomes a real signal
later, adding it is a one-file change. Not in Phase 2.
- **Opt out of automated scrapers via robots.txt.** Rejected — see
"What we do NOT do" above.
## Consequences
### Positive
- FAQ entries become eligible for rich-snippet display in Google
SERPs. For long-tail queries ("how do I sell monero without kyc"),
this can put a Morphit answer directly in the results page.
- Crawlers get complete, localized content with correct language
signals. Spanish-speaking users searching in Spanish find the
Spanish version; German speakers find the German version.
- The site scores well on Lighthouse / PageSpeed Insights /
accessibility audits, which indirectly helps ranking and directly
helps non-SEO readers.
### Negative
- More HTML per page (meta tags, JSON-LD, hreflang). Maybe +35 KB
per page before compression. Brotli crushes this to near-zero.
- Every new localized route needs its SEO copy in every locale
alongside the UI copy. Disciplined but not hard.
### Follow-up work
- Phase 3: add `Article` schema to any per-indexer RSS output.
- Phase 5: consider `/llms.txt` if the standard gains adoption.
- Whenever we add a new route, the PR template must include the
"SEO copy updated in all locales" checkbox.
## References
- Google FAQPage structured data docs:
https://developers.google.com/search/docs/appearance/structured-data/faqpage
- Web.dev SEO checklist:
https://web.dev/learn/seo
- `hreflang` spec:
https://developers.google.com/search/docs/specialty/international/localized-versions