docs: many general improvements
This commit is contained in:
@@ -58,4 +58,4 @@ Cap's free instance is supported by DigitalOcean for open-source. <a href="https
|
||||
|
||||
---
|
||||
|
||||
[](https://www.bestpractices.dev/projects/9920) [](https://www.producthunt.com/posts/cap-5?embed=true&utm_source=badge-top-post-topic-badge&utm_medium=badge&utm_souce=badge-cap-5)
|
||||
[](https://www.bestpractices.dev/projects/9920) [](https://www.jsdelivr.com/package/npm/@cap.js/widget)
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
- checkpoints: traefik support maybe?
|
||||
- general: ability to change challenge algorithm
|
||||
+190
-203
@@ -4,117 +4,117 @@ import { withMermaid } from "vitepress-plugin-mermaid";
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default withMermaid({
|
||||
lang: "en-US",
|
||||
title: "Cap — Modern, lightning-quick PoW captcha",
|
||||
description:
|
||||
"Cap is a lightweight, modern open-source CAPTCHA alternative using SHA-256 proof-of-work",
|
||||
lastUpdated: true,
|
||||
vite: {
|
||||
plugins: [llmstxt()],
|
||||
},
|
||||
transformPageData(pageData) {
|
||||
pageData.frontmatter.head ??= [];
|
||||
pageData.frontmatter.head.push([
|
||||
"link",
|
||||
{
|
||||
rel: "canonical",
|
||||
href: `https://capjs.js.org/${pageData.relativePath}`
|
||||
.replace(/index\.md$/, "")
|
||||
.replace(/\.md$/, ".html"),
|
||||
},
|
||||
]);
|
||||
},
|
||||
head: [
|
||||
["link", { rel: "icon", href: "/logo.png" }],
|
||||
[
|
||||
"meta",
|
||||
{
|
||||
name: "keywords",
|
||||
content:
|
||||
"account security, abuse detection, abuse prevention, api abuse, api defense, api analytics, api protection, api security, anti-abuse, anti-automation, anti-bot, anti-ddos, anti-dos, anti-exploitation, anti-spam, application security, automated attacks, automated scraping, bot defense, bot detection, bot management, bot mitigation, bot prevention, bot traffic, brute force, challenge generator, challenge string, challenge verifier, challenge-response, challenge-response protocol, client-server architecture, computational challenge, computational complexity, computational defense, computational puzzle, crypto puzzle, cryptographic algorithm, cryptographic challenge, cryptographic defense, cryptographic puzzle, cybersecurity, ddos, ddos protection, defense mechanism, digital security, form security, fraud detection, fraud prevention, hash function, hash puzzle, hashcash, hcaptcha, captcha alternative, human test, human verification, internet security, invisible captcha, login security, malicious traffic, pow, proof of work, proof-of-work, recaptcha, security challenge, security component, security library, server defense, spam bots, spam filtering, spam mitigation, spam prevention, target difficulty, threat intelligence, traffic management, traffic monitoring, turing test, unwanted traffic, user experience, web application protection, web application security, web defense, web defense system, website defense, website protection, website security",
|
||||
},
|
||||
],
|
||||
["meta", { name: "author", content: "Tiago Rangel" }],
|
||||
[
|
||||
"meta",
|
||||
{
|
||||
property: "og:title",
|
||||
content: "Cap — Modern, Open-source PoW CAPTCHA for JavaScript",
|
||||
},
|
||||
],
|
||||
[
|
||||
"meta",
|
||||
{
|
||||
property: "og:description",
|
||||
content:
|
||||
"Cap.js is a fast, privacy-first proof-of-work CAPTCHA alternative to reCAPTCHA and hCaptcha. Zero dependencies, developer-friendly, and effective against spam, DDoS, and automation.",
|
||||
},
|
||||
],
|
||||
["meta", { property: "og:url", content: "https://capjs.js.org" }],
|
||||
[
|
||||
"meta",
|
||||
{ property: "og:image", content: "https://capjs.js.org/logo.png" },
|
||||
],
|
||||
["meta", { name: "twitter:card", content: "summary_large_image" }],
|
||||
[
|
||||
"meta",
|
||||
{
|
||||
name: "twitter:title",
|
||||
content: "Cap — Modern, Open-source PoW CAPTCHA for JavaScript",
|
||||
},
|
||||
],
|
||||
[
|
||||
"meta",
|
||||
{
|
||||
name: "twitter:description",
|
||||
content:
|
||||
"Cap.js is a fast, privacy-first proof-of-work CAPTCHA alternative to reCAPTCHA and hCaptcha.",
|
||||
},
|
||||
],
|
||||
[
|
||||
"meta",
|
||||
{ name: "twitter:image", content: "https://capjs.js.org/logo.png" },
|
||||
],
|
||||
[
|
||||
"meta",
|
||||
{
|
||||
name: "google-site-verification",
|
||||
content: "_qNXNJhgoxAeT8hv5PctRvPqfwRKOGo-TtjAhFewmYw",
|
||||
},
|
||||
],
|
||||
[
|
||||
"script",
|
||||
{
|
||||
defer: true,
|
||||
"data-domain": "capjs.js.org",
|
||||
src: "https://a.tiago.zip/js/script.hash.outbound-links.pageview-props.tagged-events.js",
|
||||
},
|
||||
],
|
||||
[
|
||||
"script",
|
||||
{
|
||||
defer: true,
|
||||
"no-twidget": "true",
|
||||
src: "https://tiago.zip/cdn/widget.js",
|
||||
},
|
||||
],
|
||||
[
|
||||
"script",
|
||||
{
|
||||
type: "application/ld+json",
|
||||
},
|
||||
`{
|
||||
lang: "en-US",
|
||||
title: "Cap — Modern, lightning-quick PoW captcha",
|
||||
description:
|
||||
"Cap is a lightweight, modern open-source CAPTCHA alternative using SHA-256 proof-of-work",
|
||||
lastUpdated: true,
|
||||
vite: {
|
||||
plugins: [llmstxt()],
|
||||
},
|
||||
transformPageData(pageData) {
|
||||
pageData.frontmatter.head ??= [];
|
||||
pageData.frontmatter.head.push([
|
||||
"link",
|
||||
{
|
||||
rel: "canonical",
|
||||
href: `https://capjs.js.org/${pageData.relativePath}`
|
||||
.replace(/index\.md$/, "")
|
||||
.replace(/\.md$/, ".html"),
|
||||
},
|
||||
]);
|
||||
},
|
||||
head: [
|
||||
["link", { rel: "icon", href: "/logo.png" }],
|
||||
[
|
||||
"meta",
|
||||
{
|
||||
name: "keywords",
|
||||
content:
|
||||
"proof-of-work, computational challenge, cryptographic puzzle, challenge-response protocol, human verification, anti-bot, anti-abuse, automated attacks, bot detection, bot mitigation, api protection, account security, form security, spam prevention, ddos protection, malicious traffic, web application security, security library, challenge generator, captcha, hcaptcha, turnstile",
|
||||
},
|
||||
],
|
||||
["meta", { name: "author", content: "tiagozip" }],
|
||||
[
|
||||
"meta",
|
||||
{
|
||||
property: "og:title",
|
||||
content: "Cap is the self-hosted CAPTCHA for the modern web.",
|
||||
},
|
||||
],
|
||||
[
|
||||
"meta",
|
||||
{
|
||||
property: "og:description",
|
||||
content:
|
||||
"Lightweight, privacy-first, and designed to put you first. Switch from reCAPTCHA in minutes.",
|
||||
},
|
||||
],
|
||||
["meta", { property: "og:url", content: "https://capjs.js.org" }],
|
||||
[
|
||||
"meta",
|
||||
{ property: "og:image", content: "https://capjs.js.org/logo.png" },
|
||||
],
|
||||
["meta", { name: "twitter:card", content: "summary_large_image" }],
|
||||
[
|
||||
"meta",
|
||||
{
|
||||
name: "twitter:title",
|
||||
content: "Cap is the self-hosted CAPTCHA for the modern web.",
|
||||
},
|
||||
],
|
||||
[
|
||||
"meta",
|
||||
{
|
||||
name: "twitter:description",
|
||||
content:
|
||||
"Lightweight, privacy-first, and designed to put you first. Switch from reCAPTCHA in minutes.",
|
||||
},
|
||||
],
|
||||
[
|
||||
"meta",
|
||||
{ name: "twitter:image", content: "https://capjs.js.org/logo.png" },
|
||||
],
|
||||
[
|
||||
"meta",
|
||||
{
|
||||
name: "google-site-verification",
|
||||
content: "_qNXNJhgoxAeT8hv5PctRvPqfwRKOGo-TtjAhFewmYw",
|
||||
},
|
||||
],
|
||||
[
|
||||
"script",
|
||||
{
|
||||
defer: true,
|
||||
"data-domain": "capjs.js.org",
|
||||
src: "https://a.tiago.zip/js/script.hash.outbound-links.pageview-props.tagged-events.js",
|
||||
},
|
||||
],
|
||||
[
|
||||
"script",
|
||||
{
|
||||
defer: true,
|
||||
"no-twidget": "true",
|
||||
src: "https://tiago.zip/cdn/widget.js",
|
||||
},
|
||||
],
|
||||
[
|
||||
"script",
|
||||
{
|
||||
type: "application/ld+json",
|
||||
},
|
||||
`{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "SoftwareApplication",
|
||||
"name": "Cap",
|
||||
"url": "https://capjs.js.org",
|
||||
"description": "Cap is a lightweight, modern open-source CAPTCHA alternative designed using SHA-256 proof-of-work",
|
||||
"description": "Lightweight, privacy-first, and designed to put you first. Switch from reCAPTCHA in minutes.",
|
||||
"applicationCategory": "SecurityApplication",
|
||||
"operatingSystem": "All",
|
||||
"image": "https://capjs.js.org/logo.png",
|
||||
"author": {
|
||||
"@type": "Person",
|
||||
"name": "Tiago Rangel",
|
||||
"name": "tiago",
|
||||
"url": "https://tiago.zip"
|
||||
},
|
||||
"license": "https://github.com/tiagozip/cap/blob/main/LICENSE",
|
||||
@@ -124,111 +124,98 @@ export default withMermaid({
|
||||
"priceCurrency": "USD"
|
||||
}
|
||||
}`,
|
||||
],
|
||||
[
|
||||
"script",
|
||||
{
|
||||
src: "https://cdn.jsdelivr.net/npm/@cap.js/widget@0.1.28",
|
||||
async: true,
|
||||
},
|
||||
],
|
||||
],
|
||||
themeConfig: {
|
||||
search: {
|
||||
provider: "algolia",
|
||||
options: {
|
||||
appId: "B8THEYC8QW",
|
||||
apiKey: "ebdc4d8bd68e388cbeca09c14b982a85",
|
||||
indexName: "cap-tiagorangel",
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
"script",
|
||||
{
|
||||
src: "https://cdn.jsdelivr.net/npm/@cap.js/widget@0.1.32",
|
||||
async: true,
|
||||
},
|
||||
],
|
||||
],
|
||||
themeConfig: {
|
||||
search: {
|
||||
provider: "algolia",
|
||||
options: {
|
||||
appId: "B8THEYC8QW",
|
||||
apiKey: "ebdc4d8bd68e388cbeca09c14b982a85",
|
||||
indexName: "cap-tiagorangel",
|
||||
},
|
||||
},
|
||||
|
||||
logo: "/logo.png",
|
||||
siteTitle: "Cap",
|
||||
logo: "/logo.png",
|
||||
siteTitle: "Cap",
|
||||
|
||||
editLink: {
|
||||
pattern: "https://github.com/tiagozip/cap/edit/main/docs/:path",
|
||||
},
|
||||
editLink: {
|
||||
pattern: "https://github.com/tiagozip/cap/edit/main/docs/:path",
|
||||
},
|
||||
|
||||
nav: [
|
||||
{ text: "Home", link: "/" },
|
||||
{ text: "Docs", link: "/guide" },
|
||||
{ text: "GitHub", link: "https://github.com/tiagozip/cap" },
|
||||
],
|
||||
nav: [
|
||||
{ text: "Home", link: "/" },
|
||||
{ text: "Docs", link: "/guide" },
|
||||
{ text: "GitHub", link: "https://github.com/tiagozip/cap" },
|
||||
],
|
||||
|
||||
sidebar: [
|
||||
{ text: "Quickstart", link: "/guide/index.md" },
|
||||
{ text: "Feature comparison", link: "/guide/alternatives.md" },
|
||||
{
|
||||
text: "Standalone",
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: "Quickstart", link: "/guide/standalone/index.md" },
|
||||
{ text: "API", link: "/guide/standalone/api.md" },
|
||||
{ text: "Options", link: "/guide/standalone/options.md" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Libraries",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "Server", link: "/guide/server.md" },
|
||||
{ text: "Widget", link: "/guide/widget.md" },
|
||||
{ text: "Solver", link: "/guide/solver.md" },
|
||||
{ text: "Community libraries", link: "/guide/community.md" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Modes",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "Floating mode", link: "/guide/floating.md" },
|
||||
{ text: "Invisible mode", link: "/guide/invisible.md" },
|
||||
],
|
||||
},
|
||||
/*
|
||||
{
|
||||
text: "Checkpoint",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "About", link: "/guide/middleware/index.md" },
|
||||
{ text: "Elysia", link: "/guide/middleware/elysia.md" },
|
||||
{ text: "Hono", link: "/guide/middleware/hono.md" },
|
||||
{ text: "Express", link: "/guide/middleware/express.md" },
|
||||
],
|
||||
},
|
||||
*/
|
||||
{
|
||||
text: "Proof-of-work",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "Effectiveness", link: "/guide/effectiveness.md" },
|
||||
{ text: "How does it work", link: "/guide/workings.md" },
|
||||
{ text: "Philosophy", link: "/guide/philosophy.md" },
|
||||
],
|
||||
},
|
||||
{ text: "Benchmark", link: "/guide/benchmark.md" },
|
||||
{ text: "Demo", link: "/guide/demo.md" },
|
||||
{ text: "GitHub", link: "https://github.com/tiagozip/cap" },
|
||||
],
|
||||
sidebar: [
|
||||
{ text: "Quickstart", link: "/guide/index.md" },
|
||||
{ text: "Feature comparison", link: "/guide/alternatives.md" },
|
||||
{
|
||||
text: "Standalone",
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: "Quickstart", link: "/guide/standalone/index.md" },
|
||||
{ text: "API", link: "/guide/standalone/api.md" },
|
||||
{ text: "Options", link: "/guide/standalone/options.md" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Widget",
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: "Usage", link: "/guide/widget.md" },
|
||||
{ text: "Invisible mode", link: "/guide/invisible.md" },
|
||||
{ text: "Floating mode", link: "/guide/floating.md" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Libraries",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "Server", link: "/guide/server.md" },
|
||||
{ text: "M2M", link: "/guide/solver.md" },
|
||||
{ text: "Community libraries", link: "/guide/community.md" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Proof-of-work",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "Effectiveness", link: "/guide/effectiveness.md" },
|
||||
{ text: "How does it work", link: "/guide/workings.md" },
|
||||
],
|
||||
},
|
||||
{ text: "Benchmark", link: "/guide/benchmark.md" },
|
||||
{ text: "Demo", link: "/guide/demo.md" },
|
||||
{ text: "GitHub", link: "https://github.com/tiagozip/cap" },
|
||||
],
|
||||
|
||||
socialLinks: [
|
||||
{ icon: "github", link: "https://github.com/tiagozip/cap" },
|
||||
{ icon: "twitter", link: "https://x.com/0xtiago_" },
|
||||
],
|
||||
socialLinks: [
|
||||
{ icon: "github", link: "https://github.com/tiagozip/cap" },
|
||||
{ icon: "twitter", link: "https://x.com/0xtiago_" },
|
||||
],
|
||||
|
||||
footer: {
|
||||
message: "Built in Europe 🇪🇺<br>Released under the Apache 2.0 License.",
|
||||
copyright:
|
||||
"Copyright © 2025-present <a href='https://tiago.zip' target='_blank'>Tiago</a>",
|
||||
},
|
||||
},
|
||||
markdown: {
|
||||
image: {
|
||||
lazyLoading: true,
|
||||
},
|
||||
},
|
||||
sitemap: {
|
||||
hostname: "https://capjs.js.org",
|
||||
},
|
||||
footer: {
|
||||
message: "Built in Europe 🇪🇺<br>Released under the Apache 2.0 License.",
|
||||
copyright:
|
||||
"Copyright © 2025-present <a href='https://tiago.zip' target='_blank'>Tiago</a>",
|
||||
},
|
||||
},
|
||||
markdown: {
|
||||
image: {
|
||||
lazyLoading: true,
|
||||
},
|
||||
},
|
||||
sitemap: {
|
||||
hostname: "https://capjs.js.org",
|
||||
},
|
||||
});
|
||||
|
||||
+18
-18
@@ -1,6 +1,6 @@
|
||||
# Alternatives to Cap
|
||||
|
||||
| CAPTCHA | Open-source | Free | Private | Fast to solve | Easy for humans | Small error rate | Checkpoint support | Widget support | GDPR/CCPA Compliant | Customizable | Hard for bots | Easy to integrate |
|
||||
| CAPTCHA | Open-source | Free | Private | Fast to solve | Easy for humans | Small error rate | Checkpoints | Widget support | GDPR/CCPA Compliant | Customizable | Hard for bots | Easy to integrate |
|
||||
| :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- |
|
||||
| **Cap** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | 🟨 | ✅ |
|
||||
| Cloudflare Turnstile | ❌ | ✅ | 🟨 | 🟨 | ✅ | ❌ | 🟨 | ✅ | ✅ | ❌ | 🟨 | ✅ |
|
||||
@@ -12,37 +12,41 @@
|
||||
| GeeTest | ❌ | ❌ | ❌ | 🟨 | 🟨 | 🟨 | ❌ | ✅ | ✅ | ❌ | 🟨 | 🟨 |
|
||||
| Arkose Labs | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | 🟨 | ❌ | ❌ |
|
||||
|
||||
**Note:** "Hard for bots" does not consider solvers like 2captcha, XEvil solver, or BrightData, as I cannot verify their legitimacy myself. However, they're extremely cheap.
|
||||
::: tip Note
|
||||
|
||||
"Hard for bots" does not consider commercial solvers like BrightData, as I cannot verify their legitimacy myself.
|
||||
|
||||
:::
|
||||
|
||||
## All alternatives
|
||||
|
||||
### Cloudflare Turnstile
|
||||
|
||||
Cloudflare Turnstile is a great alternative to Cap, but unfortunately it is known for having an extremely high error rate and relies a lot on fingerprinting, especially for users using private browsers such as Brave or Librewolf. Also, unlike Turnstile, Cap is open-source and self-hosted.
|
||||
Cloudflare Turnstile is a great alternative to Cap, but unfortunately it is known for having an extremely high error rate and relies a lot on fingerprinting, especially for users using private browsers such as Brave or Librewolf. Additionally, unlike Turnstile, Cap is open-source and self-hosted.
|
||||
|
||||
### reCAPTCHA
|
||||
|
||||
Not only is Cap significantly smaller and faster than reCAPTCHA, it's open-source, fully free and is significantly more private. Cap doesn't require you to check traffic signs or solve puzzles, and it doesn't track users or collect data. reCAPTCHA is also highly inefficient due to its challenges being easily solved by multi-modal LLMs like Gemini 2.5
|
||||
Not only is Cap significantly smaller and faster than reCAPTCHA, it's open-source, fully free and is significantly more private. Cap doesn't require you to check traffic signs or solve puzzles, and it doesn't track users or collect data.
|
||||
|
||||
Additionally, reCAPTCHA v2 challenges can be solved trivially by multi-modal LLMs, especially audio challenges.
|
||||
|
||||
### hCAPTCHA
|
||||
|
||||
Pretty much the same as reCAPTCHA, although hCAPTCHA is significantly more secure. Personally it's the best alternative to Cap, even though the widget's bundle size alone is significantly bigger.
|
||||
Pretty much the same as reCAPTCHA, however while it's significantly more resistant to bots, it is also extremely annoying for users to solve when prompted with a visual challenge compared to reCAPTChA. It also has a significantly bigger bundle size.
|
||||
|
||||
Unfortunately, hCAPTCHA is extremely expensive to use. At the time of writing, it's [$1/1k solutions](https://www.hcaptcha.com/pricing)
|
||||
Additionally, hCAPTCHA is prohibitively expensive for many users, while Cap is completely free and self-hosted.
|
||||
|
||||
### Altcha
|
||||
|
||||
Cap is slightly smaller than Altcha and includes extra features like checkpoints, progress tracking, and a simpler dashboard. if you don’t need those and prefer a more mature solution, Altcha is a still a solid choice.
|
||||
Cap is slightly smaller than Altcha and includes extra features like progress tracking, and a simpler dashboard. if you don't need these, Altcha is a still a solid choice.
|
||||
|
||||
### mCAPTCHA
|
||||
|
||||
mCAPTCHA is similar to both Cap and Altcha, but unfortunately mCAPTCHA doesn't look actively maintained anymore (the last commit was over a year ago).
|
||||
|
||||
Its bundle is also significantly larger, with the widget iframe being at 250kb! (150kb of that is just for a shield png)
|
||||
While mCAPTCHA is similar to both Cap and Altcha, it seems to have been deprecated and has a significantly larger widget bundle.
|
||||
|
||||
### FriendlyCaptcha
|
||||
|
||||
Unlike FriendlyCaptcha, Cap is free and open-source (FriendlyCaptcha is €39/month for 5,000 requests and 5 domains) and has a smaller bundle size.
|
||||
Unlike FriendlyCaptcha, Cap is completely free and self-hosted (FriendlyCaptcha is €39/month for 5k requests and 5 domains).
|
||||
|
||||
### MTCaptcha
|
||||
|
||||
@@ -50,16 +54,12 @@ Cap is more lightweight, doesn't rely on users solving an image puzzle that LLMs
|
||||
|
||||
### GeeTest
|
||||
|
||||
Cap is free, self-hosted and open-source, while GeeTest is a paid service. Cap is also more private and doesn't rely on tracking users or collecting data. **GeeTest is also china-based, which means that it has to hand over data to the chinese government if they request so.** Note that GeeTest partially uses MD5 PoW too.
|
||||
Cap is free, self-hosted and open-source, while GeeTest is a paid service. Cap is also more private and doesn't rely on tracking users or collecting data. GeeTest is also china-based, which may be a concern for some users.
|
||||
|
||||
### Arkose Labs
|
||||
|
||||
Even while being extremely hard, slow and annoying for humans to solve, Arkose's FunCAPTCHA can be easily solved by LLMs due to their audio CAPTCHA. It's also closed-source and paid.
|
||||
Arkose's CAPTCHA is known for being hard, slow and annoying for humans to solve. It is also a paid, closed-source service.
|
||||
|
||||
### Anubis
|
||||
|
||||
Anubis is quite different from typical captchas. It's mainly designed to block scraping (like Cap's checkpoints) but afaik lacks widget support and customizable server-side integration. It's also not very secure, since its default difficulty is low and easy to bypass.
|
||||
|
||||
Its implementation has performance issues too. Anubis runs slower than necessary because it doesn’t use WebAssembly at all (unlike Cap). As such, both the default difficulty and the difficulty usually set by other websites is low to avoid frustrating users. That makes it easier for attackers to use optimized algorithms to solve it quickly.
|
||||
|
||||
Plus, the noscript fallback is basically useless: it just makes users wait a few seconds before retrying, which is even weaker than a basic rate limit.
|
||||
While Anubis is a great scraper deterrent and uses the same proof-of-work concept as Cap, it uses a low difficulty by default (which is easier for bots to solve) and does not provide a standalone CAPTCHA.
|
||||
|
||||
+3
-12
@@ -1,14 +1,12 @@
|
||||
# Benchmark
|
||||
|
||||
Press the button below to run a simple benchmark.
|
||||
|
||||
<Benchmark />
|
||||
|
||||
## Benchmark results
|
||||
|
||||
Using widget 0.1.25, WASM 0.0.6. These use a placeholder server, speeds might vary depending on your setup and network latency.
|
||||
These use a placeholder server, speeds might vary depending on your setup and network latency. Please also note that due to a bug in Chromium's devtools, speeds might be slower than expected when devtools is open.
|
||||
|
||||
Note that solving might be significantly slower if you have your devtools open depending on what browser you're using. This is not intentional but is also not a bug with Cap.
|
||||
A challenge difficulty of 4 with 50 challenges was used to run these benchmarks.
|
||||
|
||||
| Tier | Device | Chrome | Safari |
|
||||
| --------- | ------------------ | ------ | ------ |
|
||||
@@ -17,11 +15,4 @@ Note that solving might be significantly slower if you have your devtools open d
|
||||
| Mid-range | Google Pixel 7 | 1.027s | - |
|
||||
| Mid-range | iPad (9th gen) | – | 1.401s |
|
||||
| High-end | Google Pixel 9 | 0.894s | – |
|
||||
| High-end | MacBook Air M3 | 0.312s | 0.423s |
|
||||
|
||||
Tested with BrowserStack using the following configuration:
|
||||
|
||||
- **Challenge difficulty:** 4
|
||||
- **Number of challenges:** 50
|
||||
- **Salt/challenge size:** 32
|
||||
- **Number of benchmarks:** 50
|
||||
| High-end | M3 Macbook | 0.312s | 0.423s |
|
||||
@@ -82,5 +82,3 @@
|
||||
<button class="signin-button" onclick="alert('Success!')">Sign in</button>
|
||||
|
||||
You can find more demos [in the GitHub repo](https://github.com/tiagozip/cap/tree/main/demo). Note that this demo is not a full implementation of Cap and uses a placeholder server.
|
||||
|
||||
Note that the widget is also fully customizable! If you don't like how it looks, you can use CSS variables to change just about everything.
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
# Effectiveness
|
||||
|
||||
Cap significantly reduces spam and abuse on websites and web apps. It won't stop _everything_ (no CAPTCHA is foolproof), however, it minimizes the potential for abuse by making it expensive. The main principle behind implementing a proof-of-work CAPTCHA like Cap includes **proving effort** instead of basic fingerprinting or solving puzzles to verify whether someone is human.
|
||||
Cap significantly reduces spam and abuse on websites and web apps. It won't stop _everything_ (no CAPTCHA is foolproof), however, it minimizes the potential for abuse by making it expensive.
|
||||
|
||||
The main principle behind implementing a proof-of-work CAPTCHA like Cap includes **proving effort** instead of fingerprinting or solving visual puzzles.
|
||||
|
||||
## Privacy & security
|
||||
|
||||
Cap doesn't use cookies or telemetry by default. No data is collected or stored in our servers.
|
||||
Cap doesn't use cookies or telemetry by default. No data is collected or stored in our servers as it's fully self-hosted.
|
||||
|
||||
By default, Cap's server library uses the following defaults:
|
||||
## Why proof-of-work?
|
||||
|
||||
- **Challenge storage:** Challenges are only stored in memory on the server to prevent tampering and expire after 10 mins
|
||||
- **Token storage:** Only hashed tokens are stored persistently (in `.data/tokensList.json` by default) to validate user sessions, and they also expire (20 minutes by default).
|
||||
Every CAPTCHA can eventually be solved, whether by AIs, algorithms, reverse-engineering and spoofing fingerprints, or humans paid via CAPTCHA farms — this results in an endless cat-and-mouse game between attackers and defenders. The crucial difference lies in the cost imposed on attackers.
|
||||
|
||||
## Why Proof-of-work?
|
||||
|
||||
Every CAPTCHA can eventually be solved, whether by AIs, algorithms or humans paid via CAPTCHA farms — this results in an endless cat-and-mouse game between attackers and defenders. The crucial difference lies in the _cost_ imposed on attackers.
|
||||
|
||||
Cap's goal is to make automated abuse expensive while keeping the experience fast and virtually invisible for real users. PoW is a perfect balance for this issue, stopping abuse by requiring computational effort rather than relying solely on human verification methods that bots continuously learn to mimic.
|
||||
Cap's goal is to make automated abuse expensive while keeping the experience fast and virtually invisible for real users. Proof-of-work is a perfect balance for this issue, stopping abuse by requiring computational effort rather than relying solely on human verification methods that bots continuously learn to mimic.
|
||||
|
||||
Imagine sending 10,000 spam messages costs $1, potentially earning $10 – a profitable venture. If Cap increases the computational cost so that sending those messages now costs $100, the spammer loses $90. This eliminates the financial incentive.
|
||||
|
||||
For a deeper dive into the technical aspects, you might find [this research paper](https://www.researchgate.net/publication/374638786_Proof-of-Work_CAPTCHA_with_password_cracking_functionality) useful (note that cap doesn't send **any** hashes to any external servers and cap is not used by law enforcement, the whitepaper is more about the technical background).
|
||||
|
||||
You can also look into [Hashcash](https://www.researchgate.net/publication/2482110_Hashcash_-_A_Denial_of_Service_Counter-Measure), the original proof-of-work system that inspired Cap and that powers Bitcoin.
|
||||
Cap's proof-of-work is heavily inspired by [Hashcash](https://www.researchgate.net/publication/2482110_Hashcash_-_A_Denial_of_Service_Counter-Measure).
|
||||
+38
-38
@@ -4,49 +4,39 @@ outline: deep
|
||||
|
||||
# Quickstart
|
||||
|
||||
Cap is a modern, lightweight, open-source CAPTCHA alternative using SHA-256 proof-of-work.
|
||||
Cap is a modern, lightweight, and self-hosted CAPTCHA alternative using SHA-256 proof-of-work.
|
||||
|
||||
Unlike traditional CAPTCHAs, Cap:
|
||||
Unlike traditional CAPTCHAs, Cap's fast, unobtrusive, has no telemetry or tracking, and uses accessible proof-of-work instead of annoying visual puzzles.
|
||||
|
||||
- Is fast and unobtrusive
|
||||
- Uses no tracking or cookies
|
||||
- Uses proof-of-work instead of annoying visual puzzles
|
||||
- Is fully accessible and self-hostable
|
||||
Cap consists of a client-side widget, which solves challenges and displays the checkbox, and a server-side component, which generates challenges and redeems solutions.
|
||||
|
||||
Here, try it yourself:
|
||||
## Integration
|
||||
|
||||
### Widget
|
||||
|
||||
In order for your users to be able to solve Cap challenges, you'll need to install the widget library to either use the invisible mode or add the checkbox custom component, which looks like this:
|
||||
|
||||
<Demo />
|
||||
|
||||
## Components
|
||||
Add the following to your website's HTML:
|
||||
|
||||
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]
|
||||
<script src="https://cdn.jsdelivr.net/npm/@cap.js/widget@0.1.28"></script>
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/@cap.js/widget@0.1.32"></script>
|
||||
```
|
||||
|
||||
```html [unpkg]
|
||||
<script src="https://unpkg.com/@cap.js/widget@0.1.28"></script>
|
||||
```
|
||||
::: tip Using the scripts without a CDN
|
||||
|
||||
Using `cdn.jsdelivr.net` is optional. If preferred, you can self-host the scripts by either [downloading the latest release files](https://cdn.jsdelivr.net/npm/@cap.js/widget@latest) or, if you're using a framework, installing them with `npm add @cap.js/widget` and serving them from your own server. Remember to update these regularly.
|
||||
|
||||
`cdn.jsdelivr.net` is blocked in some jurisdictions, like some parts of China. If your website needs to be reachable from these jurisdictions, we recommend that you self-host the scripts.
|
||||
|
||||
:::
|
||||
|
||||
You can also just add it with `npm i @cap.js/widget` if your setup supports it, using a CDN isn't really required.
|
||||
|
||||
Next, add the `<cap-widget>` component to your HTML.
|
||||
Next, you can either add the widget component directly to your code:
|
||||
|
||||
```html
|
||||
<cap-widget id="cap" data-cap-api-endpoint="<your cap endpoint>"></cap-widget>
|
||||
<cap-widget data-cap-api-endpoint="<your cap endpoint>"></cap-widget>
|
||||
```
|
||||
|
||||
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. We'll tell you how to set this up in the next section.
|
||||
@@ -54,7 +44,7 @@ You'll need to start a server with the Cap API running at the same URL as specif
|
||||
Then, in your JavaScript, listen for the `solve` event to capture the token when generated:
|
||||
|
||||
```js{3}
|
||||
const widget = document.querySelector("#cap");
|
||||
const widget = document.querySelector("cap-widget");
|
||||
|
||||
widget.addEventListener("solve", function (e) {
|
||||
const token = e.detail.token;
|
||||
@@ -63,18 +53,28 @@ widget.addEventListener("solve", function (e) {
|
||||
});
|
||||
```
|
||||
|
||||
Alternatively, you can use `onsolve=""` directly within the widget or wrap the widget in a `<form></form>` (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`).
|
||||
Alternatively, you can wrap the widget in a `<form></form>` (where Cap will automatically submit the token alongside other form data as `cap-token`.
|
||||
|
||||
You can learn how to use the widget in more detail (such as the invisible mode) in the [widget guide](./widget.md).
|
||||
You can also use get a token programmatically without displaying the widget by using the invisible mode:
|
||||
|
||||
```js
|
||||
const cap = new Cap({
|
||||
apiEndpoint: "/api/"
|
||||
});
|
||||
const solution = await cap.solve();
|
||||
|
||||
// you can attach event listeners to track progress
|
||||
cap.addEventListener("progress", (event) => {
|
||||
console.log(`Solving... ${event.detail.progress}% done`);
|
||||
});
|
||||
|
||||
console.log(solution.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.
|
||||
Cap is fully self-hosted, so you'll need to start a server exposing an API for Cap's protocol.
|
||||
|
||||
You can choose between using the Standalone server or implementing your own server using the `@cap.js/server` package.
|
||||
For most use cases, we recommend using the Docker **[Standalone server](./standalone/index.md)**, as it exposes a simple HTTP API and provides a simple web dashboard with analytics.
|
||||
|
||||
- For most use cases, we recommend using the **[Standalone server](./standalone/index.md)** as it provides a complete solution with built-in features like token storage, ratelimiting, analytics, and a pretty nice dashboard. However, it does require docker and exposing the server port.
|
||||
|
||||
- However, if you prefer to implement your own server, you can use the `@cap.js/server` package. This package provides the necessary methods to create and validate challenges, as well as redeem solutions, but it only works with a JavaScript backend and you'll have to add your own db. [See the server guide](./server.md) for more details.
|
||||
|
||||
- If you would like the simplicity of the server package but with another language, check out the [community packages](./community.md)
|
||||
However, if you can't use Docker or need a more lightweight solution, you can use the [server library](./server.md) on Node and Bun or a [community library](./community.md) in other languages.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Invisible mode
|
||||
|
||||
You can use `new Cap({ ... })` in your client-side JavaScript to create a new Cap instance and use the `solve()` method to solve the challenge. This is helpful for situations where you don't want the Cap widget to be visible but still want security, e.g. on a social media app when posting something.
|
||||
You can use `new Cap({ ... })` in your client-side JavaScript to create a new Cap instance and use the `solve()` method to solve the challenge.
|
||||
|
||||
```js
|
||||
const cap = new Cap({
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# Philosophy
|
||||
|
||||
Most CAPTCHA systems today are built around surveillance: tracking users, fingerprinting browsers, and collecting data in exchange for access. Cap rejects that model.
|
||||
|
||||
Cap is designed around a few core ideas:
|
||||
|
||||
- **Privacy by default**: no trackers, no third-party requests, no hidden profiling.
|
||||
- **Simplicity over complexity**: no image labeling, no click-the-fire-hydrant puzzles.
|
||||
- **Speed matters**: fast to render, fast to solve, fast to validate.
|
||||
- **User-first**: designed to be unintrusive and accessible, with invisible and floating modes.
|
||||
- **Self-hostable, not centralized**: you control the infra, tokens, and behavior.
|
||||
|
||||
By using proof-of-work instead of user behavior analysis, Cap shifts the burden from identity to computation. That means anyone can prove they're human without cookies, accounts, or friction.
|
||||
|
||||
Cap is what a CAPTCHA should've been all along: **simple, honest, and yours.**
|
||||
+78
-39
@@ -20,70 +20,107 @@ pnpm i @cap.js/server
|
||||
|
||||
## Getting started
|
||||
|
||||
The best way to use Cap is with **storage hooks** that connect to your database. Here's a simple example with SQLite:
|
||||
You'll need a database to store challenges and tokens. Here's an example using Bun's SQL module and a Postgres DB:
|
||||
|
||||
```js
|
||||
import Cap from "@cap.js/server";
|
||||
import { Database } from "bun:sqlite";
|
||||
import { SQL } from "bun";
|
||||
|
||||
const db = new Database("cap.db");
|
||||
const db = new SQL(`postgres://user:password@localhost:5432/dbname`);
|
||||
|
||||
db.exec(`
|
||||
await db`
|
||||
CREATE TABLE IF NOT EXISTS challenges (
|
||||
token TEXT PRIMARY KEY,
|
||||
data TEXT NOT NULL,
|
||||
expires INTEGER NOT NULL
|
||||
data JSONB NOT NULL,
|
||||
expires BIGINT NOT NULL
|
||||
);
|
||||
`;
|
||||
|
||||
await db`
|
||||
CREATE TABLE IF NOT EXISTS tokens (
|
||||
key TEXT PRIMARY KEY,
|
||||
expires INTEGER NOT NULL
|
||||
expires BIGINT NOT NULL
|
||||
);
|
||||
`);
|
||||
`;
|
||||
|
||||
const cap = new Cap({
|
||||
storage: {
|
||||
challenges: {
|
||||
store: async (token, challengeData) => {
|
||||
db.prepare(
|
||||
"INSERT OR REPLACE INTO challenges (token, data, expires) VALUES (?, ?, ?)"
|
||||
).run(token, JSON.stringify(challengeData), challengeData.expires);
|
||||
await db`
|
||||
INSERT INTO challenges (token, data, expires)
|
||||
VALUES (${token}, ${challengeData}, ${challengeData.expires})
|
||||
ON CONFLICT (token)
|
||||
DO UPDATE SET
|
||||
data = EXCLUDED.data,
|
||||
expires = EXCLUDED.expires
|
||||
`;
|
||||
},
|
||||
|
||||
read: async (token) => {
|
||||
const row = db
|
||||
.prepare(
|
||||
"SELECT data, expires FROM challenges WHERE token = ? AND expires > ?"
|
||||
)
|
||||
.get(token, Date.now());
|
||||
const [row] = await db`
|
||||
SELECT data, expires
|
||||
FROM challenges
|
||||
WHERE token = ${token}
|
||||
AND expires > ${Date.now()}
|
||||
LIMIT 1
|
||||
`;
|
||||
|
||||
return row
|
||||
? { challenge: JSON.parse(row.data), expires: row.expires }
|
||||
? { challenge: row.data, expires: Number(row.expires) }
|
||||
: null;
|
||||
},
|
||||
|
||||
delete: async (token) => {
|
||||
db.prepare("DELETE FROM challenges WHERE token = ?").run(token);
|
||||
await db`
|
||||
DELETE FROM challenges
|
||||
WHERE token = ${token}
|
||||
`;
|
||||
},
|
||||
|
||||
deleteExpired: async () => {
|
||||
db.prepare("DELETE FROM challenges WHERE expires <= ?").run(Date.now());
|
||||
await db`
|
||||
DELETE FROM challenges
|
||||
WHERE expires <= ${Date.now()}
|
||||
`;
|
||||
},
|
||||
},
|
||||
|
||||
tokens: {
|
||||
store: async (tokenKey, expires) => {
|
||||
db.prepare(
|
||||
"INSERT OR REPLACE INTO tokens (key, expires) VALUES (?, ?)"
|
||||
).run(tokenKey, expires);
|
||||
await db`
|
||||
INSERT INTO tokens (key, expires)
|
||||
VALUES (${tokenKey}, ${expires})
|
||||
ON CONFLICT (key)
|
||||
DO UPDATE SET
|
||||
expires = EXCLUDED.expires
|
||||
`;
|
||||
},
|
||||
get: async (tokenKey) => {
|
||||
const row = db
|
||||
.prepare("SELECT expires FROM tokens WHERE key = ? AND expires > ?")
|
||||
.get(tokenKey, Date.now());
|
||||
|
||||
return row ? row.expires : null;
|
||||
get: async (tokenKey) => {
|
||||
const [row] = await db`
|
||||
SELECT expires
|
||||
FROM tokens
|
||||
WHERE key = ${tokenKey}
|
||||
AND expires > ${Date.now()}
|
||||
LIMIT 1
|
||||
`;
|
||||
|
||||
return row ? Number(row.expires) : null;
|
||||
},
|
||||
|
||||
delete: async (tokenKey) => {
|
||||
db.prepare("DELETE FROM tokens WHERE key = ?").run(tokenKey);
|
||||
await db`
|
||||
DELETE FROM tokens
|
||||
WHERE key = ${tokenKey}
|
||||
`;
|
||||
},
|
||||
|
||||
deleteExpired: async () => {
|
||||
db.prepare("DELETE FROM tokens WHERE expires <= ?").run(Date.now());
|
||||
await db`
|
||||
DELETE FROM tokens
|
||||
WHERE expires <= ${Date.now()}
|
||||
`;
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -98,7 +135,7 @@ Now, you can connect this to your backend to expose the routes needed for the wi
|
||||
|
||||
```js [Elysia]
|
||||
import { Elysia } from "elysia";
|
||||
import cap from "...";
|
||||
import cap from "./cap.js";
|
||||
|
||||
new Elysia()
|
||||
.post("/cap/challenge", async () => {
|
||||
@@ -117,7 +154,7 @@ new Elysia()
|
||||
|
||||
```js [Express]
|
||||
import express from "express";
|
||||
import cap from "...";
|
||||
import cap from "./cap.js";
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
@@ -139,7 +176,7 @@ app.listen(3000);
|
||||
|
||||
```js [Fastify]
|
||||
import Fastify from "fastify";
|
||||
import cap from "...";
|
||||
import cap from "../cap.js";
|
||||
|
||||
const fastify = Fastify();
|
||||
|
||||
@@ -180,12 +217,6 @@ if (!success) throw new Error("invalid cap token");
|
||||
|
||||
```json
|
||||
{
|
||||
// used for json keyval storage. storage hooks are recommended instead
|
||||
"tokens_store_path": ".data/tokensList.json",
|
||||
|
||||
// disables all filesystem operations, usually used along editing the state. storage hooks are recommended instead
|
||||
"noFSState": false,
|
||||
|
||||
"disableAutoCleanup": false,
|
||||
|
||||
"storage": {
|
||||
@@ -206,7 +237,15 @@ if (!success) throw new Error("invalid cap token");
|
||||
"state": {
|
||||
"challengesList": {},
|
||||
"tokensList": {}
|
||||
}
|
||||
},
|
||||
|
||||
// deprecated:
|
||||
|
||||
// used for json keyval storage
|
||||
// "tokens_store_path": ".data/tokensList.json",
|
||||
|
||||
// disables all filesystem operations, usually used along editing the state
|
||||
// "noFSState": false,
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
+2
-12
@@ -1,4 +1,4 @@
|
||||
# @cap.js/solver
|
||||
# M2M
|
||||
|
||||
`@cap.js/solver` is a standalone library that can be used to solve Cap challenges from the server. It's extremely simple (no dependencies, one single file) yet as fast and efficient as the widget. Note that it can **only be used with Bun**. Node.js support is not planned.
|
||||
|
||||
@@ -55,14 +55,4 @@ The 2nd argument is optional but can always be provided. It's always an object.
|
||||
|
||||
- For **all challenge types**, `onProgress` can also be used to provide a callback for progress updates.
|
||||
|
||||
- For **seeded challenges only**, it is used to specify the number of solutions to generate, the size of the challenges, and the difficulty
|
||||
|
||||
## FAQ
|
||||
|
||||
### Why is this needed?
|
||||
|
||||
Mainly for M2M interactions, where you want to solve Cap challenges on the server without user interaction.
|
||||
|
||||
### Doesn't this defeat the purpose of Cap?
|
||||
|
||||
Not really. Server-side solving is a core use case of proof-of-work CAPTCHAs like Cap or altcha. It's about proving effort, not necessarily involving a human. [Learn more](./effectiveness.md)
|
||||
- For **seeded challenges only**, it is used to specify the number of solutions to generate, the size of the challenges, and the difficulty
|
||||
@@ -1,43 +1,50 @@
|
||||
# Cap Standalone
|
||||
|
||||
Cap Standalone is a self-hosted version of Cap's backend that allows you to spin up a server to validate and create challenges so you can use it with languages other than JS.
|
||||
|
||||
It's simple yet powerful, allowing you to use Cap in any language that can make HTTP requests. It's mostly compatible with reCAPTCHA and hCaptcha's siteverify enpoints, so you can use it as a drop-in replacement for them.
|
||||
|
||||
It also offers API key support, a built-in assets server, a dashboard with statistics, and more.
|
||||
|
||||

|
||||
Cap Standalone is the default way of self-hosting Cap's backend. It provides a simple HTTP API for the widget to use and a siteverify endpoint compatible with hCaptcha or reCAPTCHA's, along with the ability to use multiple site keys.
|
||||
|
||||
## Installation
|
||||
|
||||
### Requirements
|
||||
You'll need to have [Docker Engine 20.10 or higher](https://docs.docker.com/get-docker/) installed on your server.
|
||||
|
||||
You'll need to have [Docker Engine 20.10 or higher](https://docs.docker.com/get-docker/) installed on your server. Both `x86_64` (amd64) and `arm64` architectures are supported.
|
||||
Start by creating a docker compose file named `docker-compose.yml` with the following content:
|
||||
|
||||
---
|
||||
```yaml
|
||||
version: "3.8"
|
||||
|
||||
Run the following command to pull the Cap Standalone Docker image from Docker Hub:
|
||||
services:
|
||||
cap:
|
||||
image: tiago2/cap:latest
|
||||
container_name: cap
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
ADMIN_KEY: your_secret_password
|
||||
volumes:
|
||||
- cap-data:/usr/src/app/.data
|
||||
restart: unless-stopped
|
||||
|
||||
```bash
|
||||
docker pull tiago2/cap:latest
|
||||
volumes:
|
||||
cap-data:
|
||||
```
|
||||
|
||||
Then, to run the server, use the following command:
|
||||
::: tip Tips
|
||||
|
||||
* Make sure to add an admin key to log into the web UI. It should be at least 30 characters long.
|
||||
|
||||
* If your port 3000 is already in use, feel free to change it to something else.
|
||||
|
||||
* If you're having trouble accessing the dashboard, add `network_mode: "host"` under the Cap service.
|
||||
:::
|
||||
|
||||
Start the container:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
-p 3000:3000 \
|
||||
-v cap-data:/usr/src/app/.data \
|
||||
-e ADMIN_KEY=your_secret_password \
|
||||
--name cap-standalone \
|
||||
tiago2/cap:latest
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Make sure to replace `your_secret_password` with a strong password, as anyone with it will be able to log into the dashboard and create keys. It'll need to be at least 30 characters long.
|
||||
And access your web dashboard at `http://localhost:3000` or by accessing port 3000 on your machine.
|
||||
|
||||
Then, you can access the dashboard at `http://localhost:3000`, log in, and create a key. You'll get a site key and a secret key which you'll be able to use on your widget.
|
||||
|
||||
On Debian and other OSes that don't use `iptables`, if you can't open the dashboard, try setting `--network=host` in the run command. Thanks to [Boro Vukovic](https://github.com/tiagozip/cap/issues/70#issuecomment-3086464282) for letting me know about this.
|
||||
Log in with your admin key and create a new site key. Take note of both the site key and secret key.
|
||||
|
||||
You'll also need to make the server publicly accessible from the internet, as the widget needs to be able to reach it. If you're using a reverse proxy, make sure to check [the options guide](/guide/standalone/options.md) to configure rate-limiting properly.
|
||||
|
||||
@@ -45,7 +52,7 @@ You'll also need to make the server publicly accessible from the internet, as th
|
||||
|
||||
### Client-side
|
||||
|
||||
Let's configure your widget to use your self-hosted Cap Standalone server. To do this, set the widget's API endpoint option to:
|
||||
You'll need to configure your widget to use your self-hosted Cap Standalone server. To do this, set the widget's API endpoint option to:
|
||||
|
||||
```
|
||||
https://<instance_url>/<site_key>/
|
||||
@@ -89,7 +96,7 @@ curl "https://<instance_url>/<site_key>/siteverify" \
|
||||
-d '{ "secret": "<key_secret>", "response": "<captcha_token>" }'
|
||||
```
|
||||
|
||||
The response should look like this:
|
||||
A successful response will return:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -97,16 +104,8 @@ The response should look like this:
|
||||
}
|
||||
```
|
||||
|
||||
Or, if the captcha token is invalid or expired, it will return:
|
||||
::: tip Client-side library storage
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false
|
||||
}
|
||||
```
|
||||
Cap Standalone can also serve the widget and floating client-side library files. [Learn more](options.md#asset-server)
|
||||
|
||||
If `success` is true, you can proceed with your app logic.
|
||||
|
||||
### Client-side library storage
|
||||
|
||||
Cap Standalone can also serve the widget and floating client-side library files. [Learn more](options.md#asset-server).
|
||||
:::
|
||||
@@ -1,5 +1,7 @@
|
||||
# @cap.js/widget
|
||||
|
||||
[](https://www.jsdelivr.com/package/npm/@cap.js/widget)
|
||||
|
||||
`@cap.js/widget` is Cap's client-side library. It includes the `cap-widget` web component, the invisible mode and the CAPTCHA solver. First, add it to your client-side code:
|
||||
|
||||
::: code-group
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# How does cap work?
|
||||
|
||||
> [!NOTE] This is a more technical explanation of how Cap works. If you're looking for a more general overview, check out the [Effectiveness](./effectiveness.md) page.
|
||||
By the way, this is a more technical explanation of how Cap works. If you're looking for a more general overview, check out the [Effectiveness](./effectiveness.md) page.
|
||||
|
||||
***
|
||||
|
||||
1. When Cap is initialized, it automatically registers a custom element for the widget in the browser
|
||||
2. The widget creates a shadow DOM and appends all necessary elements to it
|
||||
|
||||
+1
-50
@@ -45,53 +45,4 @@ features:
|
||||
- icon: 🌳
|
||||
title: Open-source
|
||||
details: Completely free & open-source under the Apache 2.0 license
|
||||
---
|
||||
|
||||
## What is Cap?
|
||||
|
||||
Cap is the modern, self-hosted alternative to traditional CAPTCHAs that uses proof-of-work instead of annoying visual puzzles.
|
||||
|
||||
Cap is built with JavaScript and Rust and both the widget and server helper have zero dependencies.
|
||||
|
||||
You can either run it on any JavaScript runtime, or use the standalone mode with Docker.
|
||||
|
||||
---
|
||||
|
||||
Cap is built into 2 main parts. The [widget](https://capjs.js.org/guide/widget.html) is a small client-side JavaScript library using custom components and WASM that renders the CAPTCHA and solves the challenge.
|
||||
|
||||
The [Standalone Server](https://capjs.js.org/guide/standalone.html) is a Docker image that helps you use Cap with any language or framework. As an alternative, if your server-side uses JavaScript, you can use the lighter [server](https://capjs.js.org/guide/server.html) library.
|
||||
|
||||
Cap also has a [M2M library](https://capjs.js.org/guide/solver.html) that implements a custom proof-of-work solver for protecting API endpoints that you still want public.
|
||||
|
||||
## Why Cap?
|
||||
|
||||
- **250x smaller than hCaptcha**
|
||||
~20kb, zero dependencies, loads in milliseconds
|
||||
- **Privacy-first**
|
||||
Cap doesn't send any telemetry back to our servers
|
||||
- **Fully customizable**
|
||||
Change the colors, size, position, icons and more with CSS variables
|
||||
- **Proof-of-work**
|
||||
Your users no longer have to waste time solving visual puzzles.
|
||||
- **Standalone mode**
|
||||
Run Cap anywhere with a Docker container with analytics & more
|
||||
- **Invisible**
|
||||
Hide Cap's widget and solve challenges in the background
|
||||
- **M2M**
|
||||
Keep your APIs protected while accessible to friendly robots
|
||||
- **Open-source**
|
||||
Completely free & open-source under the Apache 2.0 license
|
||||
|
||||
It's ideal for protecting APIs from bots, preventing spam on forms, blocking automated signup attempts and securing free-tier abuse.
|
||||
|
||||
Cap is a great alternative to [reCAPTCHA](https://www.google.com/recaptcha/about/), [hCaptcha](https://www.hcaptcha.com/) and [Cloudflare Turnstile](https://developers.cloudflare.com/turnstile/). Check out the [feature comparison](https://capjs.js.org/guide/alternatives.html) to see how Cap compares to other CAPTCHAs.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the Apache-2.0 License, please see the [LICENSE](https://github.com/tiagozip/cap/blob/main/LICENSE) file for details.
|
||||
|
||||
Copyright ©2025 - present [tiago](https://tiago.zip)
|
||||
|
||||
---
|
||||
|
||||
[](https://www.bestpractices.dev/projects/9920) [](https://www.producthunt.com/posts/cap-5?embed=true&utm_source=badge-top-post-topic-badge&utm_medium=badge&utm_souce=badge-cap-5)
|
||||
---
|
||||
Reference in New Issue
Block a user