# Component Location Roles - Security Feature
## Overview
Component locations (file paths where component code lives) now support **role-based visibility** for security. This prevents leaking sensitive internal file paths in public documentation while still providing full transparency to internal teams.
## Why This Matters 🔒
**Security Concern**: Exposing internal file paths in public documentation can:
- Reveal internal application structure
- Help attackers understand code organization
- Leak implementation details that should remain private
- Show sensitive file names (e.g., `secret_rotation.rs`, `admin_backdoor.rs`)
**Solution**: Role-based filtering ensures only appropriate audiences see specific file paths.
---
## Role System
The feature uses the existing three-role hierarchy:
| **Public** | 0 (most restrictive) | Only Public content |
| **Developer** | 1 | Public + Developer content |
| **Internal** | 2 (least restrictive) | Everything (Public + Developer + Internal) |
---
## API Reference
### Default Behavior (Secure by Default)
```rust
use waddling_errors::doc_generator::DocRegistry;
let mut registry = DocRegistry::new("MyApp", "1.0.0");
// Defaults to Internal role - only visible in internal docs
registry.register_component_location(
"AUTH",
"src/auth/jwt_signer.rs"
);
```
**Security Best Practice**: This is the default behavior to prevent accidental exposure of internal paths.
---
### Explicit Role Control
```rust
use waddling_errors::doc_generator::DocRegistry;
use waddling_errors::Role;
let mut registry = DocRegistry::new("MyApp", "1.0.0");
// Public: Safe to show everyone (documentation examples)
registry.register_component_location_with_role(
"AUTH",
"examples/auth_usage.rs",
Some(Role::Public)
);
// Developer: For contributors and team (debugging utilities)
registry.register_component_location_with_role(
"AUTH",
"src/auth/debug_utils.rs",
Some(Role::Developer)
);
// Internal: Team only (implementation details)
registry.register_component_location_with_role(
"AUTH",
"src/auth/secret_rotation.rs",
Some(Role::Internal)
);
// Visible to all (NOT RECOMMENDED for security)
registry.register_component_location_with_role(
"AUTH",
"src/auth/public_api.rs",
None
);
```
---
## Real-World Example
### Component Registration with Security
```rust
#[cfg(feature = "doc-gen")]
pub fn register_component(generator: &mut DocRegistry) {
use waddling_errors::Role;
generator.register_component(
"AUTH",
Some("Authentication and authorization system..."),
&[...],
&[...]
);
// ✅ PUBLIC: Documentation examples - safe for everyone
generator.register_component_location_with_role(
"AUTH",
"examples/complete_system/components/auth.rs",
Some(Role::Public)
);
// ❌ INTERNAL: Implementation details - team only (SECURE BY DEFAULT)
generator.register_component_location("AUTH", "src/middleware/auth.rs");
generator.register_component_location("AUTH", "src/handlers/auth_handler.rs");
generator.register_component_location("AUTH", "src/services/token_service.rs");
generator.register_component_location("AUTH", "src/auth/secret_rotation.rs");
}
```
---
## Generated Documentation Comparison
### Public Documentation (`myapp-pub.json`)
```json
{
"components": {
"AUTH": {
"name": "AUTH",
"description": "Authentication and authorization system...",
"locations": [
{
"path": "examples/complete_system/components/auth.rs",
"role": "Public"
}
]
}
}
}
```
**✅ Secure**: Only shows the public example file. Internal implementation paths are hidden.
---
### Developer Documentation (`myapp-dev.json`)
```json
{
"components": {
"AUTH": {
"name": "AUTH",
"description": "Authentication and authorization system...",
"locations": [
{
"path": "examples/complete_system/components/auth.rs",
"role": "Public"
},
{
"path": "src/auth/debug_utils.rs",
"role": "Developer"
}
]
}
}
}
```
**✅ Team Context**: Shows public examples + developer utilities. Still protects internal secrets.
---
### Internal Documentation (`myapp-int.json`)
```json
{
"components": {
"AUTH": {
"name": "AUTH",
"description": "Authentication and authorization system...",
"locations": [
{
"path": "examples/complete_system/components/auth.rs",
"role": "Public"
},
{
"path": "src/auth/debug_utils.rs",
"role": "Developer"
},
{
"path": "src/middleware/auth.rs",
"role": "Internal"
},
{
"path": "src/handlers/auth_handler.rs",
"role": "Internal"
},
{
"path": "src/services/token_service.rs",
"role": "Internal"
},
{
"path": "src/auth/secret_rotation.rs",
"role": "Internal"
}
]
}
}
}
```
**✅ Full Transparency**: Internal team sees everything for maintenance and debugging.
---
## Security Benefits
### 1. **Information Security**
- Public documentation doesn't leak internal file structure
- Attackers can't map your codebase from docs
- Sensitive file names remain private
### 2. **Professional Documentation**
- Public docs show only relevant examples
- Internal docs provide complete context for team
- Different audiences get appropriate information
### 3. **Compliance**
- Helps meet security audit requirements
- Demonstrates security-conscious design
- Prevents accidental information disclosure
### 4. **Secure by Default**
- No action needed for security - it's the default
- Must explicitly opt-in to public visibility
- Reduces risk of configuration errors
---
## Best Practices
### ✅ DO
```rust
// ✅ Mark documentation examples as Public
generator.register_component_location_with_role(
"Auth",
"examples/auth_example.rs",
Some(Role::Public)
);
// ✅ Use default (Internal) for implementation files
generator.register_component_location("Auth", "src/auth/jwt.rs");
// ✅ Mark debugging utilities as Developer
generator.register_component_location_with_role(
"Auth",
"src/auth/debug_logger.rs",
Some(Role::Developer)
);
```
### ❌ DON'T
```rust
// ❌ DON'T mark internal implementations as Public
generator.register_component_location_with_role(
"Auth",
"src/auth/secret_rotation.rs", // SENSITIVE!
Some(Role::Public) // ❌ SECURITY RISK!
);
// ❌ DON'T use None for sensitive paths
generator.register_component_location_with_role(
"Auth",
"src/auth/admin_backdoor.rs", // VERY SENSITIVE!
None // ❌ Visible to everyone!
);
```
---
## Migration Guide
### Updating Existing Code
**Before (v0.4.x - No role support):**
```rust
generator.register_component_location("AUTH", "src/auth/jwt.rs");
```
**After (v0.5.x - Secure by default):**
```rust
// Same call, but now defaults to Internal (secure!)
generator.register_component_location("AUTH", "src/auth/jwt.rs");
// For public examples, explicitly mark them:
generator.register_component_location_with_role(
"AUTH",
"examples/auth_example.rs",
Some(Role::Public)
);
```
**No Breaking Changes**: Existing code works as-is, but now has secure defaults! 🎉
---
## Data Structures
### ComponentLocation
```rust
pub struct ComponentLocation {
/// File path (e.g., "src/auth/jwt_signer.rs")
pub path: String,
/// Role visibility (Public, Developer, Internal)
/// None = visible to all roles (not recommended for security)
pub role: Option<String>,
}
```
### Convenience Constructors
```rust
impl ComponentLocation {
/// Create with any role
pub fn new(path: impl Into<String>, role: Option<String>) -> Self;
/// Create public location
pub fn public(path: impl Into<String>) -> Self;
/// Create developer location
pub fn developer(path: impl Into<String>) -> Self;
/// Create internal location (default/secure)
pub fn internal(path: impl Into<String>) -> Self;
}
```
---
## Technical Details
### Filtering Implementation
Component locations are filtered during documentation rendering in `render_all_roles()`:
```rust
// Filter component locations by role
let filtered_components: HashMap<String, ComponentMeta> = self
.components
.iter()
.map(|(name, comp)| {
let filtered_locations: Vec<ComponentLocation> = comp
.locations
.iter()
.filter(|loc| html::is_visible_at_role(loc.role.as_deref(), role))
.cloned()
.collect();
let mut filtered_comp = comp.clone();
filtered_comp.locations = filtered_locations;
(name.clone(), filtered_comp)
})
.collect();
```
### Visibility Rules
```rust
fn is_visible_at_role(content_role: Option<&str>, viewer_role: Role) -> bool {
match content_role {
None => true, // Unspecified = visible to all
Some("Public") => true, // Everyone can see Public
Some("Developer") => viewer_role != Role::Public, // Dev + Internal
Some("Internal") => viewer_role == Role::Internal, // Internal only
_ => false,
}
}
```
---
## Future Enhancements
Potential future additions (not yet implemented):
1. **Custom Roles**: Define your own role hierarchies
2. **Compartmentalization**: Numeric role system (0-9999) for complex orgs
3. **Macro Support**: `#[in_component(Auth, role = Internal)]` attribute
4. **Auto-Discovery**: Scan codebase for component usage patterns
5. **LSP Integration**: IDE navigation using component metadata
---
## FAQ
### Q: Why default to Internal instead of Public?
**A**: **Security by default**. It's safer to accidentally hide a path than to accidentally expose it. Public documentation should be curated and intentional.
### Q: Can I make all locations public?
**A**: Yes, but not recommended. Use `Role::Public` explicitly for documentation examples only. Keep implementation details internal.
### Q: What if I forget to set roles?
**A**: You're safe! The default is `Internal`, so paths are hidden from public docs unless you explicitly mark them as `Public`.
### Q: Does this affect runtime behavior?
**A**: No. Roles only affect documentation generation. Runtime error handling is unchanged.
### Q: Can I have the same file in multiple components?
**A**: Yes. A file like `src/utils/crypto.rs` could be marked as part of both `Auth` and `Storage` components with different roles.
---
## Summary
🔒 **Secure by Default**: Component locations default to `Internal` for security
📘 **Role-Based Filtering**: Public, Developer, and Internal visibility levels
🎯 **Explicit Control**: Use `register_component_location_with_role()` for fine-grained control
✅ **Zero Breaking Changes**: Existing code works with added security
🛡️ **Professional**: Prevents information leakage in public documentation
**This feature demonstrates that error handling can be security-conscious from the start!** 🦆