diff --git a/web/README.md b/web/README.md
index fdab8ff..ad2ae18 100644
--- a/web/README.md
+++ b/web/README.md
@@ -58,6 +58,11 @@ The canonical site URL + Twitter handle live in
[`src/lib/site.ts`](src/lib/site.ts) (`SITE.url`, `SITE.twitterHandle`) —
update both there if the domain or handle ever changes.
+The keyword strategy that drives every page's H1, lede, and `metadata`
+export lives in [`seo/keywords.md`](seo/keywords.md). When rewriting a
+page's headline or meta description, update the corresponding entry in
+that sheet so future rewrites stay coordinated.
+
## Local development
```sh
@@ -80,6 +85,8 @@ npm run lint # next lint (ESLint)
web/
├── content/
│ └── blog/ # MDX posts (one .mdx file per post; frontmatter at top)
+├── seo/
+│ └── keywords.md # keyword research + per-page primary/secondary registry (SQLR-33)
├── src/
│ ├── app/
│ │ ├── globals.css # design tokens + utility CSS (ports the original design's styles.css)
diff --git a/web/seo/keywords.md b/web/seo/keywords.md
new file mode 100644
index 0000000..f201966
--- /dev/null
+++ b/web/seo/keywords.md
@@ -0,0 +1,161 @@
+# SQLRite keyword sheet
+
+Last updated: 2026-05-12 (SQLR-33 — initial keyword research + on-page SEO pass).
+
+## How to read this
+
+Three buckets — head terms (high volume, hard), mid-tail (the sweet spot
+SQLRite can realistically rank for), and long-tail / informational
+queries (drive blog topics). For each entry:
+
+- **Target page** — where on the site the term should land.
+- **Primary keyword** — the dominant query the page is optimized for.
+- **Secondary keywords** — supporting phrases woven into headings, body,
+ and metadata.
+- **Intent** — `informational` (someone learning), `commercial` (someone
+ evaluating a tool), `navigational` (someone looking for SQLRite by
+ name).
+- **Priority** — `P0` (rewrite now), `P1` (next pass), `P2` (later /
+ blog backlog).
+
+Relative ordering matters more than absolute volume — we have no SEO
+budget, just SERP inspection and a hunch about what an embedded-database
+shopper actually types. Free tools that worked during the first pass:
+Google's "people also ask" / autocomplete, the SERP for the literal
+phrase, and Ahrefs' free keyword generator.
+
+## Conventions
+
+- Canonical host: `https://sqlritedb.com`. Never link to a different
+ origin in canonical-eligible copy.
+- All metadata `description` fields should clear ~150 chars; ~160 max
+ before Google truncates.
+- Don't promise features that don't exist. No "distributed SQLRite",
+ no "replication", no "production-grade" until the roadmap says so.
+- Voice is technical, slightly playful, no marketing buzzwords. "Built
+ to teach what databases actually do" stays — that's the brand.
+
+## Head terms — high volume, low realistic CTR for now
+
+| Primary | Secondary | Intent | Priority | Target page | Notes |
+| --- | --- | --- | --- | --- | --- |
+| embedded database | embedded SQL, single-file database, embedded SQLite | commercial | P0 | `/` | We won't win this — but the landing page should still cleanly contain the phrase in H1 + first 160 chars. |
+| SQLite alternative | SQLite-compatible, modern SQLite, SQLite in Rust | commercial | P0 | `/` and benchmarks section | Comparison framing on the benchmarks section earns a sliver of long-tail SQLite-vs traffic. |
+| Rust database | embedded Rust database, Rust SQL crate | commercial | P0 | `/` | Mentioned in H1 + features intro. |
+| vector database | embedded vector DB, vector search database | commercial | P1 | `/` (vector section anchor) | The market is owned by hosted services; SQLRite competes on "embedded + SQL + vector in one file". |
+
+## Mid-tail — the sweet spot we can actually rank for
+
+| Primary | Secondary | Intent | Priority | Target page | Suggested H1 / H2 | Meta description draft |
+| --- | --- | --- | --- | --- | --- | --- |
+| embedded database in Rust | embedded SQL engine Rust, single-file DB Rust | commercial | P0 | `/` | "SQLRite — an embedded SQL + vector database in Rust" (H1) | "SQLRite is an embedded SQL + vector database in Rust. SQLite-style single-file format, WAL transactions, HNSW vector search, BM25 full-text, six SDKs." |
+| SQLite alternative for Rust | Rust SQLite alternative, SQLite-compatible Rust crate | commercial | P0 | `/` (hero + features) | Feature section retains "13 features" framing; copy mentions "SQLite-compatible API". | Same as landing — composition handled in lede + first feature copy. |
+| embedded vector search Rust | vector search in Rust, HNSW Rust embedded | commercial | P0 | `/docs#vector` and `/` vector feature | "Built-in vector search with HNSW" (H3 in features) | "Add HNSW vector search to your Rust app with a single CREATE INDEX. Cosine / dot / L2 distance, sub-linear k-NN, no external service." |
+| embedded database with HNSW | HNSW SQLite, vector index embedded DB | informational | P1 | `/docs#vector` | `/docs` H2 ("Vector search") stays as-is. | Captured by /docs metadata. |
+| sqlite-compatible Rust crate | drop-in SQLite Rust, sqlite parser Rust | navigational | P1 | `/` features section | "Single-file format" / "Supported SQL" tags carry the term. | n/a |
+| Rust embedded SQL engine | Rust SQL parser engine, embedded SQL crate | commercial | P1 | `/` | Features intro. | n/a |
+| embedded SQL + vector database | embedded SQL vector DB, vector + SQL one file | commercial | P0 | `/` | New H1 leads with this. | Landing description. |
+| Rust embedded database with WAL | WAL Rust crate, embedded database WAL | informational | P1 | `/docs#persistence` | `/docs` "Persistence & the WAL" section. | Captured by /docs metadata; long-tail blog candidate. |
+
+## Long-tail / informational — easy wins + blog backlog
+
+| Primary | Intent | Priority | Target page | Notes |
+| --- | --- | --- | --- | --- |
+| how to add vector search to SQLite | informational | P1 | future blog post | Open as blog backlog ticket. Working title: "Adding HNSW vector search to a SQLite-style engine". |
+| SQLite vs SQLRite benchmarks | informational / commercial | P0 | benchmarks section + `/blog/sqlrite-vs-sqlite-benchmarks` | Benchmarks H2 now leads with "SQLRite vs SQLite benchmarks". Blog post already exists. |
+| embedded database for desktop Tauri apps | commercial | P1 | `/docs#desktop` + future blog post | Existing docs section is short; expand later. Blog candidate: "Shipping a Tauri 2 app with an embedded SQL database." |
+| natural language to SQL Rust crate | informational | P1 | `/docs` (.ask section, currently terse) + blog | Open ticket: "Doc the `.ask` REPL + `ConnectionAskExt::ask` API" + dedicated blog. |
+| MCP server for SQLite | informational | P0 | `/docs#mcp` | Docs section stays. Worth a dedicated blog post: "Wiring a SQLite-style engine into Claude Code via MCP". |
+| rust embedded database with WAL | informational | P1 | `/docs#persistence` | Already covered; long-tail traffic. |
+| how to do hybrid retrieval in Rust | informational | P2 | future blog post (vector + BM25) | We already have `examples/hybrid-retrieval`. Blog candidate. |
+| BM25 full-text search Rust | informational | P1 | `/docs#fts` | Section title already mentions BM25; meta copy captures it. |
+| single-file SQL database | informational | P1 | `/` | Features H3 stays. |
+| WASM SQL database in browser | informational | P2 | `/docs#sdk-wasm` + future blog | Blog candidate: "Running a SQL engine in a browser tab with WASM". |
+
+## Per-page primary + secondary registry
+
+This is the authoritative cross-reference for the on-page rewrites
+landed in SQLR-33. Per the acceptance criteria, every P0 page records
+its primary + secondary keyword here.
+
+### `/` (landing)
+
+- **Primary:** embedded SQL + vector database in Rust
+- **Secondary:** embedded database, SQLite alternative, Rust database,
+ HNSW vector search, BM25 full-text search, MCP server
+- **H1:** "SQLRite — an embedded SQL + vector database in Rust."
+- **Meta description (≤ 160 chars):** "SQLRite is an embedded SQL +
+ vector database in Rust. SQLite-style single-file format, WAL
+ transactions, HNSW vector search, BM25 full-text, six SDKs."
+
+### `/docs`
+
+- **Primary:** SQLRite documentation / getting started with the
+ embedded Rust database
+- **Secondary:** embedded database tutorial Rust, vector search Rust
+ quickstart, BM25 Rust, MCP server SQLite
+- **H1:** "SQLRite docs — getting started with the embedded Rust
+ database."
+- **Meta description:** "Install SQLRite, open your first .sqlrite
+ file, and run SQL — transactions, JOINs, HNSW vector search, BM25
+ full-text, and six language SDKs."
+
+### Benchmarks section (`#benchmarks` on `/`)
+
+There is no dedicated `/benchmarks` route — benchmarks live as a
+section on the landing page. The H2 + sub-copy carries the
+comparison query.
+
+- **Primary:** SQLRite vs SQLite benchmarks
+- **Secondary:** Rust embedded database benchmark, SQLite alternative
+ performance
+- **H2:** "SQLRite vs SQLite benchmarks — honest numbers, published in
+ public."
+- **Body sentence carries:** "Twelve workloads against SQLite
+ (WAL+NORMAL) and DuckDB …"
+
+### `/blog`
+
+- **Primary:** building an embedded database in Rust
+- **Secondary:** Rust database blog, SQLite-style engine notes
+- Existing H2 + meta cover this; no rewrite this pass.
+
+## Internal-linking notes (SQLR-33 sweep)
+
+- Audit confirmed no `click here` / `read more` / `here.` anchors in
+ `web/src`. Anchors are descriptive (`Read the docs`, `Star on
+ GitHub`, `All posts`, post-title pager links).
+- `/docs` is a single-page guide with anchored sections rather than
+ many child pages, so the "every doc page links to ≥ 2 sibling
+ pages" rule is met via the global `` (links to `/blog`,
+ `/blog/rss.xml`, GitHub) plus the in-page docs CTA card. The CTA
+ card now exposes an explicit `/blog` link as a second descriptive
+ in-site sibling (besides `/`).
+- Landing hero's primary CTA continues to point at `/docs`. Feature
+ cards covering Vector / FTS / MCP / SDKs already act as the "top
+ docs entry points" via the rest of the landing IA (`Architecture`,
+ `Roadmap`, `SDKShowcase`).
+
+## Re-crawl plan
+
+After every rewrite pass, re-run a free crawler (Screaming Frog up to
+500 URLs is plenty) against `https://sqlritedb.com` and verify:
+
+- **No duplicate `
`** across `/`, `/docs`, `/blog`, blog
+ posts, blog tag pages.
+- **No duplicate meta description** across the same set.
+- **No duplicate H1** per page.
+- **No orphan pages** — every URL in `/sitemap.xml` is reachable
+ from at least one other URL.
+
+`src/app/sitemap.ts` is the canonical URL list; keep it in sync when a
+new route ships.
+
+## Open backlog (split out of SQLR-33)
+
+- Blog post: "Adding HNSW vector search to a SQLite-style engine".
+- Blog post: "Wiring a SQLite-style engine into Claude Code via MCP".
+- Blog post: "Shipping a Tauri 2 app with an embedded SQL database".
+- Blog post: "Running a SQL engine in a browser tab with WASM".
+- Docs expansion: dedicate a `.ask` / natural-language-to-SQL section
+ in `/docs` once the API stabilizes.
diff --git a/web/src/app/docs/page.tsx b/web/src/app/docs/page.tsx
index 4ee8d19..d50af70 100644
--- a/web/src/app/docs/page.tsx
+++ b/web/src/app/docs/page.tsx
@@ -4,9 +4,14 @@ import { Footer } from "@/components/footer";
import { Nav } from "@/components/nav";
import { SITE } from "@/lib/site";
-const TITLE = "Getting started with SQLRite";
+// SEO targeting: primary "SQLRite documentation / getting started with the
+// embedded Rust database"; secondary "embedded database tutorial Rust",
+// "vector search Rust quickstart", "BM25 Rust", "MCP server SQLite".
+// See web/seo/keywords.md.
+const TITLE =
+ "SQLRite docs — getting started with the embedded Rust database";
const DESCRIPTION =
- "A ten-minute tour from cargo install to a persistent on-disk SQLRite database — REPL, transactions, JOINs, vector search, BM25 full-text, and six language SDKs.";
+ "Install SQLRite, open your first .sqlrite file, and run SQL — transactions, JOINs, HNSW vector search, BM25 full-text, an MCP server, and six language SDKs.";
export const metadata: Metadata = {
title: TITLE,
@@ -123,12 +128,19 @@ export default function DocsPage() {
docs · getting started
-
Getting started with SQLRite
+
+ SQLRite docs — getting started with the embedded Rust database
+
- A ten-minute tour from cargo install to a persistent
- on-disk database with real transactions, vector search, and
- full-text search. Pick the SDK that fits your language at the
- bottom — they all wrap the same engine.
+ SQLRite is an embedded SQL + vector database in Rust. This page is
+ a ten-minute tour from cargo install to a persistent
+ on-disk .sqlrite file — transactions, JOINs, HNSW
+ vector search, BM25 full-text, and the MCP server. Skip ahead to{" "}
+ vector search,{" "}
+ full-text search, the{" "}
+ MCP server, or pick the SDK that fits your
+ language at the bottom — they all wrap the
+ same engine.
Install
@@ -804,8 +816,11 @@ export default function DocsPage() {
Join the Discord
+
+ Read the SQLRite blog
+
- ← Back to landing
+ ← Back to the SQLRite home page
diff --git a/web/src/app/layout.tsx b/web/src/app/layout.tsx
index 729dfb6..4009c47 100644
--- a/web/src/app/layout.tsx
+++ b/web/src/app/layout.tsx
@@ -32,9 +32,12 @@ const jetbrainsMono = JetBrains_Mono({
display: "swap",
});
-const DEFAULT_TITLE = "SQLRite — embedded SQL database, built in Rust";
+// Header copy is the primary on-page SEO surface — see web/seo/keywords.md
+// for the keyword strategy and per-page rationale.
+const DEFAULT_TITLE =
+ "SQLRite — an embedded SQL + vector database in Rust";
const DEFAULT_DESCRIPTION =
- "An embedded SQL database modeled after SQLite, built from scratch in Rust. Single-file format, real B-tree, WAL, transactions, vector search, full-text search, and six language SDKs.";
+ "SQLRite is an embedded SQL + vector database in Rust. SQLite-style single-file format, WAL transactions, HNSW vector search, BM25 full-text, and six language SDKs.";
export const metadata: Metadata = {
metadataBase: new URL(SITE.url),
@@ -47,17 +50,17 @@ export const metadata: Metadata = {
authors: [{ name: "Joao Henrique Machado Silva", url: SITE.socials.github }],
keywords: [
"SQLRite",
- "Rust SQLite",
- "embedded database",
- "embedded SQL",
+ "embedded database in Rust",
+ "embedded SQL + vector database",
+ "SQLite alternative",
+ "SQLite-compatible Rust crate",
"Rust database",
- "vector search",
+ "embedded vector search Rust",
"HNSW",
- "BM25",
- "full-text search",
+ "BM25 full-text search",
"WAL",
- "B-tree",
- "MCP",
+ "single-file database",
+ "MCP server for SQLite",
],
openGraph: {
type: "website",
diff --git a/web/src/app/page.tsx b/web/src/app/page.tsx
index b8d6c6b..4f1589d 100644
--- a/web/src/app/page.tsx
+++ b/web/src/app/page.tsx
@@ -13,9 +13,12 @@ import { SDKShowcase } from "@/components/sdk-showcase";
import { SQLRef } from "@/components/sql-ref";
import { SITE } from "@/lib/site";
-const TITLE = "SQLRite — embedded SQL database, built in Rust";
+// SEO targeting: primary "embedded SQL + vector database in Rust",
+// secondary "SQLite alternative" / "Rust database" / "HNSW vector search" /
+// "BM25 full-text" / "MCP server". See web/seo/keywords.md.
+const TITLE = "SQLRite — an embedded SQL + vector database in Rust";
const DESCRIPTION =
- "Single-file embedded SQL engine in Rust. Real B-tree, WAL, transactions, JOINs, aggregates, HNSW vector search, BM25 full-text, plus six language SDKs.";
+ "SQLRite is an embedded SQL + vector database in Rust — a SQLite alternative with WAL transactions, HNSW vector search, BM25 full-text, and six language SDKs.";
export const metadata: Metadata = {
title: { absolute: TITLE },
diff --git a/web/src/components/benchmarks.tsx b/web/src/components/benchmarks.tsx
index c6fd4da..2d5e10f 100644
--- a/web/src/components/benchmarks.tsx
+++ b/web/src/components/benchmarks.tsx
@@ -81,13 +81,14 @@ export function Benchmarks() {
07 · benchmarks
-
Honest numbers, published in public.
+
SQLRite vs SQLite benchmarks — honest numbers, published in public.
- Twelve workloads against SQLite (WAL+NORMAL) and DuckDB on a
- pinned-host run. The point isn’t to win — SQLite has 25
- years of optimization behind it — it’s to baseline future
- engine work, prove the differentiator workloads deliver, and
- ground the roadmap with evidence.
+ Twelve workloads pitch SQLRite, the embedded Rust database,
+ against SQLite (WAL+NORMAL) and DuckDB on a pinned-host run.
+ The point isn’t to win — SQLite has 25 years of
+ optimization behind it — it’s to baseline future engine
+ work, prove the differentiator workloads (HNSW vector search,
+ BM25 full-text) deliver, and ground the roadmap with evidence.
diff --git a/web/src/components/hero.tsx b/web/src/components/hero.tsx
index b6e18e7..1bc254f 100644
--- a/web/src/components/hero.tsx
+++ b/web/src/components/hero.tsx
@@ -14,15 +14,15 @@ export function Hero() {
v{SITE.version} · MIT licensed · open source
- An embedded SQL database,{" "}
- built from scratch in Rust.
+ SQLRite — an embedded SQL +{" "}
+ vector database in Rust.
- SQLRite is a from-the-ground-up reimagining of SQLite — a
- single-file engine with a real B-tree, write-ahead log,
- transactions, JOINs, aggregates, vector search, full-text search,
- and bindings for six languages. Built to teach what databases
- actually do.
+ SQLRite is a from-scratch SQLite alternative — a single-file
+ embedded database in Rust with a real B-tree, write-ahead log,
+ transactions, JOINs, aggregates, HNSW vector search, BM25
+ full-text search, and bindings for six languages. Built to teach
+ what databases actually do.