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
//! M48 + M49 — verify-failure audit emission port + per-source rate
//! limiter (RFC_2026-05-04_jwt-full-adoption Phase 9).
//!
//! ── Why pas-external owns the port, not the schema (β1) ─────────────────
//!
//! [`AuditSink`] is purely abstract. pas-external ships only:
//!
//! - [`NoopAuditSink`] — explicit "I don't want audit emission" choice
//! - `MemoryAuditSink` — `test-support`-gated adapter for boundary
//! verification (downstream consumers' integration tests)
//!
//! Production adapters (chat-auth in PCS, future RCW/CTW middleware)
//! live in their own crates and decide their own persistence schema
//! (Postgres table, tracing-subscriber piping into Cloud Logging, etc).
//! The SDK does not bake in `sqlx` or any specific schema — schema
//! decisions belong to whichever service operates the audit pipeline.
//! See [`super::token::port::BearerVerifier`] for the matching
//! port-and-adapter precedent (D-04 γ, locked 2026-05-05).
//!
//! ── Why composition, not orchestration (refinement #1) ─────────────────
//!
//! [`super::token::PasJwtVerifier`] holds ONE port (`Arc<dyn AuditSink>`),
//! not two. Rate-limiting is a property of the sink, expressed by
//! wrapping any sink in a future `RateLimitedAuditSink<S, L>` (Phase
//! 9.C). This matches `epoch_revocation`'s deep-module note:
//! composition lives in the adapter layer; the engine sees a single
//! port. Future stacking is free (`BatchedAuditSink`,
//! `AsyncSpawnAuditSink`, etc).
//!
//! ── Failure-mode contract — non-blocking ────────────────────────────────
//!
//! [`AuditSink::record_failure`] returns `()` (no [`Result`]). M48 is
//! observability, NOT auth-flow critical. Adapters log internal substrate
//! failures via `tracing::error!` and continue. The verify hot path
//! MUST NEVER degrade because audit persistence failed; this contract
//! is enforced at the trait surface (no error to bubble in the first
//! place). Callers needing a Result for instrumentation can wrap in a
//! private struct that records the result internally.
//!
//! ── SLA contract ────────────────────────────────────────────────────────
//!
//! Implementations SHOULD return within 10ms. Heavier work (HTTP
//! roundtrip, batch flush, retry) MUST be spawned onto a background
//! task so the verify hot path is not blocked. The `&self` (not
//! `&mut self`) + `Send + Sync` bounds let one verifier emit
//! concurrently without per-call locking.
//!
//! ── Phase 10 inheritance ────────────────────────────────────────────────
//!
//! Phase 10.11 (RP middleware — `pas-external::oidc::IdTokenVerifier`)
//! emits through the same [`AuditSink`] port. id_token verify failures
//! and access_token verify failures share the audit pipeline; the
//! [`VerifyErrorKind`] enum gains id_token-specific variants in 10.11
//! without breaking the contract.
pub use ;
pub use RateLimitedAuditSink;
pub use ;
pub use MemoryAuditSink;
/// Opaque per-source bucket key for a `RateLimiter`.
///
/// Wraps a `String` so the underlying derivation strategy stays a hidden
/// implementation detail of whoever builds the key.
/// [`AuditEvent::rate_limit_key`] derives compound `client_id_hint ‖
/// kid_hint` keys per Phase 9 design call (e); future callers (Phase
/// 10.11 nonce-store, OAuth callback PKCE throttle, etc) compose their
/// own keys from substrate-relevant fields.
///
/// Newtype (rather than passing `&str` through the limiter trait) lets
/// the type system prove that callers used the correct derivation —
/// stringly-typed keys would silently re-bucket on stray formatting
/// drift (whitespace, casing, separator choice).
;