secure_errors 0.1.1

Secure error handling with public-safe responses, incident IDs, and panic boundaries.
Documentation
# secure_errors

[![crates.io](https://img.shields.io/crates/v/secure_errors.svg)](https://crates.io/crates/secure_errors)
[![docs.rs](https://docs.rs/secure_errors/badge.svg)](https://docs.rs/secure_errors)
[![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](#license)

Secure-by-default error handling for HTTP services (OWASP C10). Part of the [SunLit Security Libraries](https://github.com/kerberosmansour/SunLitSecurityLibraries) workspace.

## The problem this crate solves

Most Rust web services accidentally leak internal details — SQL fragments, hostnames, stack traces — through error responses. This crate enforces a **three-layer model**:

1. **`AppError`** — internal layer, full detail, **never serialized to the wire**.
2. **`PublicError`** — the only type that may be serialized to HTTP responses. Stable, redaction-safe.
3. **`ErrorClassification`** — operational metadata: retryability, alerting hints, signals.

A single source-of-truth mapper (`http::into_response_parts`) turns `AppError` into HTTP responses, so an axum service and an actix-web service emit **byte-identical** wire payloads for the same error.

## Install

```toml
[dependencies]
secure_errors = "0.1"  # default: axum

# For actix-web instead of axum:
# secure_errors = { version = "0.1", default-features = false, features = ["actix-web"] }

# Both at once (e.g. workspace with services on both):
# secure_errors = { version = "0.1", features = ["axum", "actix-web"] }
```

## Quick example (axum)

```rust
use axum::{routing::get, Router};
use secure_errors::kind::AppError;
use secure_errors::middleware::ErrorMappingLayer;

async fn not_found() -> Result<&'static str, AppError> {
    Err(AppError::NotFound)
}

async fn rate_limited() -> Result<&'static str, AppError> {
    Err(AppError::RateLimit { retry_after_seconds: Some(30) })
}

let app = Router::new()
    .route("/missing", get(not_found))
    .route("/throttled", get(rate_limited))
    .layer(ErrorMappingLayer::default());

// Both routes emit a stable PublicError JSON. /throttled adds Retry-After: 30.
// Internal details (dependency name, validation code) never reach the client.
```

## Quick example (actix-web)

```rust
use actix_web::{web, App, HttpResponse, HttpServer};
use secure_errors::kind::AppError;

async fn dep_down() -> Result<HttpResponse, AppError> {
    Err(AppError::Dependency { dep: "postgres" })  // dep name stays internal
}

HttpServer::new(|| App::new().route("/dep", web::get().to(dep_down)))
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
```

See [`examples/actix_error_minimal.rs`](https://github.com/kerberosmansour/SunLitSecurityLibraries/blob/main/crates/secure_errors/examples/actix_error_minimal.rs) for a complete runnable service.

## Design invariants

- **`PublicError` is the only type that may reach an HTTP wire.**
- **`http::into_response_parts` is the only place that maps errors to HTTP status codes** — guaranteed cross-framework parity.
- **No internal text** (SQL fragments, hostnames, stack traces, validation reason codes) may appear in `PublicError`.
- The internal `AppError::Internal { detail }` variant carries free-form detail for logs; the public response says only "internal_error".

## Feature flags

| Flag | Default | Enables |
|---|---|---|
| `axum` || `middleware::ErrorMappingLayer` (tower) and `impl IntoResponse for AppError` |
| `actix-web` | off | `impl actix_web::ResponseError for AppError` (see `actix` module) |

## Compatibility

- MSRV: 1.78
- `#![forbid(unsafe_code)]`, `#![deny(missing_docs)]`, `#![deny(clippy::all, clippy::pedantic)]`

## Status

Alpha.

## Related crates

Part of the [SunLit Security Libraries](https://github.com/kerberosmansour/SunLitSecurityLibraries) workspace:

| Crate | Purpose |
|---|---|
| [`security_core`]https://crates.io/crates/security_core | Shared types, identity, classification, severity, redaction. |
| [`security_events`]https://crates.io/crates/security_events | Security logging and tamper-evident audit chain. |
| [`secure_output`]https://crates.io/crates/secure_output | Context-aware output encoders (HTML, JSON, URL, JS, CSS, XML, LDAP, shell). |
| [`secure_data`]https://crates.io/crates/secure_data | Secrets, envelope encryption, Argon2id, FIPS, mobile storage. |
| [`secure_network`]https://crates.io/crates/secure_network | TLS policy, SPKI pinning, mTLS, cleartext detection. |
| [`secure_device_trust`]https://crates.io/crates/secure_device_trust | Native-client device trust and session certificates. |
| [`secure_resilience`]https://crates.io/crates/secure_resilience | RASP and environment-detection policy. |
| [`secure_privacy`]https://crates.io/crates/secure_privacy | PII classification, consent, retention, pseudonymization. |
| [`secure_boundary`]https://crates.io/crates/secure_boundary | Input validation, security headers, boundary protections. |
| [`secure_identity`]https://crates.io/crates/secure_identity | JWT/OIDC, MFA, sessions, biometric step-up. |
| [`secure_authz`]https://crates.io/crates/secure_authz | Typed deny-by-default authorization with device-trust predicates. |

## Getting help

- **Questions, ideas, design discussions** — open a [GitHub Discussion]https://github.com/kerberosmansour/SunLitSecurityLibraries/discussions.
- **Bug reports** — use the bug-report template in [GitHub Issues]https://github.com/kerberosmansour/SunLitSecurityLibraries/issues.
- **Security issues** — please do **not** open a public issue. See [SECURITY.md]https://github.com/kerberosmansour/SunLitSecurityLibraries/blob/main/SECURITY.md for the responsible-disclosure process.

## Contributing

Contributions are welcome. Please read [CONTRIBUTING.md](https://github.com/kerberosmansour/SunLitSecurityLibraries/blob/main/CONTRIBUTING.md) and the [Code of Conduct](https://github.com/kerberosmansour/SunLitSecurityLibraries/blob/main/CODE_OF_CONDUCT.md) before opening a PR.

## License

Dual-licensed under [MIT](https://github.com/kerberosmansour/SunLitSecurityLibraries/blob/main/LICENSE-MIT) or [Apache-2.0](https://github.com/kerberosmansour/SunLitSecurityLibraries/blob/main/LICENSE-APACHE) at your option.