Skip to content

Commit 7b4c735

Browse files
committed
Chore: Removed token bucket reference and added new topics to docs tutorial section
1 parent a810229 commit 7b4c735

24 files changed

Lines changed: 947 additions & 51 deletions

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
---
1212

13+
> [!NOTE]
14+
> **Formerly `api-shield`:** `waygate` is the new name for `api-shield`. Replace `pip install api-shield` with `pip install waygate`. The import root changes from `shield` to `waygate` and the CLI command from `shield` to `waygate`.
15+
1316
> [!WARNING]
1417
> **Early Access:** `waygate` is fully functional and ready to use. We're actively building on it and real-world feedback is invaluable. If you have feedback, feature ideas, or suggestions, [open an issue](https://github.com/Attakay78/waygate/issues).
1518

docs/changelog.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66

77
---
88

9+
## [0.1.3]
10+
11+
### Added
12+
13+
- New tutorial sections: [Route Control](tutorial/route-control.md), [Rollouts](tutorial/rollouts.md), [Webhooks](tutorial/webhooks.md), [Audit Log](tutorial/audit-log.md), and [Custom Responses](tutorial/custom-responses.md).
14+
15+
### Removed
16+
17+
- Removed reference to token bucket algorithm, will add real implementation in coming release.
18+
19+
---
20+
921
## [0.1.0]
1022

1123
### Changed
@@ -94,7 +106,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
94106

95107
### Added
96108

97-
- **Rate limiting** (`@rate_limit`): per-IP, per-user, per-API-key, and global counters with fixed/sliding/moving window and token bucket algorithms. Supports burst allowance, tiered limits (`{"free": "10/min", "pro": "100/min"}`), exempt IPs/roles, and custom `on_missing_key` behaviour. Works as both a decorator and a `Depends()` dependency. Responses include `X-RateLimit-Limit/Remaining/Reset` and `Retry-After` headers. Requires `waygate[rate-limit]`.
109+
- **Rate limiting** (`@rate_limit`): per-IP, per-user, per-API-key, and global counters with fixed/sliding/moving window algorithms. Supports burst allowance, tiered limits (`{"free": "10/min", "pro": "100/min"}`), exempt IPs/roles, and custom `on_missing_key` behaviour. Works as both a decorator and a `Depends()` dependency. Responses include `X-RateLimit-Limit/Remaining/Reset` and `Retry-After` headers. Requires `waygate[rate-limit]`.
98110
- **Rate limit custom responses**: `response=` on `@rate_limit` and `responses["rate_limited"]` on `WaygateMiddleware` for replacing the default 429 JSON body with any Starlette `Response`.
99111
- **Rate limit dashboard**: `/waygate/rate-limits` tab showing registered policies with reset/edit/delete actions; `/waygate/blocked` page for the blocked requests log. Policies can also be managed via the `waygate rl` CLI commands (`list`, `set`, `reset`, `delete`, `hits`).
100112
- **Rate limit audit log**: policy changes (`set`, `update`, `reset`, `delete`) are recorded in the audit log alongside route state changes, with coloured action badges in the dashboard.

docs/index.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ hide:
1818
Feature flags & OpenFeature are here
1919
</a>
2020

21+
<p class="hp-formerly-note">
22+
Formerly known as <strong>api-shield</strong>, same project, new name.
23+
</p>
24+
2125
<h1 class="hp-h1">
2226
Toggle features,<br>control your <span class="hp-accent">API</span> at runtime
2327
</h1>

docs/reference/cli.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ waygate rl set GET:/search 10/minute --key global
393393

394394
| Option | Description |
395395
|---|---|
396-
| `--algorithm TEXT` | Counting algorithm: `fixed_window`, `sliding_window`, `moving_window`, `token_bucket` |
396+
| `--algorithm TEXT` | Counting algorithm: `fixed_window`, `sliding_window`, `moving_window` |
397397
| `--key TEXT` | Key strategy: `ip`, `user`, `api_key`, `global` |
398398

399399
---
@@ -472,7 +472,7 @@ waygate grl set 2000/hour --burst 50 --exempt /health --exempt GET:/metrics
472472

473473
| Option | Description |
474474
|---|---|
475-
| `--algorithm TEXT` | Counting algorithm: `fixed_window`, `sliding_window`, `moving_window`, `token_bucket` |
475+
| `--algorithm TEXT` | Counting algorithm: `fixed_window`, `sliding_window`, `moving_window` |
476476
| `--key TEXT` | Key strategy: `ip`, `user`, `api_key`, `global` |
477477
| `--burst INT` | Extra requests above the base limit |
478478
| `--exempt TEXT` | Exempt route (repeatable). Bare path (`/health`) or method-prefixed (`GET:/metrics`) |
@@ -558,7 +558,7 @@ waygate srl set payments-service 2000/hour --burst 50 --exempt /health --exempt
558558

559559
| Option | Description |
560560
|---|---|
561-
| `--algorithm TEXT` | Counting algorithm: `fixed_window`, `sliding_window`, `moving_window`, `token_bucket` |
561+
| `--algorithm TEXT` | Counting algorithm: `fixed_window`, `sliding_window`, `moving_window` |
562562
| `--key TEXT` | Key strategy: `ip`, `user`, `api_key`, `global` |
563563
| `--burst INT` | Extra requests above the base limit |
564564
| `--exempt TEXT` | Exempt route (repeatable). Bare path (`/health`) or method-prefixed (`GET:/metrics`) |

docs/reference/rate-limiting.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def rate_limit(
4040
| Parameter | Type | Default | Description |
4141
|---|---|---|---|
4242
| `limit` | `str \| dict` | required | Limit string (`"100/minute"`) or tier dict (`{"free": "10/min", "pro": "100/min"}`) |
43-
| `algorithm` | `str` | `"fixed_window"` | Counting algorithm. One of: `fixed_window`, `sliding_window`, `moving_window`, `token_bucket` |
43+
| `algorithm` | `str` | `"fixed_window"` | Counting algorithm. One of: `fixed_window`, `sliding_window`, `moving_window` |
4444
| `key` | `str \| callable` | `"ip"` | Key strategy. One of: `"ip"`, `"user"`, `"api_key"`, `"global"`, or a sync/async callable `(Request) -> str \| None` |
4545
| `on_missing_key` | `str \| None` | strategy default | Behaviour when the key extractor returns `None`. One of: `"exempt"`, `"fallback_ip"`, `"block"` |
4646
| `burst` | `int` | `0` | Extra requests allowed above `limit` (additive) |
@@ -123,7 +123,6 @@ Controls how requests are counted within a window.
123123
| `FIXED_WINDOW` | Fixed time buckets. Simple and predictable. The default. Allows boundary bursts (up to 2x in the worst case). |
124124
| `SLIDING_WINDOW` | Blends two adjacent fixed-window counters. Smooths boundary bursts. Not suitable for small limits like `5/minute` where gradual re-allow looks like intermittent blocking. |
125125
| `MOVING_WINDOW` | Timestamps every individual request. Most accurate; highest memory. |
126-
| `TOKEN_BUCKET` | Tokens accumulate over time up to a cap. Good for controlled bursts with a sustained average rate. Currently mapped to `MOVING_WINDOW` — a native implementation will be used when available from the `limits` library. |
127126

128127
---
129128

@@ -607,7 +606,7 @@ waygate rl set GET:/search 10/minute --key global
607606

608607
| Option | Description |
609608
|---|---|
610-
| `--algorithm TEXT` | Counting algorithm: `fixed_window`, `sliding_window`, `moving_window`, `token_bucket` |
609+
| `--algorithm TEXT` | Counting algorithm: `fixed_window`, `sliding_window`, `moving_window` |
611610
| `--key TEXT` | Key strategy: `ip`, `user`, `api_key`, `global` |
612611

613612
---
@@ -682,7 +681,7 @@ waygate grl set 2000/hour --burst 50 --exempt /health --exempt GET:/metrics
682681

683682
| Option | Description |
684683
|---|---|
685-
| `--algorithm TEXT` | Counting algorithm: `fixed_window`, `sliding_window`, `moving_window`, `token_bucket` |
684+
| `--algorithm TEXT` | Counting algorithm: `fixed_window`, `sliding_window`, `moving_window` |
686685
| `--key TEXT` | Key strategy: `ip`, `user`, `api_key`, `global` |
687686
| `--burst INT` | Extra requests above the base limit |
688687
| `--exempt TEXT` | Exempt route (repeatable). Bare path or `METHOD:/path` |
@@ -768,7 +767,7 @@ waygate srl set payments-service 2000/hour --burst 50 --exempt /health --exempt
768767

769768
| Option | Description |
770769
|---|---|
771-
| `--algorithm TEXT` | Counting algorithm: `fixed_window`, `sliding_window`, `moving_window`, `token_bucket` |
770+
| `--algorithm TEXT` | Counting algorithm: `fixed_window`, `sliding_window`, `moving_window` |
772771
| `--key TEXT` | Key strategy: `ip`, `user`, `api_key`, `global` |
773772
| `--burst INT` | Extra requests above the base limit |
774773
| `--exempt TEXT` | Exempt route (repeatable). Bare path (`/health`) or method-prefixed (`GET:/metrics`) |

docs/stylesheets/extra.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,12 @@ body:has(.hp-page) .md-content__inner {
317317
font-size: 1.05rem; line-height: 1.7;
318318
color: var(--hero-sub); margin: 0;
319319
}
320+
.hp-formerly-note {
321+
font-size: 0.8rem; color: var(--hero-sub); opacity: 0.75;
322+
margin: 0; margin-top: 2px;
323+
}
324+
.hp-formerly-note a { color: var(--ac); text-decoration: none; }
325+
.hp-formerly-note a:hover { text-decoration: underline; }
320326
.hp-hero-btns {
321327
display: flex; gap: var(--s3); flex-wrap: wrap;
322328
}

docs/tutorial/audit-log.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Audit Log
2+
3+
The audit log records every state change: who made it, when, what route was affected, and the before/after status.
4+
5+
---
6+
7+
## What gets recorded
8+
9+
An entry is written whenever:
10+
11+
- A route's status changes (maintenance on/off, enable, disable, env-gate)
12+
- A global maintenance window is activated or deactivated
13+
- A rate limit policy is created, updated, or deleted
14+
- A feature flag is created, updated, or deleted
15+
- A segment is created, updated, or deleted
16+
17+
Each entry captures:
18+
19+
| Field | Description |
20+
|---|---|
21+
| `path` | The route key, e.g. `GET:/payments` |
22+
| `action` | What happened, e.g. `maintenance_on`, `enable`, `disable` |
23+
| `actor` | Who made the change (`"system"` for decorator-driven changes, or a username from the CLI or dashboard) |
24+
| `platform` | Where the change came from (`"cli"`, `"dashboard"`, `"api"`, or `""`) |
25+
| `old_status` | Route status before the change |
26+
| `new_status` | Route status after the change |
27+
| `reason` | The reason string, if one was provided |
28+
| `timestamp` | UTC timestamp of the change |
29+
30+
---
31+
32+
## Viewing the audit log
33+
34+
### Dashboard
35+
36+
Open the admin dashboard and click the **Audit** tab. Filter by route path and scroll through the history. Each row shows the actor, platform, action, and timestamp.
37+
38+
### CLI
39+
40+
```bash
41+
# Last 20 entries across all routes
42+
waygate audit
43+
44+
# Filter to a specific route
45+
waygate audit GET:/payments
46+
47+
# Increase the limit
48+
waygate audit --limit 100
49+
```
50+
51+
### Engine API
52+
53+
```python
54+
# All entries (last 100 by default)
55+
entries = await engine.get_audit_log()
56+
57+
# Filter to a specific route
58+
entries = await engine.get_audit_log(path="GET:/payments")
59+
60+
# Increase the limit
61+
entries = await engine.get_audit_log(limit=500)
62+
63+
for entry in entries:
64+
print(entry.timestamp, entry.actor, entry.action, entry.path)
65+
```
66+
67+
---
68+
69+
## Reading an entry
70+
71+
```python
72+
from waygate.core.models import AuditEntry
73+
74+
entries = await engine.get_audit_log(path="GET:/payments")
75+
entry: AuditEntry = entries[0]
76+
77+
print(entry.path) # "GET:/payments"
78+
print(entry.action) # "maintenance_on"
79+
print(entry.actor) # "admin"
80+
print(entry.platform) # "dashboard"
81+
print(entry.old_status) # "active"
82+
print(entry.new_status) # "maintenance"
83+
print(entry.reason) # "Database migration"
84+
print(entry.timestamp) # datetime(2025, 6, 1, 3, 0, tzinfo=UTC)
85+
```
86+
87+
---
88+
89+
## Suppressing audit entries
90+
91+
Pass `audit=False` to suppress entries for programmatic changes at startup, such as seeding flags or registering routes:
92+
93+
```python
94+
@asynccontextmanager
95+
async def lifespan(_):
96+
await engine.save_flag(FeatureFlag(key="new-checkout", ...), audit=False)
97+
await engine.save_segment(Segment(key="beta-users", ...), audit=False)
98+
yield
99+
```
100+
101+
Changes made through the dashboard, CLI, or REST API always create audit entries.
102+
103+
---
104+
105+
## Storage
106+
107+
Audit entries are stored in the active backend alongside route state.
108+
109+
| Backend | Audit storage | Notes |
110+
|---|---|---|
111+
| `MemoryBackend` | In-process list | Lost on restart |
112+
| `FileBackend` | Appended to the state file | Survives restarts |
113+
| `RedisBackend` | Redis list | Shared across all workers |
114+
115+
For long-term retention, export entries periodically to your own datastore.
116+
117+
---
118+
119+
## Next step
120+
121+
[**Tutorial: Admin Dashboard**](admin-dashboard.md)

0 commit comments

Comments
 (0)