llm_shield_cloud_aws/lib.rs
1//! AWS cloud integrations for LLM Shield.
2//!
3//! This crate provides AWS-specific implementations of the cloud abstraction traits
4//! defined in `llm-shield-cloud`:
5//!
6//! - **Secrets Management**: AWS Secrets Manager via `AwsSecretsManager`
7//! - **Object Storage**: AWS S3 via `AwsS3Storage`
8//! - **Metrics**: CloudWatch Metrics via `CloudWatchMetrics`
9//! - **Logging**: CloudWatch Logs via `CloudWatchLogger`
10//!
11//! # Features
12//!
13//! - Automatic credential discovery (environment → file → IAM role → IRSA)
14//! - Built-in caching for secrets (TTL-based)
15//! - Multipart uploads for large S3 objects (>5MB)
16//! - Batched metrics and log export for efficiency
17//! - Full support for AWS SDK retry and timeout policies
18//!
19//! # Architecture
20//!
21//! ```text
22//! ┌─────────────────────────────────────┐
23//! │ LLM Shield Application │
24//! └─────────────────────────────────────┘
25//! │
26//! ▼
27//! ┌─────────────────────────────────────┐
28//! │ llm-shield-cloud (traits) │
29//! │ - CloudSecretManager │
30//! │ - CloudStorage │
31//! │ - CloudMetrics/Logger │
32//! └─────────────────────────────────────┘
33//! │
34//! ▼
35//! ┌─────────────────────────────────────┐
36//! │ llm-shield-cloud-aws (impl) │
37//! │ - AwsSecretsManager │
38//! │ - AwsS3Storage │
39//! │ - CloudWatchMetrics/Logger │
40//! └─────────────────────────────────────┘
41//! │
42//! ▼
43//! ┌─────────────────────────────────────┐
44//! │ AWS Services │
45//! │ - Secrets Manager │
46//! │ - S3 │
47//! │ - CloudWatch │
48//! └─────────────────────────────────────┘
49//! ```
50//!
51//! # Usage Examples
52//!
53//! ## Secret Management
54//!
55//! ```no_run
56//! use llm_shield_cloud_aws::AwsSecretsManager;
57//! use llm_shield_cloud::CloudSecretManager;
58//!
59//! #[tokio::main]
60//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
61//! // Initialize with default configuration (uses AWS credential chain)
62//! let secrets = AwsSecretsManager::new().await?;
63//!
64//! // Fetch a secret (automatically cached for 5 minutes)
65//! let api_key = secrets.get_secret("llm-shield/openai-api-key").await?;
66//! println!("API Key: {}", api_key.as_string());
67//!
68//! // List all secrets
69//! let secret_names = secrets.list_secrets().await?;
70//! println!("Found {} secrets", secret_names.len());
71//!
72//! Ok(())
73//! }
74//! ```
75//!
76//! ## Object Storage
77//!
78//! ```no_run
79//! use llm_shield_cloud_aws::AwsS3Storage;
80//! use llm_shield_cloud::{CloudStorage, PutObjectOptions};
81//!
82//! #[tokio::main]
83//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
84//! let storage = AwsS3Storage::new("llm-shield-models").await?;
85//!
86//! // Upload a model (automatically uses multipart for files >5MB)
87//! let model_data = tokio::fs::read("toxicity-model.onnx").await?;
88//! storage.put_object("models/toxicity.onnx", &model_data).await?;
89//!
90//! // Upload with options
91//! let options = PutObjectOptions {
92//! content_type: Some("application/octet-stream".to_string()),
93//! storage_class: Some("INTELLIGENT_TIERING".to_string()),
94//! encryption: Some("AES256".to_string()),
95//! ..Default::default()
96//! };
97//! storage.put_object_with_options("models/model.onnx", &model_data, &options).await?;
98//!
99//! // Download and verify
100//! let downloaded = storage.get_object("models/toxicity.onnx").await?;
101//! assert_eq!(model_data, downloaded);
102//!
103//! Ok(())
104//! }
105//! ```
106//!
107//! ## Metrics
108//!
109//! ```no_run
110//! use llm_shield_cloud_aws::CloudWatchMetrics;
111//! use llm_shield_cloud::{CloudMetrics, Metric};
112//! use std::collections::HashMap;
113//!
114//! #[tokio::main]
115//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
116//! let metrics = CloudWatchMetrics::new("LLMShield").await?;
117//!
118//! let mut dimensions = HashMap::new();
119//! dimensions.insert("Environment".to_string(), "Production".to_string());
120//! dimensions.insert("Scanner".to_string(), "Toxicity".to_string());
121//!
122//! let metric = Metric {
123//! name: "ScanDuration".to_string(),
124//! value: 123.45,
125//! timestamp: std::time::SystemTime::now()
126//! .duration_since(std::time::UNIX_EPOCH)?
127//! .as_secs(),
128//! dimensions,
129//! unit: Some("Milliseconds".to_string()),
130//! };
131//!
132//! metrics.export_metric(&metric).await?;
133//!
134//! Ok(())
135//! }
136//! ```
137//!
138//! ## Logging
139//!
140//! ```no_run
141//! use llm_shield_cloud_aws::CloudWatchLogger;
142//! use llm_shield_cloud::{CloudLogger, LogLevel, LogEntry};
143//! use std::collections::HashMap;
144//!
145//! #[tokio::main]
146//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
147//! let logger = CloudWatchLogger::new(
148//! "/llm-shield/api",
149//! "production-instance-1"
150//! ).await?;
151//!
152//! // Simple logging
153//! logger.log("API server started", LogLevel::Info).await?;
154//!
155//! // Structured logging
156//! let mut labels = HashMap::new();
157//! labels.insert("request_id".to_string(), "req-123".to_string());
158//! labels.insert("user_id".to_string(), "user-456".to_string());
159//!
160//! let entry = LogEntry {
161//! timestamp: std::time::SystemTime::now(),
162//! level: LogLevel::Info,
163//! message: "Request processed successfully".to_string(),
164//! labels,
165//! trace_id: Some("trace-789".to_string()),
166//! span_id: Some("span-012".to_string()),
167//! };
168//!
169//! logger.log_structured(&entry).await?;
170//!
171//! Ok(())
172//! }
173//! ```
174//!
175//! # AWS Credentials
176//!
177//! This crate uses the AWS SDK's default credential provider chain:
178//!
179//! 1. **Environment variables**: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`
180//! 2. **AWS credentials file**: `~/.aws/credentials`
181//! 3. **ECS container credentials**: IAM role for ECS tasks
182//! 4. **EC2 instance profile**: IAM role for EC2 instances
183//! 5. **EKS pod identity**: IAM Roles for Service Accounts (IRSA)
184//!
185//! # IAM Permissions
186//!
187//! Required IAM permissions are documented in `iam-policies/` directory:
188//!
189//! - `secrets-manager-policy.json`: Secrets Manager permissions
190//! - `s3-policy.json`: S3 bucket access permissions
191//! - `cloudwatch-policy.json`: CloudWatch metrics and logs permissions
192//!
193//! # Configuration
194//!
195//! Configure AWS integrations via `CloudConfig`:
196//!
197//! ```yaml
198//! cloud:
199//! provider: aws
200//! aws:
201//! region: us-east-1
202//! secrets_manager:
203//! enabled: true
204//! cache_ttl_seconds: 300
205//! s3:
206//! bucket: llm-shield-models
207//! models_prefix: models/
208//! results_prefix: scan-results/
209//! cloudwatch:
210//! enabled: true
211//! namespace: LLMShield
212//! log_group: /llm-shield/api
213//! log_stream: production
214//! ```
215//!
216//! # Performance
217//!
218//! - **Secret caching**: >90% cache hit rate reduces API calls
219//! - **Multipart uploads**: Automatically used for objects >5MB
220//! - **Batch export**: Metrics and logs are batched for efficiency
221//! - **Async operations**: All I/O is fully asynchronous with tokio
222//!
223//! # Error Handling
224//!
225//! All operations return `Result<T, CloudError>` from the `llm-shield-cloud` crate:
226//!
227//! ```rust
228//! use llm_shield_cloud::{CloudError, Result};
229//! use llm_shield_cloud_aws::AwsSecretsManager;
230//!
231//! async fn fetch_secret(name: &str) -> Result<String> {
232//! let secrets = AwsSecretsManager::new().await?;
233//!
234//! match secrets.get_secret(name).await {
235//! Ok(value) => Ok(value.as_string().to_string()),
236//! Err(CloudError::SecretNotFound(name)) => {
237//! eprintln!("Secret '{}' not found", name);
238//! Err(CloudError::SecretNotFound(name))
239//! }
240//! Err(e) => {
241//! eprintln!("Failed to fetch secret: {}", e);
242//! Err(e)
243//! }
244//! }
245//! }
246//! ```
247//!
248//! # Testing
249//!
250//! Run unit tests:
251//!
252//! ```bash
253//! cargo test -p llm-shield-cloud-aws
254//! ```
255//!
256//! Run integration tests (requires AWS credentials):
257//!
258//! ```bash
259//! cargo test -p llm-shield-cloud-aws --test integration -- --ignored
260//! ```
261//!
262//! # License
263//!
264//! MIT OR Apache-2.0
265
266pub mod observability;
267pub mod secrets;
268pub mod storage;
269
270// Re-export main types
271pub use observability::{CloudWatchLogger, CloudWatchMetrics};
272pub use secrets::AwsSecretsManager;
273pub use storage::AwsS3Storage;
274
275// Re-export cloud abstractions for convenience
276pub use llm_shield_cloud::{
277 CloudError, CloudLogger, CloudMetrics, CloudSecretManager, CloudStorage, GetObjectOptions,
278 LogEntry, LogLevel, Metric, ObjectMetadata, PutObjectOptions, Result, SecretMetadata,
279 SecretValue,
280};
281
282/// Crate version
283pub const VERSION: &str = env!("CARGO_PKG_VERSION");
284
285/// Crate name
286pub const LIB_NAME: &str = env!("CARGO_PKG_NAME");
287
288#[cfg(test)]
289mod tests {
290 use super::*;
291
292 #[test]
293 fn test_version() {
294 assert!(!VERSION.is_empty());
295 assert_eq!(LIB_NAME, "llm-shield-cloud-aws");
296 }
297}