hapara.fail is dedicated to exposing the pervasive nature of student surveillance technology used in educational environments. We provide technical insights, practical tools, and resources to empower students and advocate for digital privacy. This repository contains the source code for the v3 website, built with Astro and deployed to Cloudflare Workers.
Live Site: https://www.hapara.fail
This iteration of hapara.fail delivers a fast, modern, and privacy-respecting platform featuring:
- Educational Content: In-depth blog posts and technical write-ups explaining how edtech surveillance works.
- Privacy Tools: Interactive tools designed to bypass certain restrictions or reveal hidden information (e.g., DNS Service).
- Community Focus: Resources built by and for the student privacy community.
- Modern & Responsive Design: A sleek, consistent interface optimized for all devices.
- Edge Powered: Deployed to Cloudflare Workers via the
@astrojs/cloudflareadapter. Static assets served from the builtdist/directory. - Privacy First: No user tracking, no invasive analytics. We practice what we preach.
- MDX Blog: Blog posts are written in MDX and support custom Astro components (callouts, timelines, evidence blocks, etc.).
- Text-to-Speech: Built-in audio player on blog posts using the Web Speech API, implemented as a Svelte component.
- RSS Feed: Auto-generated RSS feed at
/rss.xmlvia@astrojs/rss. - Sitemap: Auto-generated sitemap via
@astrojs/sitemap. - Open Source: All code is available for review and contribution.
- Framework: Astro v6 β Static site generator with island architecture.
- UI Components: Svelte v5 β Used for interactive islands (search, cookie notice, text-to-speech).
- Content: MDX via
@astrojs/mdxβ Blog posts with embedded Astro components. - Deployment: Cloudflare Workers via
@astrojs/cloudflareadapter. - CLI: Wrangler 4 β Cloudflare's deployment tool.
- Package Manager: pnpm v9+
- Language: TypeScript
- Icons: Lucide via
@lucide/astroand@lucide/svelte. - Fonts: Poppins and Fira Code via
@fontsource(self-hosted, no external requests). - Code Formatter: Prettier
- SEO: Local metadata tags in
src/components/Head.astro
βββ 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 # Client-side blog search
β β β βββ CookieNotice.svelte # Cookie/notice banner
β β β βββ TextToSpeech.svelte # Text-to-speech audio player
β β βββ Footer.astro
β β βββ Head.astro # SEO meta tags, fonts, shared <head>
β β βββ 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 & 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 & HTTP method enforcement
βββ astro.config.mjs # Astro configuration
βββ package.json
βββ pnpm-workspace.yaml
βββ svelte.config.js
βββ tsconfig.json
βββ wrangler.jsonc # Cloudflare Workers configuration
| 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 |
| 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 |
Blog posts are sourced from src/content/blog/ as MDX files, rendered by the dynamic [...slug].astro route, and can embed any component from src/components/blog/.
Configured in astro.config.mjs:
| Route | Destination |
|---|---|
/bypass |
/services/dns |
/dns |
/services/dns |
/discord |
Discord Invite |
/github |
GitHub Profile |
- Node.js (v18 or higher recommended)
- pnpm v9+
- A Cloudflare account with Wrangler authenticated (
wrangler login) β only needed for deployment
-
Clone the repository:
git clone https://github.com/hapara-fail/website.git cd website -
Install dependencies:
pnpm install
-
Start the local development server:
pnpm run dev
This starts the Astro dev server at
http://localhost:4321with hot module reloading.
| Script | Description |
|---|---|
pnpm run dev |
Start the Astro development server |
pnpm run build |
Generate version stamp + build the site to dist/ |
pnpm run preview |
Build + run Wrangler dev server against the built dist/ |
pnpm run deploy |
Build + 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) |
Ensure you have Wrangler installed and authenticated (wrangler login).
pnpm run deployThis command generates the version stamp, runs astro build to produce the dist/ directory, then deploys via wrangler deploy to the hapara-fail Worker defined in wrangler.jsonc. Assets are served from the dist/ directory via the Workers Assets binding.
Security headers are applied to every response via Astro middleware (src/middleware.ts):
Content-Security-Policyβ restricts asset origins; allows connections tomonitor.dns2.hapara.failfor DNS status checks, andcdn.jsdelivr.netwithraw.githubusercontent.comfallback for blocklist content.Strict-Transport-Securityβ HSTS withincludeSubDomains; preload(1 year).X-Content-Type-Options: nosniffX-Frame-Options: SAMEORIGINReferrer-Policy: strict-origin-when-cross-originPermissions-Policyβ disables camera, microphone, geolocation, and payment APIs.
Only GET and HEAD HTTP methods are accepted; all others return 405 Method Not Allowed.
Contributions are welcome! Please review our Contributing Guidelines before submitting.
If you have ideas for improvements, new tools, bug fixes, or blog post topics:
- Open an Issue on GitHub using our standardized templates.
- Submit a Pull Request with your proposed changes.
- Join our Discord server to discuss.
Donation options are available here.
This project is licensed under the terms specified at www.hapara.fail/license.