Skip to content
Open
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
71 changes: 71 additions & 0 deletions src/app/(home)/about/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { InfoPage, InfoSection } from "@/components/info/info-page";
import { SITE_URL } from "@/lib/structured-data";

export const metadata = {
title: "About",
description:
"About the Avail documentation site — what Avail DA and Avail Nexus are, who maintains these docs, and how the content is organised.",
alternates: { canonical: `${SITE_URL}/about` },
};

export default function AboutPage() {
return (
<InfoPage
title="About these docs"
lede="docs.availproject.org is the official documentation for Avail, published and maintained by the Avail Project."
>
<InfoSection heading="What Avail is">
<p>
Avail is a modular blockchain stack. It is split into two products,
and this site documents both of them.
</p>
<p>
<strong>Avail DA</strong> is a data availability layer. Rollups and
other chains post their transaction data to Avail DA and can prove
that the data was published, without every participant having to
download all of it. The docs cover submitting and reading data,
running full nodes and validators, light clients, network endpoints
and chain specs, and the node API across TypeScript, Rust and Go.
</p>
<p>
<strong>Avail Nexus</strong> is a cross-chain unification layer. It
gives applications a single balance and a single transaction flow
across many chains, through an SDK or through drop-in UI widgets. The
docs cover the SDK reference, bridging, swaps, supported chains and
tokens, and the widget components.
</p>
</InfoSection>

<InfoSection heading="How this site is organised">
<p>
Content is split by product: everything under <code>/docs/da</code> is
Avail DA, and everything under <code>/docs/nexus</code> is Avail
Nexus. Each product has its own Get Started path, conceptual material,
and API reference.
</p>
</InfoSection>

<InfoSection heading="For AI agents and scripts">
<p>
These docs are published in machine-readable form as well as HTML.{" "}
<a href="/llms.txt">/llms.txt</a> is an intent-based index of the
whole site, with per-product dumps at{" "}
<a href="/llms-da.txt">/llms-da.txt</a> and{" "}
<a href="/llms-nexus.txt">/llms-nexus.txt</a>. Any documentation URL
returns clean markdown if you append <code>.md</code> to it or send an{" "}
<code>Accept: text/markdown</code> header, and structured reference
data is available at{" "}
<a href="/api/reference.json">/api/reference.json</a>.
</p>
</InfoSection>

<InfoSection heading="Corrections and contributions">
<p>
Every page has a feedback control, and the docs are open source. If
something here is wrong or missing, please tell us — see{" "}
<a href="/contact">Contact</a> for the ways to reach the team.
</p>
</InfoSection>
</InfoPage>
);
}
71 changes: 71 additions & 0 deletions src/app/(home)/contact/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { InfoPage, InfoSection } from "@/components/info/info-page";
import { SITE_URL } from "@/lib/structured-data";

export const metadata = {
title: "Contact",
description:
"How to reach the Avail team — developer support on Discord, documentation issues on GitHub, security disclosures, and business enquiries.",
alternates: { canonical: `${SITE_URL}/contact` },
};

export default function ContactPage() {
return (
<InfoPage
title="Contact"
lede="How to reach the Avail team, depending on what you need."
>
<InfoSection heading="Developer support">
<p>
For build questions, integration help, and anything about running
nodes, the fastest route is the community. Ask in the{" "}
<a href="https://discord.com/invite/AvailProject">Avail Discord</a>,
where the engineering team is active, or in the{" "}
<a href="https://t.me/AvailCommunity">Avail Telegram community</a>.
</p>
</InfoSection>

<InfoSection heading="Documentation issues">
<p>
If a page is wrong, out of date, or missing, use the feedback control
at the bottom of that page — it goes straight to the docs backlog. You
can also open an issue in the{" "}
<a href="https://github.com/availproject">
Avail GitHub organisation
</a>{" "}
against the relevant repository.
</p>
</InfoSection>

<InfoSection heading="Security disclosures">
<p>
Please do not report vulnerabilities in public channels. Email{" "}
<a href="mailto:security@availproject.org">
security@availproject.org
</a>
. Scope, rewards, and the disclosure process are described in the{" "}
<a href="/docs/da/bug-bounty">bug bounty documentation</a>.
</p>
</InfoSection>

<InfoSection heading="Business and partnerships">
<p>
For partnership, integration, and commercial enquiries — including
access to Turbo DA — email{" "}
<a href="mailto:business@availproject.org">
business@availproject.org
</a>
.
</p>
</InfoSection>

<InfoSection heading="Elsewhere">
<p>
Announcements and updates are posted on{" "}
<a href="https://x.com/AvailProject">X</a>. General information about
the project, including the team and open roles, lives on the{" "}
<a href="https://www.availproject.org">main Avail website</a>.
</p>
</InfoSection>
</InfoPage>
);
}
11 changes: 11 additions & 0 deletions src/app/(home)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,21 @@ import { HeroSection } from "@/components/home/hero-section";
import { HomeControls } from "@/components/home/home-controls";
import { ProductGrid } from "@/components/home/product-grid";
import { SiteFooter } from "@/components/home/site-footer";
import { SITE_URL, websiteLd } from "@/lib/structured-data";

export const metadata = {
alternates: { canonical: SITE_URL },
};

export default function HomePage() {
return (
<main id="main-content" className="relative">
{/* Site identity for agents — not rendered */}
<script
type="application/ld+json"
// biome-ignore lint/security/noDangerouslySetInnerHtml: JSON-LD payload is built from static, non-user data
dangerouslySetInnerHTML={{ __html: JSON.stringify(websiteLd) }}
/>
{/* Frame padding — thin muted border visible on top + sides */}
<div className="md:px-3 md:pt-3">
{/* Content panel — white bg, rounded corners */}
Expand Down
99 changes: 99 additions & 0 deletions src/app/(home)/privacy/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { InfoPage, InfoSection } from "@/components/info/info-page";
import { SITE_URL } from "@/lib/structured-data";

export const metadata = {
title: "Privacy",
description:
"What the Avail documentation site collects — analytics, stored preferences, optional wallet connections, and page feedback — and how to opt out.",
alternates: { canonical: `${SITE_URL}/privacy` },
};

export default function PrivacyPage() {
return (
<InfoPage
title="Privacy"
lede="This page describes what docs.availproject.org collects when you read it. It covers this documentation site only — not the Avail network itself, which is a public blockchain, and not other Avail properties."
>
<InfoSection heading="Analytics">
<p>
We use PostHog to understand which pages are useful and where people
get stuck. It records page views, how far down a page you scroll, how
long you stay, and clicks on links, buttons and forms. Analytics
requests are proxied through this domain rather than sent to a
third-party domain directly.
</p>
<p>
Where session recordings are captured, all text inputs are masked
before anything leaves your browser, so what you type is not recorded.
Analytics state is kept in your browser&rsquo;s local storage and a
cookie.
</p>
<p>
If your browser sends a <code>Do Not Track</code> signal, analytics
are disabled automatically. Blocking the analytics script does not
affect any documentation content.
</p>
</InfoSection>

<InfoSection heading="Preferences stored in your browser">
<p>
Two settings are saved locally so the site behaves consistently
between visits: the network you have selected (mainnet or testnet) and
your light or dark theme choice. These stay in your browser and are
not sent to us. Clearing site data removes them.
</p>
</InfoSection>

<InfoSection heading="Connecting a wallet">
<p>
Some Avail Nexus pages include live demo widgets that you can connect
a wallet to. Connecting is entirely optional, and no documentation
requires it. When you connect, the site reads your public address and
the balances needed to render the demo. We never receive your private
keys or seed phrase, and every transaction is signed in your own
wallet.
</p>
</InfoSection>

<InfoSection heading="Page feedback">
<p>
Each page has a feedback control. When you submit it, we receive your
rating, the path of the page you were on, and anything you chose to
add — a comment, contact details, or a screenshot.
</p>
<p>
Please note that feedback is filed as an issue in a{" "}
<a href="https://github.com/availproject/docs">
public GitHub repository
</a>
. Anything you include, including contact details and screenshots,
will be publicly visible. Leave out anything you would not want
published, and use the channels on the <a href="/contact">Contact</a>{" "}
page for private matters.
</p>
</InfoSection>

<InfoSection heading="Hosting">
<p>
The site is hosted on Vercel, which processes standard request data
such as IP address and user agent in order to serve pages and protect
the service. Uploaded feedback screenshots are stored in Vercel Blob
storage.
</p>
</InfoSection>

<InfoSection heading="Questions">
<p>
For questions about this page, or to ask us to remove something you
submitted, email{" "}
<a href="mailto:info@availproject.org">info@availproject.org</a>.
Security issues should go to{" "}
<a href="mailto:security@availproject.org">
security@availproject.org
</a>{" "}
instead — see <a href="/contact">Contact</a>.
</p>
</InfoSection>
</InfoPage>
);
}
9 changes: 5 additions & 4 deletions src/app/api/markdown/[...slug]/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { type NextRequest, NextResponse } from "next/server";
import { AGENT_HEADERS } from "@/lib/agent-headers";
import { agentNotFoundResponse } from "@/lib/agent-not-found";
import { trackAgentRequest } from "@/lib/analytics/agent-tracking";
import { getPageMarkdownOverride } from "@/lib/llms";
import { cleanMarkdownForAgents } from "@/lib/markdown-clean";
Expand All @@ -11,10 +12,10 @@ function getPageFromSlug(slug: string[]) {
return directMatch;
}

// DA content is canonicalized under /docs/DA/* while many links and
// users request /docs/da/*. Support both casings for API consumers.
// DA content is canonicalized under /docs/da/* but older links and some
// agents request /docs/DA/*. Support both casings for API consumers.
if (slug.length > 0 && slug[0].toLowerCase() === "da") {
return source.getPage(["DA", ...slug.slice(1)]);
return source.getPage(["da", ...slug.slice(1)]);
}

return null;
Expand All @@ -28,7 +29,7 @@ export async function GET(
const page = getPageFromSlug(slug);

if (!page) {
return NextResponse.json({ error: "Page not found" }, { status: 404 });
return agentNotFoundResponse(`/docs/${slug.join("/")}`);
}

try {
Expand Down
11 changes: 7 additions & 4 deletions src/app/api/markdown/markdown-api.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { NextRequest } from "next/server";
import { describe, expect, it, vi } from "vitest";
import { MARKDOWN_VARY } from "@/lib/agent-headers";

const FAKE_CONTENT = "# Test Page\n\nSome markdown content here for testing.";
// cleanMarkdownForAgents adds a trailing newline to plain markdown
Expand All @@ -8,10 +9,10 @@ const CLEANED_CONTENT = `${FAKE_CONTENT}\n`;
vi.mock("@/lib/source", () => ({
source: {
getPage: (slug: string[]) => {
if (slug.length === 0 || (slug[0] === "DA" && slug[1] === "build")) {
if (slug.length === 0 || (slug[0] === "da" && slug[1] === "build")) {
return {
url:
slug.length === 0 ? "/docs" : `/docs/DA/${slug.slice(1).join("/")}`,
slug.length === 0 ? "/docs" : `/docs/da/${slug.slice(1).join("/")}`,
data: {
title: "Test Page",
description: "A test page",
Expand Down Expand Up @@ -62,9 +63,11 @@ describe("markdown API - root route", () => {
);
});

it("has Vary: accept header", async () => {
it("has a merged Vary header including Accept", async () => {
const res = await rootGET(makeRequest("/api/markdown"));
expect(res.headers.get("Vary")).toBe("accept");
const vary = res.headers.get("Vary") ?? "";
expect(vary).toBe(MARKDOWN_VARY);
expect(vary).toContain("Accept");
});

it("returns JSON when format=json", async () => {
Expand Down
3 changes: 2 additions & 1 deletion src/app/api/markdown/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { type NextRequest, NextResponse } from "next/server";
import { AGENT_HEADERS } from "@/lib/agent-headers";
import { agentNotFoundResponse } from "@/lib/agent-not-found";
import { trackAgentRequest } from "@/lib/analytics/agent-tracking";
import { cleanMarkdownForAgents } from "@/lib/markdown-clean";
import { source } from "@/lib/source";
Expand All @@ -9,7 +10,7 @@ export async function GET(request: NextRequest) {
const page = source.getPage([]);

if (!page) {
return NextResponse.json({ error: "Page not found" }, { status: 404 });
return agentNotFoundResponse("/docs");
}

try {
Expand Down
11 changes: 8 additions & 3 deletions src/app/docs/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { PageFooter } from "@/components/mdx/page-footer";
import { Badge } from "@/components/ui/badge";
import { getProductTree } from "@/lib/page-tree-utils";
import { source } from "@/lib/source";
import { organizationLd } from "@/lib/structured-data";

export const revalidate = false;
export const dynamic = "force-static";
Expand Down Expand Up @@ -159,11 +160,15 @@ export default async function Page(props: {
headline: doc.title,
description: doc.description || "",
url: pageUrl,
publisher: {
"@type": "Organization",
name: "Avail",
mainEntityOfPage: pageUrl,
inLanguage: "en",
isPartOf: {
"@type": "WebSite",
"@id": `${baseUrl}/#website`,
name: "Avail Documentation",
url: baseUrl,
},
publisher: organizationLd,
};

return (
Expand Down
Loading