tideway 0.7.21

A batteries-included Rust web framework built on Axum for building SaaS applications quickly
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Release owners: copy a short DX gate summary into the release notes and use `docs/dx_metrics_snapshot.md` as the source of record for DX reporting.

## [0.7.21] - 2026-07-11

### Security

- Access-token verification now rejects refresh tokens and enforces issuer/audience checks in generated auth paths.
- HS256 issuer and verifier setup now provides checked constructors that reject short and known-placeholder secrets; unchecked constructors are deprecated.
- Credentialed CORS configurations now reject wildcard origins and headers.
- Request logging redacts sensitive headers, query parameters, and structured body fields, and bounds body preview work.
- Generated billing webhooks claim events atomically before side effects and release retryable claims safely.
- Organization invitations are claimed atomically and cannot be consumed more than once.

### Fixed

- Per-IP rate limiting no longer trusts forwarded headers by default and uses a shared fallback bucket when client IP data is unavailable.
- In-memory job dequeue/retry coordination and WebSocket broadcasts avoid lock contention and stale membership cleanup races.
- Generated billing redirects are validated and billing route access boundaries are explicit.
- CLI dependency editing returns actionable errors for malformed `Cargo.toml` tables and feature arrays instead of panicking.

### Added

- Hot-path Criterion benchmarks and a manual-only end-to-end HTTP/WebSocket load-test workflow with artifact upload.
- Dependency auditing, formatting, all-feature Clippy, and MSRV checks in CI.
- Immutable SHA pinning and least-privilege permissions for GitHub Actions workflows.

### Changed

- Minimum supported Rust version is now 1.88 so the dependency graph includes the security-fixed `time` release.
- `tideway-cli` 0.1.34 scaffolds Tideway 0.7.21 with safer auth and billing defaults.

### Performance

- Reduced allocation and lock hold time in request logging, rate limiting, jobs, and WebSocket hot paths.
- Added reproducible microbenchmarks and integration load-test baselines for future regression tracking.

### DX Gate

- Quickstart parity, documentation drift, scaffold idempotency, golden-path integration, CLI filesystem policy, and public API checks pass.
- Generated API and SaaS presets compile and test against the workspace source.

### Migration Notes

- Install Rust 1.88 or newer before upgrading.
- Replace `JwtIssuerConfig::with_secret` with `with_secure_secret` and `JwtVerifier::from_secret` with `from_secret_checked`.
- Credentialed CORS must list explicit origins and headers rather than using `*`.
- No public API removals are included; deprecated JWT constructors remain available for compatibility.

## [0.7.19] - 2026-05-04

### Changed

- README and getting-started quickstarts now use `tideway dev --fix-env`, matching the enforced onboarding guardrails.
- Docs taxonomy checks now match the current Vue-focused advanced CLI helper wording.
- `tideway-cli` now scaffolds Tideway `0.7.19`.

### Notes

- This release includes `tideway-cli` `0.1.31`.
- No semver-breaking public API changes were introduced.

## [0.7.18] - 2026-04-27

### Added

- API-first scaffold refinements, including a seeded todo resource following the entity -> repository -> service path.
- Agent- and CI-friendly plan output plumbing across mutating CLI commands.
- SaaS resource generation improvements for auth-scoped resources, ownership checks, audit hooks, and service-owned validation.
- Testcontainers-backed Postgres helper support behind `test-containers`.

### Changed

- `tideway-cli` now scaffolds Tideway `0.7.18`.
- CLI onboarding now promotes the tested API path first, with advanced frontend/backend presets one step deeper.
- Dev-mode diagnostics, request dumping, doctor findings, and generated service errors are more explicit.

### Notes

- This release includes `tideway-cli` `0.1.30`.

## [0.7.17] - 2026-03-17

### Added

- `App::into_make_service_with_connect_info()` to mirror `serve()` when manually calling `axum::serve`.

### Changed

- Request logging body previews are now disabled by default (`body_preview_size = 0`).
- Manual-serve docs now use the same `ConnectInfo<SocketAddr>` path as `serve()` for per-IP middleware parity.

### Fixed

- Cookie session stores now reject expired sessions on load and set cookie `Max-Age` from the actual session expiry.
- Auth extractors now reuse cached users without redundant `validate_user()` calls.
- Testing body assertions preserve response status and headers for subsequent assertions.
- In-memory job workers now use queue wakeups instead of polling idle loops.
- `tideway doctor` now accepts valid `sqlite:...` URLs generated by Tideway itself.
- `tideway dev` no longer treats `dev-dependencies` and `build-dependencies` as runtime database requirements.
- `tideway setup` now exits non-zero on real setup failures and safely updates Vite alias config.

### Notes

- No semver-breaking public API changes were introduced in this batch. This should ship as a patch release.
- Two rollout-sensitive behavior changes are worth calling out in release notes:
  - `tideway setup` now returns a non-zero exit code when frontend setup fails instead of reporting success.
  - Request logging no longer captures body previews by default; set `body_preview_size` explicitly if you relied on that output.

## [0.7.9] - 2026-01-26

### Added

- `ensure!` guard macro for concise precondition checks.
- `module!` macro for route groups plus OpenAPI module helpers.
- Alba-style testing helpers (`TestApp`, JSON helpers, `post_json`, status/header assertions).
- Auth extractor caching for users and claims (`ClaimsRef`) to avoid duplicate verification.
- Expanded docs and README examples for auth extractors, testing helpers, OpenAPI modules, and CLI flags.

## [0.7.8] - 2026-01-23

### Changed

- Metrics and request logging now prefer route templates when available, reducing label cardinality.
- Request logging skips formatting work when the configured log level is disabled.
- Feature matrix added to README; install snippet updated.
- Added optional `feature-gate-warnings` and `feature-gate-errors` for clearer feature diagnostics.
- Added tests covering MatchedPath metrics and logging guard behavior.

## [0.7.7] - 2026-01-23

### Changed

- Seat add-ons via Checkout are now rejected; use `SeatManager::add_seats` (proration-safe).
- `create_seat_checkout_session` is deprecated in favor of `SeatManager::add_seats`.

## [0.7.6] - 2026-01-23

### Security

- Password reset and email verification tokens now use OS CSPRNG for generation.
- MFA tokens are hashed before storage and consumption in the login flow.
- Trusted-device verification now rejects missing fingerprints when fingerprint validation is enabled.

## [0.7.1] - 2025-12-12

### Fixed

- **Compilation fix**: Updated `tower-http` minimum version to 0.6.7 to ensure `TimeoutLayer::with_status_code()` method is available. Version 0.7.0 would fail to compile with older tower-http versions.

## [0.7.0] - 2025-12-12

### Security

This release contains important security hardening for enterprise deployments.

- **ConsoleMailer now redacts email content by default.** Email body content is no longer printed to stdout to prevent sensitive data (tokens, PII, verification links) from being captured in container logs.

- **Webhook signature prefix is now strictly enforced.** When using `HmacSha256Verifier::new_with_prefix()`, signatures missing the required prefix are now rejected instead of silently accepting unprefixed signatures.

- **Database credentials protected in all output.** Both `Debug` and `Serialize` implementations for `DatabaseConfig` now redact the password, preventing credential leakage in logs, JSON serialization, and error messages.

- **JWT warning spam eliminated.** Security warnings for missing issuer/audience validation now fire only once per verifier instance using `OnceLock`.

### Added

- `RedisJobQueue::shutdown()` - Gracefully stop the background scheduler task
- `RedisJobQueue::ping()` - Async health check that updates cached status
- `InMemoryJobQueue::shutdown()` - Gracefully stop the background scheduler task
- `InMemoryJobQueue::with_history_limit()` - Create queue with custom completed/failed history size
- `RedisCache::ping()` - Async health check that updates cached status
- `SeaOrmPool::ping()` - Async health check that updates cached status
- `ConnectionManager::reconcile_counter()` - Detect and correct connection counter drift
- `ConsoleMailer::with_full_output()` - Opt-in to see full email content (development only)
- Configuration validation warnings for invalid environment variables (compression, timeout, session configs)

### Changed

- **BREAKING**: `ConsoleMailer` now redacts email body content by default. Use `.with_full_output(true)` to see full content.
- **BREAKING**: `HmacSha256Verifier::new_with_prefix()` now rejects signatures missing the required prefix (was silently accepting).
- `InMemoryJobQueue` completed/failed lists are now bounded (default 10,000) to prevent unbounded memory growth.
- `is_healthy()` methods on `RedisCache`, `RedisJobQueue`, `InMemoryJobQueue`, and `SeaOrmPool` now return cached status from `ping()` instead of blocking.
- Database pool configuration now validates limits: `max_connections` (1-1000), `connect_timeout` (1-300 seconds).
- Signal handlers in `App::serve()` use fallback instead of panicking on failure.
- Shutdown grace period increased from 2 to 5 seconds.

### Fixed

- **Blocking I/O in async context**: `InMemoryJobQueue::is_healthy()` used `blocking_lock()` which could deadlock the async runtime. Now uses `AtomicBool`.
- **Resource leak**: Background scheduler tasks in `RedisJobQueue` and `InMemoryJobQueue` now have proper shutdown mechanisms.
- **Unbounded memory growth**: `InMemoryJobQueue` completed/failed job lists now have configurable size limits.
- **Panic in production**: Removed `expect()` calls in `App::serve()` that could panic on invalid config or signal handler failures.
- **WebSocket connection counter drift**: Added `reconcile_counter()` to detect and correct atomic counter drift.

### Migration Guide

#### ConsoleMailer (Breaking Change)

If you use `ConsoleMailer` for development and need to see full email content:

```rust
// Before (0.6.x) - full content shown by default
let mailer = ConsoleMailer::new();

// After (0.7.0) - content redacted by default
let mailer = ConsoleMailer::new();  // Shows: "[TEXT] 42 bytes [REDACTED]"

// To see full content (development only):
let mailer = ConsoleMailer::new().with_full_output(true);
```

#### Webhook Signature Verification (Breaking Change)

If you use `HmacSha256Verifier::new_with_prefix()`, signatures **must** now include the prefix:

```rust
// Verifier configured with prefix
let verifier = HmacSha256Verifier::new_with_prefix(secret, "sha256=");

// Before (0.6.x) - both would pass:
verifier.verify_signature(payload, "sha256=abc123...").await  // OK
verifier.verify_signature(payload, "abc123...").await         // Also OK (wrong!)

// After (0.7.0) - prefix is required:
verifier.verify_signature(payload, "sha256=abc123...").await  // OK
verifier.verify_signature(payload, "abc123...").await         // FAILS (correct!)
```

#### Job Queue Shutdown (Recommended)

For clean resource cleanup, call `shutdown()` before dropping job queues:

```rust
// Redis job queue
let queue = RedisJobQueue::new("redis://localhost", None, 3, 5)?;
// ... use queue ...
queue.shutdown().await;  // NEW: Clean shutdown

// In-memory job queue
let queue = InMemoryJobQueue::new(3, 60);
// ... use queue ...
queue.shutdown().await;  // NEW: Clean shutdown
```

#### Health Check Pattern (Recommended)

For accurate health status, run periodic `ping()` calls:

```rust
// Database pool
let pool = SeaOrmPool::from_config(&config).await?;
tokio::spawn(async move {
    let mut interval = tokio::time::interval(Duration::from_secs(30));
    loop {
        interval.tick().await;
        pool.ping().await;  // Updates cached health status
    }
});

// is_healthy() now returns cached status (non-blocking)
if pool.is_healthy() { /* ... */ }
```

## [0.6.0] - 2025-12-12

### Security

**Cookie sessions are now properly encrypted.** Previously, session data was stored as plaintext JSON despite having an encryption key configured. This version implements authenticated encryption using XChaCha20-Poly1305 via the `cookie` crate's private cookies.

### Changed
- **BREAKING**: Cookie session encryption key must now be 64 bytes (128 hex characters), not 32 bytes. Generate with: `openssl rand -hex 64`
- Session cookies are now encrypted and authenticated - tampered cookies are rejected
- Added `encrypt()` and `decrypt()` public methods to `CookieSessionStore` for direct use
- Updated session documentation with correct key size requirements

### Migration Guide

If you were using cookie sessions, regenerate your encryption key:

```bash
# Old (no longer works):
# openssl rand -hex 32

# New (required):
openssl rand -hex 64
export SESSION_ENCRYPTION_KEY=your-128-char-hex-key
```

**Important:** Existing sessions will be invalidated when you upgrade, as the encryption format has changed.

## [0.5.0] - 2025-12-12

### Security

This release contains important security fixes. Users are strongly encouraged to upgrade.

- **Webhook HMAC verification**: Implemented proper HMAC-SHA256 signature verification with timing-safe comparison using the `subtle` crate. Previously, the webhook verifier accepted any non-empty signature.

- **Session encryption key requirement**: Cookie sessions now require an explicit encryption key. Without it, `CookieSessionStore::new()` returns an error. Use `allow_insecure_key: true` for development only.

- **Error information disclosure (CWE-209)**: Server errors (5xx) now hide internal details in production, preventing leakage of database credentials, internal hostnames, SQL queries, and stack traces.

- **Rate limiter IP spoofing**: Added `trust_proxy` configuration (default: `false`). X-Forwarded-For headers are now ignored by default to prevent IP spoofing attacks.

- **CORS disabled by default**: CORS is now disabled by default and must be explicitly enabled.

### Added
- `HmacSha256Verifier` for webhook signature verification with support for hex, base64, and prefixed signatures
- `trust_proxy` configuration for rate limiting
- `allow_insecure_key` configuration for session development mode
- `safe_message()` method on `TidewayError` for production-safe error messages
- Comprehensive test suite (200+ tests)
- Webhook documentation (`docs/webhooks.md`)

### Changed
- **BREAKING**: CORS `enabled` now defaults to `false` (was `true`)
- **BREAKING**: Rate limiter `trust_proxy` defaults to `false` - X-Forwarded-For is no longer trusted by default
- **BREAKING**: Cookie sessions require `encryption_key` or `allow_insecure_key: true`
- Session encryption key error now returns `Result` instead of panicking
- Updated `sessions.md`, `rate_limiting.md`, and `error_handling.md` documentation

### Dependencies
- Added `subtle` crate for timing-safe cryptographic operations
- `hmac` and `sha2` crates (already present) now used for webhook verification

### Migration Guide

#### CORS Configuration

CORS is now disabled by default. To enable:

```rust
// Option 1: Use permissive() for development
let cors = CorsConfig::permissive();

// Option 2: Explicitly enable with origins
let cors = CorsConfig::builder()
    .enabled(true)
    .allow_origin("https://example.com")
    .build();
```

#### Session Configuration

Cookie sessions now require a 64-byte encryption key (128 hex characters):

```bash
# Generate a key
openssl rand -hex 64

# Set environment variable
export SESSION_ENCRYPTION_KEY=your-128-char-hex-key
```

Or for development only:
```rust
let config = SessionConfig {
    allow_insecure_key: true,  // WARNING: Never use in production!
    ..Default::default()
};
```

#### Rate Limiting Behind a Proxy

If your application is behind nginx, AWS ALB, or Cloudflare:

```rust
let rate_limit = RateLimitConfig::builder()
    .enabled(true)
    .per_ip()
    .trust_proxy(true)  // Enable this if behind a trusted proxy
    .build();
```

Or via environment:
```bash
export RATE_LIMIT_TRUST_PROXY=true
```

## [0.4.0] - 2025-12-11

### Added
- Comprehensive test coverage for core modules

### Changed
- **BREAKING**: `SeaOrmPool::as_ref()` renamed to `SeaOrmPool::inner()` to avoid confusion with `std::convert::AsRef` trait
- **BREAKING**: `DatabaseConnection::as_ref()` renamed to `DatabaseConnection::inner()`
- **BREAKING**: `TestUser::default()` renamed to `TestUser::generate()` to avoid confusion with `std::default::Default` trait

### Fixed
- Fixed deprecated `TimeoutLayer::new` usage (now uses `with_status_code`)
- Fixed all clippy warnings across the codebase
- Improved code quality with idiomatic Rust patterns

### Migration Guide

#### Database Connection Changes

If you were using `as_ref()` to get the inner SeaORM connection:

```rust
// Before (0.3.x)
let conn = sea_orm_pool.as_ref();

// After (0.4.0)
let conn = sea_orm_pool.inner();
```

#### TestUser Changes

If you were using `TestUser::default()` in tests:

```rust
// Before (0.3.x)
let user = TestUser::default();

// After (0.4.0)
let user = TestUser::generate();
```

The rename to `generate()` better reflects that this creates a new user with randomly generated fake data, not a "default" user with empty/zero values.

## [0.3.0] - 2025-12-11

### Added
- **Email support** with `Mailer` trait for sending transactional emails
- `ConsoleMailer` for development (prints emails to stdout)
- `SmtpMailer` using lettre for production SMTP
- `SmtpConfig` with builder pattern and `from_env()` for configuration
- `AppContext::with_mailer()` builder method for dependency injection
- Documentation for third-party email services (Resend, SendGrid, Postmark, AWS SES)
- Email example and comprehensive test suite

## [0.2.1] - 2025-12-11

### Fixed
- Various bug fixes and improvements

## [0.2.0] - 2025-12-11

### Added
- Background jobs system with `JobQueue` trait
- WebSocket support with connection management
- In-memory and Redis-backed job queues

## [0.1.0] - Initial Release

### Added
- Core framework with Axum integration
- Route modules and modular architecture
- Database support with SeaORM
- Cache and session traits
- Request validation
- Health checks
- Prometheus metrics
- Compression and security middleware
- Alba-style testing utilities