# Donjon License Server - Cloudflare Worker

Python-based Cloudflare Worker that serves as the storage and distribution layer
for Donjon 7.0 license management at `license.donjonsec.com`.

The worker handles license storage, public key distribution, revocation lists,
and online validation. Cryptographic signing (Dilithium + Ed25519) is performed
by the admin CLI tool -- this worker stores and serves pre-signed artifacts.

## Prerequisites

- Cloudflare account with Workers enabled
- Node.js 18+ (for Wrangler CLI)
- Wrangler CLI: `npm install -g wrangler`

## Setup

```bash
# 1. Authenticate with Cloudflare
wrangler login

# 2. Create the KV namespace
wrangler kv namespace create LICENSE_KV
# Note the namespace ID from the output

# 3. Update wrangler.toml with the KV namespace ID
# Replace REPLACE_WITH_KV_NAMESPACE_ID with the actual ID

# 4. Set the admin API key (use a strong random value)
wrangler kv key put --binding LICENSE_KV admin_api_key "YOUR-SECRET-ADMIN-KEY"

# 5. Upload public keys generated by donjon-license-admin.py keygen
# The keys should be a JSON object, for example:
#   {"dilithium": "<base64>", "ed25519": "<base64>"}
wrangler kv key put --binding LICENSE_KV public_keys '{"dilithium":"...","ed25519":"..."}'

# 6. Initialise an empty revocation list
wrangler kv key put --binding LICENSE_KV revocation_list '[]'
```

## Deploy

```bash
wrangler deploy
```

After deploying, the worker will be available at:
`https://donjon-license-server.<your-subdomain>.workers.dev`

## Custom Domain

After purchasing `donjonsec.com`:

1. Add the domain to your Cloudflare account
2. Navigate to **Workers & Pages > donjon-license-server > Settings > Triggers**
3. Under **Custom Domains**, click **Add Custom Domain**
4. Enter `license.donjonsec.com`
5. Cloudflare will automatically configure DNS and TLS

## API Reference

### Public Endpoints

| Method | Path                  | Description                          |
|--------|-----------------------|--------------------------------------|
| GET    | `/`                   | Health check and server metadata     |
| GET    | `/api/v1/public-keys` | Public keys for offline verification |
| GET    | `/api/v1/revoked`     | Current revocation list              |
| POST   | `/api/v1/validate`    | Online license validation            |
| GET    | `/api/v1/license/:id` | Download a stored license            |

### Admin Endpoints (require `X-Admin-Key` header)

| Method | Path                | Description                 |
|--------|---------------------|-----------------------------|
| POST   | `/api/v1/generate`  | Store a pre-signed license  |
| POST   | `/api/v1/revoke`    | Revoke a license by ID      |
| GET    | `/api/v1/stats`     | License issuance statistics  |

### Request/Response Examples

**Health Check:**
```bash
curl https://license.donjonsec.com/
```

**Get Public Keys:**
```bash
curl https://license.donjonsec.com/api/v1/public-keys
```

**Validate a License:**
```bash
curl -X POST https://license.donjonsec.com/api/v1/validate \
  -H "Content-Type: application/json" \
  -d '{"license_id": "abc123-def456"}'
```

**Store a License (Admin):**
```bash
curl -X POST https://license.donjonsec.com/api/v1/generate \
  -H "Content-Type: application/json" \
  -H "X-Admin-Key: YOUR-SECRET-ADMIN-KEY" \
  -d '{
    "license_id": "abc123-def456",
    "license_data": {
      "holder": "Acme Corp",
      "type": "enterprise",
      "expires": "2027-01-01T00:00:00Z",
      "signatures": {
        "dilithium": "...",
        "ed25519": "..."
      }
    }
  }'
```

**Revoke a License (Admin):**
```bash
curl -X POST https://license.donjonsec.com/api/v1/revoke \
  -H "Content-Type: application/json" \
  -H "X-Admin-Key: YOUR-SECRET-ADMIN-KEY" \
  -d '{"license_id": "abc123-def456", "reason": "refund"}'
```

**View Stats (Admin):**
```bash
curl https://license.donjonsec.com/api/v1/stats \
  -H "X-Admin-Key: YOUR-SECRET-ADMIN-KEY"
```

## KV Keys

| Key                     | Description                              |
|-------------------------|------------------------------------------|
| `admin_api_key`         | Secret key for admin endpoint auth       |
| `public_keys`           | JSON object with Dilithium/Ed25519 keys  |
| `revocation_list`       | JSON array of revoked license IDs        |
| `stats`                 | JSON object with issuance statistics     |
| `health_status`         | Last health check timestamp              |
| `license:<id>`          | Individual license data, keyed by ID     |
| `revocation:<id>`       | Revocation metadata for a specific ID    |

## Architecture Notes

- **No server-side signing**: The worker does not perform cryptographic signing.
  All licenses are signed by the admin CLI tool (`donjon-license-admin.py`) and
  uploaded pre-signed. This avoids Pyodide/WASM limitations with native crypto
  libraries.
- **Rate limiting**: Cloudflare's built-in rate limiting can be configured via
  the dashboard under Security > WAF > Rate Limiting Rules.
- **Cron health checks**: The worker runs a scheduled task every 6 hours to
  record a health status entry in KV.
