UNPKG

@appwarden/middleware

Version:

Instantly disable all user interaction with your app deployed on Cloudflare or Vercel

353 lines (258 loc) 14.9 kB
# @appwarden/middleware [![Docs](https://img.shields.io/badge/Documentation-blue)](https://appwarden.io/docs/reference/appwarden-middleware) [![GitHub](https://img.shields.io/badge/GitHub-appwarden%2Fmiddleware-181717?logo=github&logoColor=white)](https://github.com/appwarden/middleware) [![npm version](https://img.shields.io/npm/v/@appwarden/middleware.svg)](https://www.npmjs.com/package/@appwarden/middleware) [![npm provenance](https://img.shields.io/badge/npm-provenance-green)](https://docs.npmjs.com/generating-provenance-statements) ![Test Coverage](https://img.shields.io/badge/coverage-94.6%25-brightgreen) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) ## Core Features - **Discord Integration**: Quarantine your website via Discord commands (`/quarantine [un]lock`) - **Instant Quarantine**: Immediately redirects all visitors to a lock page when activated to stop in progress attacks. - **Nonce-based Content Security Policy (See [Feature Compatibility](#feature-compatibility))**: Deploy a nonce-based Content Security Policy (CSP) using HTML rewriting on Cloudflare where supported. - **Minimal Runtime Overhead**: Negligible performance impact by using `event.waitUntil` for status checks ### Feature Compatibility The table below summarizes which Appwarden features are available on each platform, including quarantine enforcement and Content Security Policy (CSP) support (with or without nonces). | Platform / Adapter | Package / entrypoint | Quarantine | CSP | CSP Nonce | | --------------------------------------- | ------------------------------------------------- | ---------- | --- | --------- | | Cloudflare – Universal middleware | `@appwarden/middleware/cloudflare` | ✅ | ✅ | ✅ | | Cloudflare – Astro | `@appwarden/middleware/cloudflare/astro` | ✅ | ✅ | ✅ | | Cloudflare – React Router | `@appwarden/middleware/cloudflare/react-router` | ✅ | ✅ | ✅ | | Cloudflare – TanStack Start | `@appwarden/middleware/cloudflare/tanstack-start` | ✅ | ✅ | ✅ | | Cloudflare – Next.js (OpenNext adapter) | `@appwarden/middleware/cloudflare/nextjs` | ✅ | ✅ | ❌ | | Vercel - Universal middleware | `@appwarden/middleware/vercel` | ✅ | ✅ | ❌ | Nonce-based CSP requires HTML rewriting and is only available on Cloudflare. Next.js on Cloudflare (OpenNext) and Vercel Edge Middleware apply CSP headers only and do **not** support nonces. If you are using Next.js on Cloudflare, please use the Cloudflare Universal middleware for CSP nonce support. ## Configuration The following options are shared across the Cloudflare and Vercel middleware bundles. ### `lockPageSlug` The path or route (for example, `/maintenance`) to redirect users to when the domain is quarantined. This should be a working page on your site, such as a maintenance or status page, that explains why the website is temporarily unavailable. ### `contentSecurityPolicy` (optional) Controls the [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) headers that Appwarden adds. This configuration is optional—if not provided, no CSP header will be applied. When provided, both `mode` and `directives` are required: - `mode` controls how the CSP is applied: - `"disabled"` – no CSP header is sent. - `"report-only"` – sends the `Content-Security-Policy-Report-Only` header so violations are reported (for example in the browser console) but not blocked. - `"enforced"` – sends the `Content-Security-Policy` header so violations are actively blocked. When developing or iterating on your CSP, we recommend starting with `"report-only"` so you can identify and fix violations before switching to `"enforced"`. - `directives` is an object whose keys are CSP directive names and whose values are arrays of allowed sources. For example: ```ts contentSecurityPolicy: { mode: "enforced", directives: { "script-src": ["'self'", "{{nonce}}"], "style-src": ["'self'", "{{nonce}}"], }, } ``` To add a nonce to a directive (See [Feature Compatibility](#feature-compatibility)), include the `"{{nonce}}"` placeholder in the list of sources. ### `appwardenApiToken` The Appwarden API token used to authenticate requests to the Appwarden API. See the [API token management guide](https://appwarden.io/docs/guides/api-token-management) for details on creating and managing your token. Treat this token as a secret (similar to a password): do not commit it to source control and store it in environment variables or secret management where possible. Appwarden stores API tokens using AES-GCM encryption and does not display them after creation. ### `cacheUrl` (Vercel only) The URL or connection string of the cache provider (for example, Upstash or Vercel Edge Config) that stores the quarantine status for your domain. See the [Vercel integration guide](https://appwarden.io/docs/guides/vercel-middleware-integration#3-configure-a-cache-provider) for cache provider configuration details. ### `vercelApiToken` (Vercel only) A Vercel API token that Appwarden uses to manage the Vercel Edge Config cache provider that synchronizes the quarantine status of your domain. See the [Vercel integration guide](https://appwarden.io/docs/guides/vercel-middleware-integration#3-configure-a-cache-provider) for cache provider configuration details. Appwarden never stores or logs your Vercel API token; it is used only to manage the quarantine status cache for your domain. ## Installation Compatible with websites powered by [Cloudflare](https://developers.cloudflare.com/workers/static-assets/) or [Vercel](https://vercel.com). For more background and advanced configuration, see the [Appwarden documentation](https://appwarden.io/docs). ### 1. Cloudflare #### 1.1 Universal Middleware (direct Cloudflare Worker usage) The **Universal Middleware** (`@appwarden/middleware/cloudflare`) supports any website deployed on Cloudflare. It deploys as a standalone Cloudflare Worker and incurs additional usage-based charges. The easiest way to deploy this universal middleware is via our [build-cloudflare-action](https://github.com/appwarden/build-cloudflare-action); see the [Cloudflare integration guide](https://appwarden.io/docs/guides/cloudflare-middleware-integration#1-set-up-the-github-actions-workflow) for workflow details. If you prefer to manage your own Cloudflare Worker instead of using the GitHub Action, you can mount the universal Cloudflare middleware directly using the `@appwarden/middleware/cloudflare` bundle: ```ts // src/worker.ts import { createAppwardenMiddleware } from "@appwarden/middleware/cloudflare" const appwardenHandler = createAppwardenMiddleware((cloudflare) => ({ debug: cloudflare.env.DEBUG, lockPageSlug: cloudflare.env.APPWARDEN_LOCK_PAGE_SLUG, appwardenApiToken: cloudflare.env.APPWARDEN_API_TOKEN, contentSecurityPolicy: { mode: cloudflare.env.CSP_MODE, directives: cloudflare.env.CSP_DIRECTIVES, }, })) export default { fetch(request: Request, env: CloudflareEnv, ctx: ExecutionContext) { return appwardenHandler(request, env, ctx) }, } ``` See the Cloudflare integration docs on [appwarden.io](https://appwarden.io/docs) for environment variable setup and deployment details. #### 1.2 Cloudflare framework adapters If you are using a supported framework, you can mount Appwarden inside your application using framework-specific adapters. This approach incurs no additional cost. Framework adapters can also sync Appwarden-managed lock page and CSP settings into your build: 1. Add a build hook such as `"prebuild": "appwarden-link --fqdn=your.app"` 2. Import `.appwarden/linked/middleware.json` 3. Merge that generated config with code-level overrides by calling `getAppwardenConfiguration(generatedConfig, overrides)` Values passed in `overrides` take precedence over the generated config. ##### Astro on Cloudflare ```ts // src/middleware.ts import { createAppwardenMiddleware, getAppwardenConfiguration, } from "@appwarden/middleware/cloudflare/astro" import { sequence } from "astro:middleware" import { env } from "cloudflare:workers" import appwardenConfig from "../.appwarden/linked/middleware.json" const appwarden = createAppwardenMiddleware(() => getAppwardenConfiguration(appwardenConfig, { appwardenApiToken: env.APPWARDEN_API_TOKEN, }), ) export const onRequest = sequence(appwarden) ``` See the [Astro + Cloudflare guide](https://appwarden.io/docs/guides/astro-cloudflare) for more details. ##### React Router on Cloudflare - Set `future.v8_middleware: true` in your `react-router.config.ts` file ```ts // app/root.tsx import { createAppwardenMiddleware, getAppwardenConfiguration, } from "@appwarden/middleware/cloudflare/react-router" import { env } from "cloudflare:workers" import appwardenConfig from "../.appwarden/linked/middleware.json" export const middleware = [ createAppwardenMiddleware(() => getAppwardenConfiguration(appwardenConfig, { appwardenApiToken: env.APPWARDEN_API_TOKEN, }), ), ] ``` See the [React Router + Cloudflare guide](https://appwarden.io/docs/guides/react-router-cloudflare) for more details. ##### TanStack Start on Cloudflare ```ts // start.ts import { createAppwardenMiddleware, getAppwardenConfiguration, } from "@appwarden/middleware/cloudflare/tanstack-start" import { createMiddleware, createStart } from "@tanstack/react-start" import { env } from "cloudflare:workers" import appwardenConfig from "../.appwarden/linked/middleware.json" const appwardenMiddleware = createMiddleware().server( createAppwardenMiddleware(() => getAppwardenConfiguration(appwardenConfig, { appwardenApiToken: env.APPWARDEN_API_TOKEN, }), ), ) export const startInstance = createStart(() => ({ requestMiddleware: [appwardenMiddleware], })) ``` See the [TanStack Start + Cloudflare guide](https://appwarden.io/docs/guides/tanstack-start-cloudflare) for more details. ##### Next.js on Cloudflare (OpenNext) ```ts // middleware.ts or proxy.ts import { createAppwardenMiddleware, getAppwardenConfiguration, } from "@appwarden/middleware/cloudflare/nextjs" import appwardenConfig from "./.appwarden/linked/middleware.json" export const config = { matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"], } export default createAppwardenMiddleware((runtime) => getAppwardenConfiguration(appwardenConfig, { appwardenApiToken: runtime.env.APPWARDEN_API_TOKEN, }), ) ``` This adapter applies CSP **headers only** before origin (no HTML rewriting, no nonce injection). Nonce-based CSP (`{{nonce}}`) is **not supported** in this adapter; CSP directives must not include `{{nonce}}`. ### 2. Vercel To use Appwarden as Vercel Edge Middleware, use the `@appwarden/middleware/vercel` bundle: ```ts // middleware.ts (Next.js app on Vercel) import { createAppwardenMiddleware } from "@appwarden/middleware/vercel" const appwardenMiddleware = createAppwardenMiddleware({ // Edge Config or Upstash KV URL cacheUrl: process.env.APPWARDEN_CACHE_URL!, // Required when using Vercel Edge Config vercelApiToken: process.env.APPWARDEN_VERCEL_API_TOKEN!, appwardenApiToken: process.env.APPWARDEN_API_TOKEN!, lockPageSlug: "/maintenance", contentSecurityPolicy: { // See Configuration > contentSecurityPolicy section for details mode: "report-only", directives: { "default-src": ["'self'"], }, }, }) export default appwardenMiddleware ``` Nonce-based CSP (`{{nonce}}`) is **not supported** in Vercel Edge Middleware; CSP directives must not include `{{nonce}}`. ## Supported platforms - [All websites on Cloudflare](https://appwarden.io/docs/guides/cloudflare-middleware-integration) - [Astro on Cloudflare](https://appwarden.io/docs/guides/astro-cloudflare) - [React Router on Cloudflare](https://appwarden.io/docs/guides/react-router-cloudflare) - [TanStack Start on Cloudflare](https://appwarden.io/docs/guides/tanstack-start-cloudflare) - [Next.js on Cloudflare (OpenNext)](https://appwarden.io/docs/guides/nextjs-cloudflare) - [All websites on Vercel](https://appwarden.io/docs/guides/vercel-integration) ## Agent skills This repository ships [TanStack Intent](https://tanstack.com/intent) skills under `skills/` so coding agents can discover and apply Appwarden installation, configuration, and troubleshooting guidance. After installing `@appwarden/middleware`, configure your agent to use the skills: ```bash npx @tanstack/intent@latest install ``` For edit-time skill suggestions in Claude Code or Codex: ```bash npx @tanstack/intent@latest hooks install ``` Then load a specific skill when working on a task: ```bash npx @tanstack/intent@latest load @appwarden/middleware#appwarden-middleware-get-started ``` List available skills: ```bash npx @tanstack/intent@latest list ``` ## Contributing Contributions are welcome! Please feel free to submit a Pull Request. 1. Fork the repository 2. Create your feature branch (`git checkout -b feature/amazing-feature`) 3. Commit your changes using the [Conventional Commits](https://www.conventionalcommits.org/) format - This project enforces commit message format with commitlint - Examples: - `feat: add new feature` - `fix: resolve issue with X` - `docs: update README` - `chore: update dependencies` - `test: add tests for feature X` 4. Push to the branch (`git push origin feature/amazing-feature`) 5. Open a Pull Request ## Development ```bash # Install dependencies pnpm install # Build the package pnpm build # Run tests pnpm test ``` ## Security Please review our [security policy](SECURITY.md) for details on how we handle vulnerabilities and how to report a security issue. This package is published with npm trusted publishers, to prevent npm token exfiltration, and provenance enabled, which provides a verifiable link between the published package and its source code. For more information, see [npm provenance documentation](https://docs.npmjs.com/generating-provenance-statements). ## Versioning & dependencies - This project uses Conventional Commits and automated release tooling to keep versions and the changelog up to date. - Patch releases may include bug fixes and internal maintenance or dependency updates (for example, Cloudflare Workers types, Wrangler, and GitHub Action tooling). New features are shipped in minor releases. - See `CHANGELOG.md` for the complete release history. ## License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.