diff --git a/docs/chapter-16.rst b/docs/chapter-16.rst
index 5880ae828..0c32c323a 100644
--- a/docs/chapter-16.rst
+++ b/docs/chapter-16.rst
@@ -816,7 +816,6 @@ You need to style the tags. For example:
line-height: 1.2em;
margin: 2px;
cursor: pointer;
- opacity: 0.2;
text-transform: capitalize;
}
ul.tags-list li[data-selected=true] {
@@ -873,3 +872,212 @@ but the entire page will be redirected.
The contents of the component html can contain `` and they can modify global page variables
as well as modify other components.
+
+.. _altcha_captcha:
+
+Adding a Captcha Solution with Altcha
+-------------------------------------
+
+This section provides a simple captcha implementation for your py4web applications using the **Altcha** library. While not exhaustively tested, it serves as a practical example for integrating a robust, client-side captcha solution.
+More information in https://altcha.org
+
+Prerequisites
+^^^^^^^^^^^^^
+
+First, you need to install the Altcha library. You can do this using pip:
+
+.. code-block:: bash
+
+ python3 -m pip install --upgrade altcha
+
+You also need a secret key for HMAC verification. It's recommended to store this in your application's settings. For this example, we'll assume you have a file like ``.settings.py`` with the following variable:
+
+.. code-block:: python
+
+ # .settings.py
+ ALTCHA_HMAC_KEY = "your-very-secret-key-here"
+
+Controller Logic
+^^^^^^^^^^^^^^^^
+
+Next, you need to add the necessary actions to your controller file. The following code provides two actions: one to generate the captcha challenge (``altcha``) and another to handle a form with the captcha (``some_form``).
+
+.. code-block:: python
+
+ # controllers/default.py
+ from altcha import (
+ create_challenge,
+ verify_solution,
+ ChallengeOptions,
+ )
+ from py4web import action, response, request, URL, Field, flash, Form
+ from py4web.utils.form import XML, T
+ from .settings import ALTCHA_HMAC_KEY
+
+ @action("altcha", method=["GET"])
+ def get_altcha():
+ """Generates and returns an Altcha challenge."""
+ try:
+ challenge = create_challenge(
+ ChallengeOptions(
+ hmac_key=ALTCHA_HMAC_KEY,
+ max_number=50000,
+ )
+ )
+ response.headers["Content-Type"] = "application/json"
+ return challenge.__dict__
+ except Exception as e:
+ response.status = 500
+ return {"error": f"Failed to create challenge: {str(e)}"}
+
+ @action.uses("form_altcha.html", session, flash)
+ def some_form():
+ """An example form that uses the Altcha captcha."""
+ fields = [
+ Field("name", requires=IS_NOT_EMPTY()),
+ Field("color", type="string", requires=IS_IN_SET(["red", "blue", "green"])),
+ ]
+ form = Form(fields,
+ csrf_session=session,
+ submit_button=T("Submit"))
+
+ # Insert the Altcha widget HTML before the submit button
+ form.structure.insert(-1, XML('