diff --git a/app/layout.tsx b/app/layout.tsx
index d20216a..9297caf 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -4,6 +4,7 @@ import { links, SITE_URL, INDEXABLE } from "@/lib/links";
import { Chrome } from "@/components/site/Chrome";
import { Footer } from "@/components/site/Footer";
import { ScrollReveal } from "@/components/site/ScrollReveal";
+import { FAQ_ITEMS } from "@/lib/faq";
import "./globals.css";
const geistSans = Geist({
@@ -47,6 +48,8 @@ export const viewport: Viewport = {
const ORG_ID = `${SITE_URL}#organization`;
const SITE_ID = `${SITE_URL}#website`;
+const SERVICE_ID = `${SITE_URL}#service`;
+const FAQ_ID = `${SITE_URL}#faq`;
const organizationSchema = {
"@context": "https://schema.org",
@@ -54,8 +57,9 @@ const organizationSchema = {
"@id": ORG_ID,
name: "deCDN",
url: SITE_URL,
- logo: `${SITE_URL}icon.svg`,
- description: DESCRIPTION,
+ logo: `${SITE_URL}d_logo.png`,
+ description:
+ "Organization developing deCDN, a peer-to-peer content delivery network with per-megabyte settlement in USDC.",
sameAs: [links.github, links.x],
};
@@ -69,6 +73,29 @@ const websiteSchema = {
publisher: { "@id": ORG_ID },
};
+const serviceSchema = {
+ "@context": "https://schema.org",
+ "@type": "Service",
+ "@id": SERVICE_ID,
+ name: "deCDN",
+ url: SITE_URL,
+ serviceType: "Content Delivery Network",
+ provider: { "@id": ORG_ID },
+ areaServed: "Worldwide",
+ description: DESCRIPTION,
+};
+
+const faqSchema = {
+ "@context": "https://schema.org",
+ "@type": "FAQPage",
+ "@id": FAQ_ID,
+ mainEntity: FAQ_ITEMS.map(({ q, a }) => ({
+ "@type": "Question",
+ name: q,
+ acceptedAnswer: { "@type": "Answer", text: a },
+ })),
+};
+
// Inline JSON in ` in any field
// can't close the tag and create an XSS sink. < decodes back to `<` in
// every JSON parser, so structured-data consumers are unaffected.
@@ -86,13 +113,15 @@ export default function RootLayout({
className={`${geistSans.variable} h-full motion-safe:scroll-smooth`}
>
- {[organizationSchema, websiteSchema].map((schema) => (
-
- ))}
+ {[organizationSchema, websiteSchema, serviceSchema, faqSchema].map(
+ (schema) => (
+
+ ),
+ )}
diff --git a/components/site/Faq.tsx b/components/site/Faq.tsx
index e2e95a7..d603909 100644
--- a/components/site/Faq.tsx
+++ b/components/site/Faq.tsx
@@ -1,6 +1,7 @@
import { FaqItem } from "@/components/ui/FaqItem";
import { Frame } from "@/components/ui/Frame";
import { SectionHeader } from "@/components/ui/SectionHeader";
+import { FAQ_ITEMS } from "@/lib/faq";
export function Faq() {
return (
@@ -18,36 +19,9 @@ export function Faq() {
-
-
-
-
-
-
+ {FAQ_ITEMS.map((item, i) => (
+
+ ))}
diff --git a/lib/faq.ts b/lib/faq.ts
new file mode 100644
index 0000000..7800776
--- /dev/null
+++ b/lib/faq.ts
@@ -0,0 +1,26 @@
+export const FAQ_ITEMS = [
+ {
+ q: "How is it 90% cheaper?",
+ a: "Legacy CDNs provision for peak traffic and amortise that cost across long contracts, so a popular release gets expensive fast. deCDN is demand-shaped: supply forms around traffic, nodes cache what's hot, and bandwidth gets reused across many pulls. Cost per gigabyte collapses as regional demand concentrates — the opposite of how fixed-provisioning CDNs behave. Target price today: ~$0.01/GB, one cent, per actual gigabyte delivered.",
+ },
+ {
+ q: "How is byte-level integrity enforced?",
+ a: "Every blob is BLAKE3-addressed, so the hash the client is asking for is the same hash they'll be checking on the way in. Verification happens chunk-by-chunk, in flight — mismatched bytes are dropped and the next peer on the list is asked instead. Peers that send garbage lose staked TOKEN via a non-custodial Merkle proof. There is no central arbiter; the math is the arbiter.",
+ },
+ {
+ q: "Can content be gated or taken down?",
+ a: "Both. For gating: upload ciphertext instead of plaintext — nodes cache the encrypted blob without ever seeing inside, and your app holds the keys, handing them to clients over a separate authenticated channel. Subscription, paywall, whatever logic fits. For takedown: governance can flag specific BLAKE3 hashes as non-servable, and nodes that keep serving them past a short compliance window lose staked TOKEN — slashing is on-chain, not a human call. Both the key gate and the flag list are auditable; compliance runs end-to-end, from origin onboarding to peer-level slashing, governance-owned and auditable throughout.",
+ },
+ {
+ q: "Why does the network need a token?",
+ a: "TOKEN secures the network — operators stake it to participate, and they lose it if they misbehave. Payments, though, happen in USDC, because operators want stable currency they can pay power bills with. Twenty percent of protocol fees flow into a Balancer V3 80/20 TOKEN/USDC buyback, tying token demand to network throughput.",
+ },
+ {
+ q: "Can publishers pay on behalf of users?",
+ a: "Yes. Account abstraction lets a publisher fund a payment channel for their audience, so users pull content with no wallet and no subscription — same free-to-download experience as a public mirror, except the publisher pays peers per megabyte instead of one cloud's egress bill. Default flow is client-pays; publisher-pays is the flag you flip when you want to ship widely without per-user payment friction.",
+ },
+ {
+ q: "Does this work well for AI models?",
+ a: "Yes — AI is where deCDN's primitives compound. Clients pull large weights from several peers in parallel and saturate their own uplink instead of a single origin's. BLAKE3 chunk addressing lets teams grab one quantisation variant or a single layer without downloading the full checkpoint. And when a model drops and every region pulls it at once, the mesh warms around the demand instead of choking at a central bucket.",
+ },
+] as const;