--- outline: deep --- # Quickstart Cap is a modern, lightweight, open-source CAPTCHA alternative using SHA-256 proof-of-work. Unlike traditional CAPTCHAs, Cap: - Is fast and unobtrusive - Uses no tracking or cookies - Uses proof-of-work instead of intrusive puzzles - Is fully accessible and self-hostable Here, try it yourself: ## Components Cap consists mainly of the **widget** (can be used invisibly) and **server** (you can use the Standalone server instead). Alternatively, M2M is also supported and there's also a checkpoint middleware similar to Cloudflare. This guide details how to use the usual setup. You can find guides on using the [Standalone server](./standalone/index.md), [M2M solver](./solver.md), and [checkpoint middleware](./middleware/index.md) in their respective sections. We highly recommend checking out the [Standalone mode](./standalone/index.md) as it's complete, fast, simple to set up, and works with any language that can make HTTP requests. It also includes a dashboard, API key support, and more. ## Client-side Start by adding importing the Cap widget library from a CDN: ::: code-group ```html [jsdelivr] ``` ```html [unpkg] ``` ::: ::: warning You should optimally pin a specific version to avoid breaking changes instead of using @latest ::: Next, add the `` component to your HTML. ```html ``` You'll need to start a server with the Cap API running at the same URL as specified in the `data-cap-api-endpoint` attribute. In the server-side example we provided, it's set to `/api`, but you can change this by replacing every `app.post('/api/...', ...)` to `app.post('//...', ...)`. We'll tell you how to set up the server in the next section. Then, in your JavaScript, listen for the `solve` event to capture the token when generated: ```js{3} const widget = document.querySelector("#cap"); widget.addEventListener("solve", function (e) { const token = e.detail.token; // Handle the token as needed }); ``` Alternatively, you can use `onsolve=""` directly within the widget or wrap the widget in a `
` (where Cap will automatically submit the token alongside other form data. for this, it'll create a hidden field with name set to its `data-cap-hidden-field-name` attribute or `cap-token`). ## Server-side Cap is fully self-hosted, so you'll need to start a server exposing an API for Cap's methods running at the same URL as specified in the `data-cap-api-endpoint` attribute. This is easy since we've already pre-made a library to help you generate and validate challenges for you. At least Node 14 is required. Most modern Bun or Deno versions should work too. Start by installing it: ::: code-group ```bash [bun] bun add @cap.js/server ``` ```bash [npm] npm i @cap.js/server ``` ```bash [pnpm] pnpm i @cap.js/server ``` ::: ::: tip Don't use JavaScript on your backend? Try the [Standalone mode](./standalone/index.md). ::: Now, you'll need to change your server code to add the routes that Cap needs to work. Here's an example: ::: code-group ```js [elysia] import { Elysia } from "elysia"; import Cap from "@cap.js/server"; const cap = new Cap({ tokens_store_path: ".data/tokensList.json", }); new Elysia() .post("/api/challenge", () => { return cap.createChallenge(); }) .post("/api/redeem", async ({ body, set }) => { const { token, solutions } = body; if (!token || !solutions) { set.status = 400; return { success: false }; } return await cap.redeemChallenge({ token, solutions }); }) .listen(3000); console.log(`🦊 Elysia is running at http://localhost:3000`); ``` ```js [fastify] import Fastify from "fastify"; import Cap from "@cap.js/server"; const fastify = Fastify(); const cap = new Cap({ tokens_store_path: ".data/tokensList.json", }); fastify.post("/api/challenge", (req, res) => { res.send(cap.createChallenge()); }); fastify.post("/api/redeem", async (req, res) => { const { token, solutions } = req.body; if (!token || !solutions) { return res.code(400).send({ success: false }); } res.send(await cap.redeemChallenge({ token, solutions })); }); fastify.listen({ port: 3000, host: "0.0.0.0" }).then(() => { console.log("Server is running on http://localhost:3000"); }); ``` ```js [bun.serve] import Cap from "@cap.js/server"; const cap = new Cap({ tokens_store_path: ".data/tokensList.json", }); Bun.serve({ port: 3000, routes: { "/api/challenge": { POST: () => { return Response.json(cap.createChallenge()); }, }, "/api/redeem": { POST: async (req) => { const body = await req.json(); const { token, solutions } = body; if (!token || !solutions) { return Response.json({ success: false }, { status: 400 }); } return Response.json(await cap.redeemChallenge({ token, solutions })); }, }, }, }); console.log(`Server running at http://localhost:3000`); ``` ```js [hono] import { Hono } from "hono"; import Cap from "@cap.js/server"; const app = new Hono(); const cap = new Cap({ tokens_store_path: ".data/tokensList.json", }); app.post("/api/challenge", (c) => { return c.json(cap.createChallenge()); }); app.post("/api/redeem", async (c) => { const { token, solutions } = await c.req.json(); if (!token || !solutions) { return c.json({ success: false }, 400); } return c.json(await cap.redeemChallenge({ token, solutions })); }); export default { port: 3000, fetch: app.fetch, }; ``` ```js [express] import express from "express"; import Cap from "@cap.js/server"; const app = express(); app.use(express.json()); const cap = new Cap({ tokens_store_path: ".data/tokensList.json", }); app.post("/api/challenge", (req, res) => { res.json(cap.createChallenge()); }); app.post("/api/redeem", async (req, res) => { const { token, solutions } = req.body; if (!token || !solutions) { return res.status(400).json({ success: false }); } res.json(await cap.redeemChallenge({ token, solutions })); }); app.listen(3000, () => { console.log("Listening on port 3000"); }); ``` ::: Note that these are the simplest examples possible. You should adapt them to your needs. Usually, you'll want to: - Use an actual database such as SQLite or Redis for storing tokens and challenges. This is done for you in the [Standalone server](./standalone/index.md) - Add some kind of actual ratelimiting. This is also done for you in the [Standalone server](./standalone/index.md) ### Token validation Once the token is generated and captured, you can use it later to validate the user's identity. You can do this by calling `await cap.validateToken` in your server-side code: ```js await cap.validateToken("..."); // returns { success: Boolean } ``` Note that the token will immediately be deleted after this. To prevent this, use `await cap.validateToken("...", { keepToken: true })`.