Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .cursor/memory/mistakes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Project Memory — Mistakes & Corrections

Persistent lessons from user corrections. All agents must read this before feature work and must not repeat listed mistakes.

## Entry format

When appending, use the next `MEM-###` ID and this structure:

```markdown
### MEM-001 — YYYY-MM-DD

- **Category:** e.g. state-management, navigation, styling, workflow
- **Mistake:** What the agent did wrong (one sentence)
- **Do instead:** The correct behavior or pattern
- **Context:** When this applies (feature type, file, stack area)
- **Source:** user | review-loop
```

## Entries

<!-- New entries are appended below. Do not delete past entries unless the user asks. -->

### MEM-001 — 2026-07-31

- **Category:** seo, content
- **Mistake:** Shipping meta titles/descriptions outside SEO length targets, or “fixing” length by truncating existing titles/descriptions mid-phrase.
- **Do instead:** Keep titles 30–60 chars (CJK min 20) and descriptions 120–160 chars (CJK min 70). Whitepaper base titles ≤52 for ` - vX.Y.Z`. Always **rewrite** meta from page/post substance so the result is a complete, natural phrase—never chop an old title/description to fit. After meta changes, run `npm run check:seo-meta` in `website/`.
- **Context:** i18n `*.meta.title` / `*.meta.description`, blog frontmatter `title`/`description`, `default-metadata.ts`, any new page SEO
- **Source:** user

### MEM-002 — 2026-07-31

- **Category:** branding, content
- **Mistake:** Using the brand form “Quantus Network” (or localized equivalents like “Jaringan Quantus”, “Quantusネットワーク”, “Сеть Quantus”) in site copy.
- **Do instead:** Use **Quantus** only. When shortening meta titles, rewrite to stay within SEO length targets (MEM-001) without reintroducing “Network”.
- **Context:** Especially `website/src/i18n/*.json` meta and UI strings; avoid the phrase in new copy
- **Source:** user
47 changes: 47 additions & 0 deletions .cursor/rules/memory.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
description: Read and update project mistake memory so corrections are not repeated.
alwaysApply: true
---

# Project Memory

## Read before acting

At the start of any task (including orchestration and subagent work), read `.cursor/memory/mistakes.md`.

- Apply every entry that matches the current task category or context.
- When delegating via Task, include **relevant memory entries** in the subagent prompt.
- If a memory entry conflicts with a new user instruction, follow the user and ask whether to update the memory entry.

## When the user flags a mistake

Treat these as memory-save triggers (and similar phrasing):

- "that's a mistake" / "that's wrong"
- "remember this" / "add to memory"
- "don't do that again"
- "you got this wrong"
- "save this correction"

When triggered:

1. **Do not argue** — treat the user's correction as authoritative.
2. **Extract** a concise mistake + correct behavior + context.
3. **Check** existing entries in `mistakes.md` for duplicates or overlap.
- If an entry already covers it, update that entry's **Do instead** or **Context** instead of adding a duplicate.
- Otherwise append a new `MEM-###` entry at the bottom of the **Entries** section.
4. **Confirm** briefly what was saved (ID, mistake, do-instead).

Recording memory is **not** implementation work — the orchestrator (or any agent when not in a readonly phase) may edit `mistakes.md` directly.

## During the pipeline

- **Research / critique / implement / review:** Honor memory entries; proposals and code must not repeat past mistakes.
- **Review loop rejections:** If the user says a rejection reflects a lasting project preference, offer to save it to memory.
- **After handoff:** If the user corrects the result, save to memory before doing follow-up work.

## Entry quality

- One lesson per entry; keep each bullet short and actionable.
- Prefer project-specific guidance over generic advice.
- Use categories that help future matching: `state-management`, `navigation`, `ui`, `testing`, `workflow`, `packages`, `architecture`.
3 changes: 2 additions & 1 deletion website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"preview": "astro preview",
"astro": "astro",
"format:check": "prettier --check .",
"format:write": "prettier --write ."
"format:write": "prettier --write .",
"check:seo-meta": "node scripts/check-seo-meta.mjs"
},
"dependencies": {
"@fontsource/geist": "^5.2.8",
Expand Down
5 changes: 5 additions & 0 deletions website/public/_headers
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# Security headers: clickjacking + full CSP (GA, Twitter, YouTube, Quantus APIs)
/*
X-Frame-Options: DENY
Content-Security-Policy: default-src 'self'; base-uri 'self'; form-action 'self'; object-src 'none'; worker-src 'none'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://platform.twitter.com; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self' data: https://img.youtube.com https://i.ytimg.com https://pbs.twimg.com https://abs.twimg.com https://ton.twimg.com https://platform.twitter.com https://syndication.twitter.com https://www.google-analytics.com https://www.googletagmanager.com; media-src 'self'; connect-src 'self' https://api.quantus.com https://quests.quantus.com https://sub2.quantus.com wss://feed-telemetry.quantus.cat https://www.googletagmanager.com https://www.google-analytics.com https://*.google-analytics.com https://*.analytics.google.com https://stats.g.doubleclick.net https://platform.twitter.com https://cdn.syndication.twimg.com https://syndication.twitter.com https://syndication.x.com; frame-src https://www.youtube-nocookie.com https://platform.twitter.com https://syndication.twitter.com https://twitter.com https://x.com; frame-ancestors 'none'

/.well-known/api-catalog
Content-Type: application/linkset+json; profile="https://www.rfc-editor.org/info/rfc9727"

Expand Down
151 changes: 151 additions & 0 deletions website/scripts/check-seo-meta.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#!/usr/bin/env node
/**
* Validates SEO title/description lengths for i18n page meta and blog frontmatter.
*
* Latin locales: title 30–60, description 120–160
* CJK locales (ja-JP, ko-KR, zh-CN): title 20–60, description 70–160
* Whitepaper titles max 52 (room for " - vX.Y.Z" suffix)
*/
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const websiteRoot = path.resolve(__dirname, "..");
const i18nDir = path.join(websiteRoot, "src/i18n");
const blogsDir = path.join(websiteRoot, "src/contents/blogs");

const CJK = new Set(["ja-JP", "ko-KR", "zh-CN"]);

const TITLE_MAX = 60;
const WHITEPAPER_TITLE_MAX = 52;
const DESC_MAX = 160;

function limitsFor(locale) {
if (CJK.has(locale)) {
return { titleMin: 20, descMin: 70 };
}
return { titleMin: 30, descMin: 120 };
}

function walkJson(obj, prefix = "") {
const out = [];
if (obj && typeof obj === "object" && !Array.isArray(obj)) {
for (const [k, v] of Object.entries(obj)) {
const next = prefix ? `${prefix}.${k}` : k;
out.push(...walkJson(v, next));
}
} else {
out.push([prefix, obj]);
}
return out;
}

function checkLength(kind, locale, key, value, errors) {
const text = String(value ?? "");
const len = [...text].length; // code-point length
const { titleMin, descMin } = limitsFor(locale);

if (kind === "title") {
const max = key.includes("whitepaper") ? WHITEPAPER_TITLE_MAX : TITLE_MAX;
if (len < titleMin || len > max) {
errors.push(
`[title] ${locale} ${key} len=${len} (want ${titleMin}-${max}): ${text}`,
);
}
} else {
if (len < descMin || len > DESC_MAX) {
errors.push(
`[description] ${locale} ${key} len=${len} (want ${descMin}-${DESC_MAX}): ${text.slice(0, 100)}`,
);
}
}
}

function checkI18n(errors) {
for (const file of fs.readdirSync(i18nDir).filter((f) => f.endsWith(".json"))) {
const locale = path.basename(file, ".json");
const data = JSON.parse(fs.readFileSync(path.join(i18nDir, file), "utf8"));
for (const [key, value] of walkJson(data)) {
if (key.endsWith(".meta.title")) {
checkLength("title", locale, key, value, errors);
} else if (key.endsWith(".meta.description")) {
checkLength("description", locale, key, value, errors);
}
}
}
}

function parseFrontmatter(raw) {
if (!raw.startsWith("---")) return null;
const end = raw.indexOf("---", 3);
if (end === -1) return null;
const fm = raw.slice(3, end);
const titleMatch = fm.match(/^title:\s*(.*)$/m);
const descMatch = fm.match(/^description:\s*(.*)$/m);
if (!titleMatch || !descMatch) return null;

const unquote = (s) => {
const t = s.trim();
if (
(t.startsWith('"') && t.endsWith('"')) ||
(t.startsWith("'") && t.endsWith("'"))
) {
return t.slice(1, -1);
}
return t;
};

return {
title: unquote(titleMatch[1]),
description: unquote(descMatch[1]),
};
}

function walkFiles(dir) {
const out = [];
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) out.push(...walkFiles(full));
else if (/\.(md|mdx)$/.test(entry.name)) out.push(full);
}
return out;
}

function checkBlogs(errors) {
for (const file of walkFiles(blogsDir)) {
const locale = path.basename(path.dirname(file));
const raw = fs.readFileSync(file, "utf8");
const meta = parseFrontmatter(raw);
if (!meta) {
errors.push(`[parse] ${path.relative(websiteRoot, file)}: missing title/description`);
continue;
}
const rel = path.relative(websiteRoot, file);
checkLength("title", locale, rel, meta.title, errors);
checkLength("description", locale, rel, meta.description, errors);
}
}

function checkDefaultMetadata(errors) {
const file = path.join(websiteRoot, "src/constants/default-metadata.ts");
const raw = fs.readFileSync(file, "utf8");
const title = raw.match(/default:\s*"([^"]+)"/)?.[1];
const desc = raw.match(/const description =\s*\n?\s*"([^"]+)"/)?.[1];
if (title) checkLength("title", "en-US", "default-metadata.title", title, errors);
if (desc)
checkLength("description", "en-US", "default-metadata.description", desc, errors);
}

const errors = [];
checkI18n(errors);
checkBlogs(errors);
checkDefaultMetadata(errors);

if (errors.length) {
console.error(`SEO meta check failed (${errors.length} issue(s)):\n`);
for (const e of errors) console.error(` - ${e}`);
process.exit(1);
}

console.log("SEO meta check passed.");
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ const formattedDate = formatDate(displayDate, locale, {
>{t("whitepaper.whitepaper")} / V{version}</span
>

<h1 class="font-whitepaper-cover-title text-whitepaper-primary">
<p class="font-whitepaper-cover-title text-whitepaper-primary">
Quantus
</h1>
</p>

<p class="font-whitepaper-cover-tagline text-whitepaper-cover-tagline">
{t("whitepaper.tag_line")}
Expand Down
2 changes: 1 addition & 1 deletion website/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const env = {
SITE_NAME: "Quantus Network",
SITE_NAME: "Quantus",
SITE_BASE_URL:
typeof process !== "undefined" && process.env?.SITE_BASE_URL
? process.env.SITE_BASE_URL
Expand Down
16 changes: 8 additions & 8 deletions website/src/constants/default-jsonld.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const organizationJsonLd: Organization = {
"@type": "Organization",
name: "Quantus Labs LLC",
description:
"Quantus Labs LLC is a technology company based in Wyoming, USA, focused on building infrastructure for decentralized, censorship-resistant technologies. Our flagship project, Quantus Network is a Layer 1 blockchain designed to be simple, open-source, and community-owned from day one.",
"Quantus Labs LLC is a technology company based in Wyoming, USA, focused on building infrastructure for decentralized, censorship-resistant technologies. Our flagship project, Quantus is a Layer 1 blockchain designed to be simple, open-source, and community-owned from day one.",
url: env.SITE_BASE_URL,
logo: {
"@type": "ImageObject",
Expand Down Expand Up @@ -81,7 +81,7 @@ export const iosAppJsonLd: MobileApplication = {
"@type": "MobileApplication",
name: "Quantus",
description:
"The post-quantum secure crypto wallet for the Quantus Network. Enjoy instant, reversible transactions and HD multi-account management.",
"The post-quantum secure crypto wallet for Quantus. Enjoy instant, reversible transactions and HD multi-account management.",
operatingSystem: "iOS",
applicationCategory: "FinanceApplication",
installUrl: APP_LINKS.ios,
Expand All @@ -93,7 +93,7 @@ export const androidAppJsonLd: MobileApplication = {
"@type": "MobileApplication",
name: "Quantus",
description:
"The post-quantum secure crypto wallet for the Quantus Network. Enjoy instant, reversible transactions and HD multi-account management.",
"The post-quantum secure crypto wallet for Quantus. Enjoy instant, reversible transactions and HD multi-account management.",
operatingSystem: "Android",
applicationCategory: "FinanceApplication",
installUrl: APP_LINKS.android,
Expand All @@ -103,17 +103,17 @@ export const androidAppJsonLd: MobileApplication = {
export const whitepaperJsonLd: TechArticle = {
"@id": `${env.SITE_BASE_URL}/whitepaper`,
"@type": "TechArticle",
headline: "Quantus Network Whitepaper",
headline: "Quantus Whitepaper",
description:
"The official whitepaper for Quantus Network, detailing the versioned history, protocol, and architecture of the network.",
"The official whitepaper for Quantus, detailing the versioned history, protocol, and architecture of the network.",
author: {
"@type": "Organization",
name: "Quantus Network",
name: "Quantus",
url: "https://github.com/Quantus-Network",
},
publisher: {
"@type": "Organization",
name: "Quantus Network",
name: "Quantus",
logo: {
"@type": "ImageObject",
url: "https://github.com/Quantus-Network.png",
Expand All @@ -123,7 +123,7 @@ export const whitepaperJsonLd: TechArticle = {
about: {
"@type": "Thing",
name: "Blockchain Protocol",
description: "Technical specification of the Quantus Network protocol.",
description: "Technical specification of the Quantus protocol.",
},
};

Expand Down
8 changes: 4 additions & 4 deletions website/src/constants/default-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import type { SEOProps } from "astro-seo";

const title = {
template: `%s | ${env.SITE_NAME}`,
default: "Quantus Network - Quantum-Secure Layer 1 Blockchain",
default: "Quantus - Quantum-Secure Encrypted Money",
};
const description =
"Quantus: Quantum-secure Layer 1 blockchain. Download our wallet for post-quantum protected assets.";
"Quantus is quantum-secure encrypted money on Layer 1. Download the wallet for post-quantum protected assets, private transfers, and reversible transactions.";
const image = {
url: `${env.SITE_BASE_URL}/quantus-banner.png`,
secureUrl: `${env.SITE_BASE_URL}/quantus-banner.png`,
Expand Down Expand Up @@ -41,8 +41,8 @@ const defaultMetadata: SEOProps = {
description,
image: image.url,
imageAlt: image.alt,
site: env.SITE_BASE_URL,
creator: "Quantus Labs",
site: "@QuantusNetwork",
creator: "@QuantusNetwork",
card: "summary_large_image",
},
};
Expand Down
7 changes: 4 additions & 3 deletions website/src/contents/blogs/de-DE/weekly-update-01-13-2026.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
---
title: "Quantus Weekly: Threshold Dilithium Signaturen und verteilte Koordination"
description: "Wöchentliches Update zu Threshold Signaturen, ZK-Circuit-Verbesserungen, Mobile-Wallet-Updates und Community-Inhalten."
title: "Quantus Weekly: Threshold Dilithium und Koordination"
description: Threshold Dilithium ermöglicht verteiltes Signieren mit einer On-Chain-Signatur. Updates zu ZK-Circuits, Mobile Wallet und Community-Inhalten.

pubDate: "2026-01-13"
heroImage: "/blog/covers/weekly-update-01-13-2026.webp"
heroAlt: "Quantus Weekly: Threshold Dilithium Signaturen und verteilte Koordination"
heroAlt: "Quantus Weekly: Threshold Dilithium und Koordination"
featured: false
tags:
[
Expand Down
7 changes: 4 additions & 3 deletions website/src/contents/blogs/de-DE/weekly-update-01-21-2026.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
---
title: "Quantus Weekly: Native Assets, ZK-Aggregation und Bitcoins Quantenrisiko"
description: "Wöchentliches Update über unsere Entscheidung für rein native Assets zur Quantensicherheit, Dilithium-Threshold-Signaturen und Fortschritte bei ZK-Aggregation und benutzerdefinierten Multisig-Pallets."
title: "Quantus Weekly: Native Assets und Bitcoins Quantenrisiko"
description: Quantus lehnt Wrapped Assets ab und setzt auf natives QUAN mit ML-DSA-87 und ZK-Aggregation. Updates zu Threshold-Signaturen, Multisig und dem Blog-Launch.

pubDate: "2026-01-21"
heroImage: "/blog/covers/weekly-update-01-21-2026.webp"
heroAlt: "Quantus Weekly: Native Assets, ZK-Aggregation und Bitcoins Quantenrisiko"
heroAlt: "Quantus Weekly: Native Assets und Bitcoins Quantenrisiko"
featured: false
tags:
[
Expand Down
3 changes: 2 additions & 1 deletion website/src/contents/blogs/de-DE/weekly-update-01-28-2026.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
title: "Quantus Weekly: Deniable RPCs und Neodyme-Sicherheitsaudit"
description: "Wöchentliches Update über unsere neuen Deniable RPCs für verbesserte Privatsphäre, den Abschluss des Neodyme-Sicherheitsaudits für Rusty Crystals und private ZK-Adressen für alle Token."
description: Neue Deniable RPCs für mehr Privatsphäre, abgeschlossenes Neodyme-Audit für Rusty Crystals und private ZK-Adressen für alle Token.

pubDate: "2026-01-28"
heroImage: "/blog/covers/weekly-update-01-28-2026.webp"
heroAlt: "Quantus Weekly: Deniable RPCs und Neodyme-Sicherheitsaudit"
Expand Down
Loading