{"id":13550075,"url":"https://github.com/sebadob/spow","last_synced_at":"2025-09-10T01:50:42.730Z","repository":{"id":213257330,"uuid":"733449061","full_name":"sebadob/spow","owner":"sebadob","description":"Proof of Work for the Server and Client + WASM","archived":false,"fork":false,"pushed_at":"2025-05-06T19:34:40.000Z","size":267,"stargazers_count":18,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-06T20:35:18.588Z","etag":null,"topics":["hashcat","proof-of-work","rust","wasm"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sebadob.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-12-19T10:58:23.000Z","updated_at":"2025-05-06T19:34:40.000Z","dependencies_parsed_at":null,"dependency_job_id":"76fe4728-84a6-4870-890e-68d8271ea1d7","html_url":"https://github.com/sebadob/spow","commit_stats":null,"previous_names":["sebadob/spow"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/sebadob/spow","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebadob%2Fspow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebadob%2Fspow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebadob%2Fspow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebadob%2Fspow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sebadob","download_url":"https://codeload.github.com/sebadob/spow/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebadob%2Fspow/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274397773,"owners_count":25277400,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-09T02:00:10.223Z","response_time":80,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hashcat","proof-of-work","rust","wasm"],"created_at":"2024-08-01T12:01:28.682Z","updated_at":"2025-09-10T01:50:42.719Z","avatar_url":"https://github.com/sebadob.png","language":"Rust","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# spow - Proof of Work for Server and Client + WASM\n\n## Why?\n\nEveryone loves captcha's right? Well, not that much. They are absolutely annoying and often\neven humans cannot solve them, because something is just on the edge or the server on the\nother side simply thinks you're stupid and gives you another one.  \nThey provide an absolutely awful UX and the only reason they exist is to prevent spammers.\n\nProof of Work's (PoW) solve kind of the same task, but without any real impact on the UX, they\nwork differently. Instead of making the user solve a puzzle, they make your computer solve it\nfor you. The user has nothing else to do than wait a small amount of time until the puzzle\nhas been solved automatically.\n\nThe idea behind it is that it is just way too costly for any bots and spammers to do this\nfor a single action. If you are just a normal person using an App or a web page, you don't\nmind and will probably not even notice this small amount of work your computer has to do now\nand then, but if you are a spammer and are doing this on millions of Apps at the same time,\nit will simply not be possible.\n\nThere are really sophisticated solutions to this out there, like\n[Cloudflare Turnstile](https://www.cloudflare.com/products/turnstile/) which I really like.\nThis tiny crate however makes it possible to get the same UX while being able to easily\nself-host it and just integrate it into your API without any external dependencies. You don't\neven need to run another service on the side. You can either implement the PoW creation into\nyour Rust backend directly by using the `spow` crate, or since `v0.4.0` you can find a server\nside prebuilt WASM in `prebuilt/server/wasm`. The server WASM can not only solve, but also\ncreate and validate PoWs.\n\n## How?\n\nIn the backend, it could not be much simpler:\n\n1. At application startup, you need to call either `Pow::init()` or `Pow::init_random()` once.\n   This will initialize the secret which is used to sign the challenges, so you know if a challenge\n   actually came from your backend, or if someone else has just generated one himself.\n2. If you want to create a new challenge for a client at some point, you then only need to\n   create a new PoW with either `Pow::new()` or `Pow::with_difficulty()`. This will create\n   a random, unique PoW, which can be converted into a challenge for the client with either\n   `.build_challenge()` or `.to_string()`.\n3. Send the challenge string to the client\n4. The client needs to do some work to solve that puzzle. To do that, you can use a wasm\n   or JS-only version. These are in the `prebuilt/browser/` folder and can be used directly in any\n   frontend. If you do not have a really good reason not to, you should always use the wasm\n   version, because it is up to ~20 times faster than JS only. This means you can use higher\n   difficulties with no impact on the UX.\n5. When you get back a solved puzzle from a client, just `Pow::validate()` it. This will ...\n    - validate the version tag\n    - validate the difficulty (must be 10 - 99)\n    - validate the expiry of the PoW (set during `Pow::new()`)\n    - validate that the challenge actually came from the same backend and has not been changed\n    - validate the puzzle result itself using the given difficulty\n\n... that's it.\n\nUnder the hood, `spow` uses a modified, extended version of the `Hashcat` PoW algorithm,\nwhich is being used for instance for the Bitcoin blockchain as well.\n\n## Difficulty\n\nThe difficulty is an int between 10 and 99. The default is `20`.  \nWith each step, the time to solve the puzzle will increase exponentially, while the time to\nvalidate it in the backend will always stay the same and is independent of the difficulty.\n\nHow high you want to set this greatly depends on your use case, where your app will be running\n(powerful desktop machines, laptops, or an app on some smartphone, ...) and how much you want\nto simply make the user wait.\n\nThe way these puzzles work is, you can get very lucky and solve it quickly, or have bad luck\nand need way longer than the median value. All of this depends on the random values of the\nPoW during creation. To find a good value, you should test a lot of different PoW's preferably\non your target system to find a good median time.  \nThis project has a bin target which does exactly that, but actually in the backend. You can\nrun it with for instance `cargo run --release 20 100` and it will solve 100 randomly generated\npuzzles with difficulty 20, and at the end print out the median time. Please keep in mind though\nthat the backend code is much more performant than the one inside the browser. In first tests,\nthe wasm version in the browser needs roughly 2.5 - 3 times the amount of time, that is needed\nin the backend in optimized, pure rust code. The JS version is ~20 times slower than wasm.\n\nThe `examples` folder contains a tiny frontend demo using Svelte. It uses a hardcoded\nchallenge though, and it only shows how to solve a puzzle at this time. It does not provide\na good indicator for a median time needed to solve puzzles with these difficulties.\n\n## WASM `getrandom` Backend\n\nIf you compile to WASM, you need to specify the `getrandom` backend to use. To do this, create `.cargo/config.toml` and\npaste:\n\n```toml\n[target.wasm32-unknown-unknown]\nrustflags = ['--cfg', 'getrandom_backend=\"wasm_js\"']\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsebadob%2Fspow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsebadob%2Fspow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsebadob%2Fspow/lists"}