Metadata-Version: 2.4
Name: brsxkeys
Version: 0.1.0
Summary: Self-service API key management with tiered rate limiting and an admin dashboard (FastAPI based)
Author: BRSX-Labs
License: MIT
Project-URL: Homepage, https://github.com/BRSX-Labs/brsxkeys
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: fastapi>=0.110
Requires-Dist: uvicorn>=0.29
Requires-Dist: python-multipart>=0.0.9

# brsxkeys

Self-service API key management: users generate their own API keys,
choose a usage tier, and get rate-limited automatically. Comes with a
built-in dashboard (user side + admin panel) and a `require_key`
dependency you can drop into any other FastAPI project. FastAPI based.

> ⚠️ **Security note:** Like `brsxmail`, this package does not provide
> production-grade security on its own. Passwords are hashed with
> plain `sha256` (no salt), sessions are kept in RAM (lost on restart,
> not shared across workers), and there's no CORS/brute-force
> protection beyond the per-key rate limiting described below. This
> package **is designed to be used together with BRSX-Labs' `zerov4`
> security middleware** for anything internet-facing or with real
> users. See "Using it together with zerov4" below.

## Installation

```bash
pip install brsxkeys
```

or clone this repo and:

```bash
pip install -e .
```

## Two ways to use this package

**1. As a full dashboard** (self-service key generation, usage stats,
admin panel):

```python
from brsxkeys import keys
keys.run()
```

**2. As a dependency in your own separate FastAPI project** (just the
key-checking logic, no dashboard):

```python
from brsxkeys import keys
from fastapi import Depends

@app.get("/private", dependencies=[Depends(keys.require_key)])
def private():
    return {"msg": "only visible with a valid API key"}
```

Both read/write the **same** config and storage, so keys created via
the dashboard are immediately usable by `require_key` in your other
project (as long as they point at the same `data_dir`).

## Running the dashboard

### Quick start

```bash
python run.py
```

- On first run, if `brsxkeys.config.json` doesn't exist, an interactive
  setup wizard opens in the terminal. It asks for:
  - domain (for user email registration)
  - port, host
  - storage type (json/sqlite)
  - data folder name
  - **tier definitions** — you name each tier (e.g. `free`, `pro`) and
    set its request limit and time window (e.g. 100 requests / 86400
    seconds = 1 day); add as many tiers as you want, empty input to finish
  - interface preference for the user side (built-in vs. your own HTML)
  - **admin account** (email + password) for the `/panel` dashboard
- On subsequent runs, the wizard isn't asked again — the server starts
  directly.
- To reset settings:
  ```bash
  python run.py --reconfigure
  ```

### Via CLI after pip install

```bash
brsxkeys
brsxkeys --reconfigure
```

### The simplest usage

```python
from brsxkeys import keys
keys.run()
```

### From code (advanced)

```python
from brsxkeys import create_app, get_or_create_config
import uvicorn

config = get_or_create_config()
app = create_app(config)
uvicorn.run(app, host=config["host"], port=config["port"])
```

## The two sides of the dashboard

### `/` — user side

- Register / login (same domain-restricted email system as `brsxmail`)
- Pick a tier and generate your own API key (self-service — no admin
  approval needed)
- See all your own keys, their tier, active/inactive status, and usage
  (`used/limit`, reset window)
- Revoke your own keys

This side **can be customized**: place your own `index.html` in the
folder where you run the server, and choose "I'll use my own index.html"
in the wizard. Same mechanism as `brsxmail` — if the file is missing,
a clear warning is printed to the terminal and it falls back to the
built-in interface.

### `/panel` — admin side

- Login with the admin account created during setup (a real account
  with `role: "admin"`, stored the same way as regular users, just
  flagged differently)
- View tier definitions
- View **every** key across **every** user, with the same usage stats
  the user side shows, plus the owner's email
- Revoke any key, regardless of owner

`/panel` is **always** the built-in interface — it is never affected
by `use_custom_html`. Only `/` can be customized.

## Writing your own interface for "/"

The server reads your `index.html` and fills in these placeholders:

- `{{LOGGED_IN}}` → `"true"` / `"false"`
- `{{USER}}` → the logged-in user's email (empty if not logged in)
- `{{DOMAIN}}` → the domain from config (e.g. `@brsx.com`)
- `{{TIERS}}` → comma-separated list of tier names (e.g. `free,pro`)

## API contract (dashboard endpoints)

**General rule:** All `POST` endpoints expect **form-data**. All
responses are JSON. Session is a cookie (`session_id`) set after login.

| Method | Path                    | Body        | Fields              | Auth needed      | Returns                     |
|--------|-------------------------|-------------|----------------------|------------------|-------------------------------|
| GET    | `/`                     | —           | —                    | —                | HTML                          |
| POST   | `/register`             | form-data   | `email`, `password`  | —                | `{ok}` / `{error}`             |
| POST   | `/login`                | form-data   | `email`, `password`  | —                | `{ok, role}` / `{error}`       |
| POST   | `/logout`               | —           | —                    | —                | `{ok}`                         |
| GET    | `/tiers`                | —           | —                    | —                | tier definitions (dict)        |
| POST   | `/keys/create`          | form-data   | `tier`               | user login       | `{ok, key}`                    |
| GET    | `/keys`                 | —           | —                    | user login       | list of your own keys w/ usage |
| DELETE | `/keys/{key}`           | —           | —                    | user login       | `{ok}` / `{error}`             |
| GET    | `/panel`                | —           | —                    | —                | HTML (admin login form)        |
| GET    | `/panel/keys`           | —           | —                    | admin login      | list of ALL keys w/ usage      |
| DELETE | `/panel/keys/{key}`     | —           | —                    | admin login      | `{ok}` / `{error}`             |
| GET    | `/panel/tiers`          | —           | —                    | admin login      | tier definitions (dict)        |

Each key object looks like:
```json
{
  "key": "bxk_...",
  "owner": "dev@your-domain.com",
  "tier": "free",
  "active": true,
  "created_at": 1785082921.9,
  "usage_count": 3,
  "limit": 100,
  "window_seconds": 86400
}
```

For a working example, check the bundled `brsxkeys/webui/index.html`
and `brsxkeys/webui/panel.html` — real, working JS examples of all
these calls.

## `require_key`: protecting endpoints in your own project

```python
from brsxkeys import keys
from fastapi import Depends

@app.get("/private", dependencies=[Depends(keys.require_key)])
def private():
    ...

# or, if you need to know the caller's tier/owner inside the endpoint:
@app.get("/private")
def private(key_info: dict = Depends(keys.require_key)):
    return {"tier": key_info["tier"], "owner": key_info["owner"]}
```

The key can be sent either way — both are supported:

```bash
curl -H "X-API-Key: bxk_..." http://localhost:8000/private
curl "http://localhost:8000/private?api_key=bxk_..."
```

Behavior:
- No key sent → `401 Missing API key`
- Invalid key → `401 Invalid API key`
- Key exists but inactive/revoked → `403 API key is inactive or revoked`
- Quota exceeded for the current window → `429`, and **the key is
  deactivated** until you manually reactivate it or it's naturally
  reset (the usage window resets automatically the next time the key
  is checked after `window_seconds` has elapsed)

`require_key` reads config/storage lazily on first use — importing
`brsxkeys` doesn't trigger the setup wizard by itself; it only runs
when a protected endpoint is actually hit for the first time.

## Using it together with zerov4

Same pattern as `brsxmail`. Use `blocking=False` to get the FastAPI
app without starting uvicorn, then hand it off to `zerov4`:

```python
# main.py
from brsxkeys import keys
from zerov4 import arx

app = keys.run(blocking=False)   # only creates the dashboard app
arx.run(app)                      # zerov4 wraps it and starts the server
```

This protects the dashboard itself (registration, login, key
creation) with `zerov4`'s bot/brute-force/session-hijacking defenses.
Note this is separate from `require_key`, which protects endpoints in
some *other* app of yours that consumes brsxkeys as a library.

## Storage

Default: JSON file based (`data_dir/users.json`, `data_dir/keys.json`,
`data_dir/usage.json`). If `sqlite` is chosen in the setup wizard, a
single `brsxkeys.db` is used instead. Both backends implement the same
interface.

## Notes

- Rate limiting is per-key, per-tier, using a fixed time window (not
  a sliding window): once `window_seconds` has elapsed since the
  window started, usage resets to 0 on the next check.
- When a key exhausts its quota mid-window, it is deactivated
  (`active: false`) rather than just throttled; the user (or admin)
  can see this reflected in the dashboard.
- Domain checking defaults to `@example.com`, changeable in the setup
  wizard.
