key_vault/monitor/mod.rs
1//! Layer 8 — Security monitor.
2//!
3//! A [`SecurityMonitor`] is the vault's outbound channel for anomaly events:
4//! repeated decryption failures, unusual access patterns, and threshold
5//! breaches. Monitor calls happen on the failure path only; the success path
6//! costs nothing.
7//!
8//! Built-in monitors (`NoMonitor`, `LogMonitor`, `MetricsMonitor`,
9//! `WebhookMonitor`, `CompositeMonitor`) arrive in Phase 0.8. This module
10//! currently defines the trait surface and the three event-context structs.
11
12use alloc::borrow::Cow;
13use alloc::string::String;
14use core::time::Duration;
15
16/// Context passed when a decryption attempt fails — wrong key, tampered
17/// ciphertext, etc.
18#[non_exhaustive]
19#[derive(Debug, Clone)]
20pub struct FailureContext {
21 /// Logical name of the key whose use produced the failure.
22 pub key_name: String,
23 /// Number of consecutive failures observed for this key, including this
24 /// one.
25 pub consecutive_failures: u32,
26 /// Time elapsed since the first failure in the current window.
27 pub window_elapsed: Duration,
28 /// Caller-supplied free-form note. Sanitized — never includes key bytes
29 /// or ciphertext.
30 pub note: Cow<'static, str>,
31}
32
33/// Context for a successful access that the monitor flagged as anomalous —
34/// unusual caller, unusual frequency, off-hours activity.
35#[non_exhaustive]
36#[derive(Debug, Clone)]
37pub struct AccessContext {
38 /// Logical name of the key that was accessed.
39 pub key_name: String,
40 /// Caller-supplied free-form note. Sanitized.
41 pub note: Cow<'static, str>,
42}
43
44/// Context for a configured threshold being crossed (e.g. N failures in M
45/// seconds).
46#[non_exhaustive]
47#[derive(Debug, Clone)]
48pub struct ThresholdContext {
49 /// Logical name of the key.
50 pub key_name: String,
51 /// Number of failures observed within the configured window.
52 pub failures_in_window: u32,
53 /// Width of the configured window.
54 pub window: Duration,
55 /// `true` if this breach has put the vault into lock-out state.
56 pub lockout_triggered: bool,
57}
58
59/// Outbound channel for anomaly events.
60///
61/// Implementations should treat monitor calls as advisory and fire-and-forget:
62/// the vault must not block on a monitor, must not crash if a monitor panics
63/// or returns an error, and must not retry. Implementations that need
64/// retry, queuing, or batching are expected to handle that internally
65/// (typically on a background thread).
66///
67/// # Implementor contract
68///
69/// - **Non-blocking.** Calls must return promptly. Network or disk work should
70/// be deferred to a background worker.
71/// - **No panics.** A panicking monitor implementation is a bug in the
72/// implementation, not the vault. Wrap fallible operations and absorb
73/// their errors.
74/// - **No key material in calls.** None of the context structs carry raw key
75/// bytes; do not introduce custom side-channels that do.
76/// - **`Send + Sync`.** Monitors are shared across threads.
77pub trait SecurityMonitor: Send + Sync {
78 /// Called when a decryption attempt fails.
79 fn on_decryption_failure(&self, ctx: &FailureContext);
80
81 /// Called when an access pattern looks anomalous to the configured
82 /// detector.
83 fn on_anomalous_access(&self, ctx: &AccessContext);
84
85 /// Called when a configured failure threshold is crossed.
86 fn on_threshold_breach(&self, ctx: &ThresholdContext);
87}