> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hapara.fail/llms.txt
> Use this file to discover all available pages before exploring further.

# Website Architecture

> Technical overview of the hapara.fail v3 platform

## Overview

The v3 website is a full Astro application. The project uses Astro v6 for routing, layouts, content, and builds; Svelte 5 islands for interactive UI; MDX content collections for blog posts; and the `@astrojs/cloudflare` adapter for Cloudflare Workers deployment.

Pages live under `src/pages/`, shared UI lives under `src/layouts/` and `src/components/`, blog content lives under `src/content/`, and security headers are applied through Astro middleware in `src/middleware.ts`.

## Tech Stack

<CardGroup cols={2}>
  <Card title="Framework" icon="sparkles">
    Astro v6 with file-based routing and island architecture.
  </Card>

  <Card title="Interactive UI" icon="bolt">
    Svelte v5 islands for search, cookie notice, and text-to-speech.
  </Card>

  <Card title="Content" icon="file-code">
    MDX via `@astrojs/mdx`, backed by Astro content collections.
  </Card>

  <Card title="Deployment" icon="cloudflare">
    Cloudflare Workers via `@astrojs/cloudflare` and Wrangler 4.
  </Card>

  <Card title="Assets" icon="images">
    Workers Assets serving the generated `dist/` directory.
  </Card>

  <Card title="Feeds" icon="rss">
    RSS at `/rss.xml` via `@astrojs/rss`; sitemap via `@astrojs/sitemap`.
  </Card>

  <Card title="Icons" icon="icons">
    Lucide icons through `@lucide/astro` and `@lucide/svelte`.
  </Card>

  <Card title="Fonts" icon="font">
    Poppins and Fira Code from `@fontsource`, self-hosted with no external font requests.
  </Card>
</CardGroup>

## Project Structure

```text theme={null}
public/                         # Static files copied as-is to dist/
scripts/
  generate-version.js           # Build-time script to stamp version info
src/
  assets/                       # Images imported and processed by Astro
  components/
    blog/                       # MDX-embeddable Astro components
      BreachTable.astro
      CalloutWarning.astro
      EvidenceBlock.astro
      PullQuote.astro
      SectionLabel.astro
      SourceChip.astro
      Timeline.astro
      TimelineItem.astro
    svelte/                     # Interactive Svelte island components
      BlogSearch.svelte
      CookieNotice.svelte
      TextToSpeech.svelte
    Footer.astro
    Head.astro                  # SEO meta tags, fonts, shared head content
    Header.astro
    Scripts.astro               # Shared script includes
  content/
    blog/                       # MDX blog post source files
      adguard-home.mdx
      age-verification.mdx
      chromeos-wifi-password-extractor.mdx
      death-of-learning.mdx
      dns.mdx
  layouts/
    BaseLayout.astro            # Shared page layout wrapper
  pages/
    blog/
      index.astro               # Blog index page
      [...slug].astro           # Dynamic blog post renderer
    services/
      dns.astro                 # DNS service page and tool
    404.astro
    about.astro
    contribute.astro
    index.astro                 # Homepage
    license.astro
    privacy.astro
    rss.xml.ts                  # RSS feed endpoint
    terms.astro
  content.config.ts             # Astro content collection schema
  middleware.ts                 # Security headers and HTTP method enforcement
astro.config.mjs                # Astro configuration
package.json
pnpm-workspace.yaml
svelte.config.js
tsconfig.json
wrangler.jsonc                  # Cloudflare Workers configuration
```

## Routing

Astro owns routing through `src/pages/`. The Cloudflare Worker produced by the adapter serves the built site from `dist/` through Workers Assets.

### Primary Pages

| Route           | Description               |
| :-------------- | :------------------------ |
| `/`             | Homepage                  |
| `/about`        | About page                |
| `/contribute`   | Contribute / donate page  |
| `/terms`        | Terms of Service          |
| `/privacy`      | Privacy Policy            |
| `/license`      | License information       |
| `/blog`         | Blog index                |
| `/blog/[slug]`  | Individual blog posts     |
| `/services/dns` | DNS service page & tool   |
| `/rss.xml`      | RSS feed                  |
| `/llms.txt`     | LLM-readable site summary |

### Blog Posts

Blog posts are authored as MDX files in `src/content/blog/`, validated through the content collection schema in `src/content.config.ts`, and rendered by `src/pages/blog/[...slug].astro`.

| Route                                    | Title                             |
| :--------------------------------------- | :-------------------------------- |
| `/blog/dns`                              | DNS                               |
| `/blog/adguard-home`                     | AdGuard Home                      |
| `/blog/age-verification`                 | Age Verification                  |
| `/blog/chromeos-wifi-password-extractor` | ChromeOS Wi-Fi Password Extractor |
| `/blog/death-of-learning`                | Death of Learning                 |

### Shortcuts & Redirects

Redirects are configured in `astro.config.mjs`.

| Route      | Destination     |
| :--------- | :-------------- |
| `/bypass`  | `/services/dns` |
| `/dns`     | `/services/dns` |
| `/discord` | Discord Invite  |
| `/github`  | GitHub Profile  |

## Content Components

The MDX blog can embed reusable Astro components from `src/components/blog/`:

| Component        | Purpose                                     |
| :--------------- | :------------------------------------------ |
| `BreachTable`    | Structured breach or evidence tables        |
| `CalloutWarning` | Prominent warnings inside long-form posts   |
| `EvidenceBlock`  | Source-backed evidence excerpts and context |
| `PullQuote`      | Editorial pull quotes                       |
| `SectionLabel`   | Small section markers                       |
| `SourceChip`     | Compact source attribution links            |
| `Timeline`       | Timeline wrapper for chronological sections |
| `TimelineItem`   | Individual timeline entries                 |

## Interactive Islands

Astro renders the site statically by default and hydrates only the components that need client-side behavior:

* `BlogSearch.svelte` powers client-side blog search on the blog index.
* `TextToSpeech.svelte` adds a Web Speech API audio player to blog posts, including a seekable progress bar, voice selection modal, and floating player UI.
* `CookieNotice.svelte` displays the site notice as a Svelte island.

## Generated Files

* `/rss.xml` is generated by `@astrojs/rss`.
* The sitemap is generated by `@astrojs/sitemap`.
* `/llms.txt` provides an LLM-readable site description.
* Fira Code is self-hosted for code blocks through `@fontsource`.

## Security

Security is enforced in `src/middleware.ts`. The middleware applies strict response headers to every response:

| Header                      | Value / Purpose                                                                                                     |
| :-------------------------- | :------------------------------------------------------------------------------------------------------------------ |
| `Content-Security-Policy`   | Restricts asset origins; allows DNS status checks against `monitor.dns2.hapara.fail` and blocklist fetch fallbacks. |
| `Strict-Transport-Security` | HSTS with `includeSubDomains; preload` for one year.                                                                |
| `X-Content-Type-Options`    | `nosniff`                                                                                                           |
| `X-Frame-Options`           | `SAMEORIGIN`                                                                                                        |
| `Referrer-Policy`           | `strict-origin-when-cross-origin`                                                                                   |
| `Permissions-Policy`        | Disables camera, microphone, geolocation, and payment APIs.                                                         |

Only `GET` and `HEAD` HTTP methods are accepted; all others return `405 Method Not Allowed`.

## Development

### Prerequisites

* Node.js v18 or higher
* [pnpm](https://pnpm.io/) v9 or higher
* A Cloudflare account with Wrangler authenticated (`wrangler login`) for deployment

### Local Setup

<Steps>
  <Step title="Clone">
    Clone the repository to your local machine:

    ```bash theme={null}
    git clone https://github.com/hapara-fail/website.git
    cd website
    ```
  </Step>

  <Step title="Install">
    Install dependencies:

    ```bash theme={null}
    pnpm install
    ```
  </Step>

  <Step title="Run">
    Start the Astro development server:

    ```bash theme={null}
    pnpm run dev
    ```

    This starts the local Astro dev server at `http://localhost:4321` with hot module reloading.
  </Step>
</Steps>

### Available Scripts

| Script                      | Description                                               |
| :-------------------------- | :-------------------------------------------------------- |
| `pnpm run dev`              | Start the Astro development server.                       |
| `pnpm run build`            | Generate the version stamp and build the site to `dist/`. |
| `pnpm run preview`          | Build and run Wrangler dev against the built `dist/`.     |
| `pnpm run deploy`           | Build and deploy to Cloudflare Workers.                   |
| `pnpm run format` / `fmt`   | Format all files with Prettier.                           |
| `pnpm run generate-version` | Stamp version data only; called automatically by build.   |

### Deployment

Ensure Wrangler is installed and authenticated (`wrangler login`), then run:

```bash theme={null}
pnpm run deploy
```

This generates the version stamp, runs `astro build` to produce `dist/`, then deploys via `wrangler deploy` to the `hapara-fail` Worker defined in `wrangler.jsonc`. Assets are served from `dist/` through the Workers Assets binding.
