> ## Documentation Index
> Fetch the complete documentation index at: https://developers.techwolf.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Security

## Signature verification (Ed25519)

Webhook authenticity and integrity are ensured using **Ed25519** asymmetric
signatures.

### Key material from TechWolf

For each webhook endpoint, TechWolf provides:

* **Public key (Ed25519)** - used by you to verify webhook signatures

Private keys are held only by TechWolf and securely managed.

### Signed message format

The signature is computed over:

```
timestamp:tenant:event_id:request_body
```

The result is sent in the `X-Signature-V1` header:

```
X-Signature-V1: <hex-signature-1>,<hex-signature-2>
```

TechWolf supports non-breaking key rotation. During a rotation window, the same
request may be signed with multiple active private keys, so `X-Signature-V1` can
contain several signatures. **Validate the webhook by verifying that at least
one signature matches** using any of the currently active public keys. This
allows key updates without downtime.

### Security responsibilities

You **must**:

* Verify the Ed25519 signature using the provided public key
* Validate timestamp freshness to prevent replay attacks (e.g. reject requests
  where the `X-Signature-Timestamp` is older than 5 minutes)
* Reject invalid or stale requests with a **401** response

***

## Python example: signature verification

```python theme={null}
from nacl.signing import VerifyKey
from nacl.exceptions import BadSignatureError

# Public key provided by TechWolf (hex-encoded)
PUBLIC_KEY = "TECHWOLF_PUBLIC_KEY_HEX"

verify_key = VerifyKey(bytes.fromhex(PUBLIC_KEY))

signatures = request.headers["X-Signature-V1"]
timestamp = request.headers["X-Signature-Timestamp"]
tenant = request.headers["X-Tenant"]
event_id = request.headers["X-Event-Id"]
body = request.data.decode("utf-8")

# TechWolf signature payload format:
# <timestamp>:<tenant>:<event_id>:<request_body>
message = f"{timestamp}:{tenant}:{event_id}:{body}".encode("utf-8")

# X-Signature-V1 may contain multiple comma-separated signatures.
# The request is valid if at least one signature matches.
for signature in signatures.split(","):
    try:
        verify_key.verify(
            message,
            bytes.fromhex(signature.strip()),
        )
        break
    except BadSignatureError:
        continue
else:
    abort(401, "invalid request signature")
```

***

## OAuth2 endpoint authentication (optional)

In addition to signature verification, TechWolf can authenticate webhook
requests to your endpoint using **OAuth2**. This protects against unauthorised
callers reaching your endpoint. OAuth2 is **optional** and does **not** replace
signature verification; authenticity and integrity are always guaranteed by
Ed25519.

### Request format

When OAuth2 is enabled, TechWolf sends an access token in the **Authorization**
header:

```
Authorization: Bearer <access_token>
```

### Enabling OAuth2

To enable OAuth2 for webhook delivery, you must provide:

| Item                                | Description                                                                                                                             |
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **OAuth2 Token URL**                | Your token endpoint                                                                                                                     |
| **Grant type**                      | `client_credentials`                                                                                                                    |
| **Client credentials for TechWolf** | `client_id`, `client_secret`                                                                                                            |
| **Scope**                           | As required by your endpoint                                                                                                            |
| **Additional parameters**           | Any other fields your token endpoint expects (e.g. `audience`, `resource`, or custom parameters). Provide name and value per parameter. |

### Client authentication method

When TechWolf requests an access token from your token endpoint (client
credentials grant), the `client_id` and `client_secret` can be sent in one of
two ways:

| Method                  | Description                                                                             |
| ----------------------- | --------------------------------------------------------------------------------------- |
| **Form-encoded (POST)** | `client_id` and `client_secret` in the POST body (`application/x-www-form-urlencoded`). |
| **HTTP Basic (POST)**   | `client_id` and `client_secret` in the `Authorization` header (Basic auth).             |

## Custom headers (optional)

You can configure **custom headers** that TechWolf will send with every webhook
request to your endpoint (e.g. for routing or internal auth). The standard
headers (including signature headers) are always sent; custom headers are added
on top.
