initial-commit

This commit is contained in:
Guillaume GRABÉ
2026-05-12 17:06:43 +02:00
commit 051a080dfa
110 changed files with 26377 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+985
View File
@@ -0,0 +1,985 @@
# Charybdis - Architecture
**Version**: 2.0
**Last Updated**: 2026-05-06
**Status**: Living Document
> For the product vision and roadmap, see [VISION.md](../VISION.md).
---
## Executive Summary
**Charybdis** is a security-native platform engineering tool that unifies software catalog, vulnerability management, and compliance posture in a single event-driven platform. Built in Rust, deployed as a single binary.
### Core Value Proposition
**Problem**:
- Platform engineers run 5+ disconnected tools (Backstage, DefectDojo, Dependency-Track, license scanners, compliance spreadsheets)
- Software catalogs rely on static YAML files that drift from reality
- Security posture is invisible at the catalog level — vulns live in separate tools with no link to services
- Compliance evidence is assembled manually from fragmented data sources
**Solution**:
- **Dynamic software catalog** — event-driven, gRPC-native, no static YAML
- **Native vulnerability management** — ingest scan results (SARIF, CycloneDX, SPDX), triage, assess, track
- **Security gates & compliance** — severity thresholds, license policies, compliance framework mappings
- **Plugin system for integrations** — Slack, Jira, GitHub, custom tools react to events
- **Single binary deployment** — Rust + PostgreSQL, ~50MB RAM, sub-millisecond latency
### Use Case Examples
**Service registration with auto-provisioning:**
```
CI/CD Pipeline (Python) → gRPC CreateEntity(kind=Component)
Charybdis persists entity
Event: EntityCreated published
┌────────────────────┴────────────────────┐
↓ ↓
Jira Plugin Slack Plugin
- Creates onboarding epic - Notifies #platform channel
↓ ↓
└────────────────────┬────────────────────┘
Entity visible via gRPC API / Backstage YAML adapter
```
**Vulnerability ingestion with security gates:**
```
Scanner (Trivy) → CI/CD → gRPC IngestScan(sarif_report)
Charybdis parses SARIF
Creates Vulnerability entities linked to Component
Rules engine evaluates auto-assessment
Security gate checks thresholds
┌────────────────────┴────────────────────┐
↓ ↓
Gate PASSED Gate FAILED
- Vulns tracked - Slack alert to #security
- Dashboard updated - Jira ticket created
- CI/CD pipeline blocked
```
---
## Architectural Principles
### 1. **No Database Migrations Ever**
The database schema is created once on first startup and **never changes**.
**How it works**:
- Entities stored as protobuf bytes in `entity_data BYTEA` column
- Plugins add new entity types via protobuf `oneof` variants
- Build system regenerates code, database schema remains static
**Benefits**:
- Deploy new plugins without downtime
- No migration scripts to manage
- Forward/backward compatibility built-in
- Easy rollback (protobuf versioning)
### 2. **gRPC First, Everything Else is Adapter**
Charybdis provides **only gRPC APIs**. All other interfaces (REST, YAML, GraphQL) are adapters on top.
**Rationale**:
- **Strong typing**: Protobuf ensures type safety across all languages
- **Performance**: Binary protocol, efficient serialization
- **Multi-language**: Official gRPC clients for 10+ languages
- **Streaming**: Built-in support for real-time updates (future)
- **Code generation**: Automatic client/server code generation
**What Charybdis provides**:
- ✅ gRPC API with protobuf definitions
- ✅ YAML adapter endpoints (for Backstage compatibility/migration)
- ❌ REST API (teams can add if needed via gateway)
- ❌ GraphQL (teams can add if needed)
### 3. **Plugin-Based Extensibility**
Plugins are **compile-time integrated** Rust crates that:
1. **Define entity types** via protobuf schemas
2. **React to events** via EventHandler trait
3. **Call external APIs** to synchronize state
4. **Store references** in entity annotations
5. **(Future) Extend gRPC API** with custom endpoints
**Plugin characteristics**:
- Not hot-swappable (require rebuild)
- Type-safe at compile time
- No runtime dependency resolution
- Configurable via YAML config file
- Can be developed by community
### 4. **Event-Driven Orchestration**
Events are published **after** entities are persisted:
```
CRUD Operation → Persist to DB → Publish Event → Plugins React
```
**Event flow**:
1. Entity created/updated/deleted in database
2. Event published to event bus with full entity data
3. All subscribed plugin handlers receive event
4. Plugins filter events they care about
5. Plugins perform asynchronous actions
6. Plugins update entity annotations (via repository)
**Error handling**:
- Plugin errors **do not** fail the CRUD operation
- Entity is already persisted before event publishing
- Plugins log errors for observability
- Retries handled by event bus backend
### 5. **Backstage Compatibility by Design**
Charybdis entity model is **100% compatible** with Backstage's descriptor format.
**How it works**:
- Core protobuf types mirror Backstage YAML structure
- YAML adapter endpoints serve entities in Backstage format
- Backstage reads Charybdis as a dynamic location provider
- No changes needed to Backstage frontend
**Key compatibility points**:
- Entity `kind`, `apiVersion`, `metadata`, `spec` structure
- Annotation format (reverse-DNS style)
- Relationship types (`dependsOn`, `partOf`, etc.)
- All standard Backstage entity kinds supported
---
## System Architecture
### Component Overview
```
┌─────────────────────────────────────────────────────────────────┐
│ Client Layer │
│ CI/CD Pipelines (Python, Go, Node.js, Bash, etc.) │
│ Security Scanners (SARIF, CycloneDX, SPDX output) │
│ IaC Tools (Terraform, Pulumi, Crossplane) │
│ Backstage (via YAML adapter, optional) │
└────────────────┬────────────────────────────────────────────────┘
│ gRPC / YAML HTTP
┌─────────────────────────────────────────────────────────────────┐
│ Charybdis Core │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ gRPC API │ │
│ │ EntityService: CRUD + List (with field masks) │ │
│ │ ScanService: IngestScan (SARIF, CycloneDX, SPDX) │ │
│ │ AssessmentService: Triage, Accept, Remediate │ │
│ └────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────▼─────────────────────────────────────┐ │
│ │ Security Engine (native) │ │
│ │ - Vulnerability tracking per entity │ │
│ │ - Rules engine (auto-assessment) │ │
│ │ - Security gates (severity thresholds) │ │
│ │ - License compliance │ │
│ └────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────▼─────────────────────────────────────┐ │
│ │ Adapters │ │
│ │ - YAML Adapter (Backstage compat / migration) │ │
│ └────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────▼─────────────────────────────────────┐ │
│ │ Repository Layer │ │
│ │ - PostgreSQL with protobuf storage │ │
│ │ - JSONB annotations for fast queries │ │
│ │ - Zero-migration schema │ │
│ └────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────▼─────────────────────────────────────┐ │
│ │ Event Bus System │ │
│ │ - Memory backend (dev) │ │
│ │ - Redis backend (prod, future) │ │
│ └────────────────────┬─────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│ Entity / Vulnerability / Gate events
┌─────────────────────────────────────────────────────────────────┐
│ Plugin Layer (integrations) │
│ ┌──────────────┐ ┌──────────────┐ ┌────────────────────────┐ │
│ │ Slack/Teams │ │ Jira/GitHub │ │ Custom Plugins │ │
│ │ Notifications │ │ Issue Track │ │ Your integrations │ │
│ └──────┬────────┘ └──────┬───────┘ └──────────┬─────────────┘ │
└─────────┼──────────────────┼─────────────────────┼──────────────┘
▼ ▼ ▼
Slack/Teams API Jira/GitHub API External APIs
```
### Data Flow
#### Entity Creation Flow
```
1. Client (CI/CD)
↓ gRPC CreateEntityRequest
2. Charybdis gRPC API
↓ Validate entity
↓ Generate UUID, timestamps
3. Entity Repository
↓ Serialize to protobuf bytes
↓ INSERT INTO entities
4. PostgreSQL
↓ Entity persisted
5. Event Bus
↓ Publish EntityCreated event (with full entity data)
6. Plugin Handlers (parallel)
├─ DefectDojo: Create product → Store product_id in annotations
├─ DependencyTrack: Create project → Store project_uuid in annotations
└─ Custom: Custom actions
7. Repository.update_annotations()
↓ Merge annotations from plugins
↓ UPDATE entities SET annotations = ...
8. PostgreSQL
↓ Entity updated with external tool IDs
9. Return CreateEntityResponse to client
```
#### Backstage Integration Flow
```
1. Backstage Catalog Backend
↓ HTTP GET /yaml/locations
2. Charybdis YAML Adapter
↓ Query repository.list_all()
↓ Convert entities to Location YAML
3. Backstage receives location list
↓ Fetches each entity: GET /yaml/entities/:id
4. Charybdis YAML Adapter
↓ repository.get_by_id()
↓ Convert entity protobuf → Backstage YAML
5. Backstage ingests entities
↓ Displays in UI with all annotations
↓ Shows linked DefectDojo products, etc.
```
---
## Core Entity Model
### Protobuf Schema
```protobuf
message Entity {
// System-managed fields
string id = 1; // UUID (auto-generated)
google.protobuf.Timestamp created_at = 21;
google.protobuf.Timestamp updated_at = 22;
// Backstage-compatible fields
string api_version = 2; // "backstage.io/v1alpha1"
string kind = 3; // "Component", "API", "User", etc.
// Polymorphic metadata (identifying information)
oneof metadata {
// Core types (built-in)
ComponentMetadata component_metadata = 10;
APIMetadata api_metadata = 11;
UserMetadata user_metadata = 12;
GroupMetadata group_metadata = 13;
SystemMetadata system_metadata = 14;
DomainMetadata domain_metadata = 15;
ResourceMetadata resource_metadata = 16;
LocationMetadata location_metadata = 17;
TemplateMetadata template_metadata = 18;
// Plugin types (dynamically added)
// Example: DefectDojoProductMetadata defectdojo_product_metadata = 100;
}
// Polymorphic spec (configuration and behavior)
oneof spec {
// Core types (built-in)
ComponentSpec component_spec = 10;
APISpec api_spec = 11;
UserSpec user_spec = 12;
GroupSpec group_spec = 13;
SystemSpec system_spec = 14;
DomainSpec domain_spec = 15;
ResourceSpec resource_spec = 16;
LocationSpec location_spec = 17;
TemplateSpec template_spec = 18;
// Plugin types (dynamically added)
// Example: DefectDojoProductSpec defectdojo_product_spec = 100;
}
// Annotations - external tool references
// Format: "tool.com/resource-type-attribute"
// Examples:
// "defectdojo.com/product-id": "42"
// "dependencytrack.com/project-uuid": "550e8400-..."
// "github.com/repo-slug": "myorg/myrepo"
map<string, string> annotations = 20;
}
```
### Backstage Entity Kinds Mapping
All Backstage entity kinds must be supported:
| Backstage Kind | Charybdis Proto Type | Status |
|----------------|---------------------|--------|
| Component | ComponentMetadata/Spec | ✅ Done |
| Service | ServiceMetadata/Spec | ✅ Done |
| API | APIMetadata/Spec | ✅ Done |
| User | UserMetadata/Spec | ✅ Done |
| Group | GroupMetadata/Spec | ✅ Done |
| System | SystemMetadata/Spec | ✅ Done |
| Domain | DomainMetadata/Spec | ✅ Done |
| Resource | ResourceMetadata/Spec | ✅ Done |
| Location | LocationMetadata/Spec | Not planned (entities are event-driven, not file-based) |
| Template | TemplateMetadata/Spec | Phase 3 (Scaffolder) |
---
## YAML Adapter Layer
### Purpose
Serve entities in Backstage-compatible YAML format over HTTP.
### API Specification
#### 1. List Locations
**Endpoint**: `GET /yaml/locations`
**Purpose**: Return a dynamic Location entity that lists all entities in Charybdis.
**Response Format** (Backstage Location YAML):
```yaml
apiVersion: backstage.io/v1alpha1
kind: Location
metadata:
name: charybdis-all-entities
description: Dynamic location managed by Charybdis
spec:
type: charybdis
targets:
- http://charybdis.company.com/yaml/entities/service-payment
- http://charybdis.company.com/yaml/entities/service-auth
- http://charybdis.company.com/yaml/entities/api-payments-v1
- http://charybdis.company.com/yaml/entities/user-john-doe
# ... all entities
```
**Implementation Notes**:
- Query `SELECT id, kind FROM entities`
- Generate URL for each entity: `/yaml/entities/{kind}-{id}`
- Return as Backstage Location YAML
- Cache response (invalidate on entity CRUD)
#### 2. Get Entity by ID
**Endpoint**: `GET /yaml/entities/:id`
**Purpose**: Return a specific entity in Backstage YAML format.
**Response Format** (Backstage Component YAML):
```yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: payment-service
namespace: production
description: Core payment processing service
annotations:
github.com/repo-slug: myorg/payment-service
defectdojo.com/product-id: "42"
dependencytrack.com/project-uuid: "550e8400-e29b-41d4-a716-446655440000"
tags:
- payments
- critical
links:
- url: https://dashboard.company.com/payment-service
title: Dashboard
icon: dashboard
spec:
type: service
lifecycle: production
owner: payments-team
system: payment-system
dependsOn:
- component:auth-service
providesApis:
- payments-api-v1
```
**Implementation Notes**:
- Query `repository.get_by_id(id)`
- Convert Entity protobuf → Backstage YAML
- Map `oneof metadata/spec` to appropriate YAML structure
- Include all annotations
- Return as `text/yaml` content type
#### 3. Configuration in Backstage
**Backstage app-config.yaml**:
```yaml
catalog:
locations:
- type: url
target: http://charybdis.company.com/yaml/locations
rules:
- allow: [Component, API, User, Group, System, Domain, Resource]
```
**How it works**:
1. Backstage fetches `/yaml/locations` on catalog refresh
2. Parses Location YAML to get entity URLs
3. Fetches each entity URL (`/yaml/entities/:id`)
4. Ingests entities into Backstage catalog
5. Displays in UI with all metadata and annotations
---
## Plugin System
### Plugin Architecture
Charybdis supports **two types of plugins**, both compile-time integrated:
#### **1. Event-Driven Plugins**
React to entity lifecycle events (create, update, delete):
- **Example**: DefectDojo, DependencyTrack
- **Implement**: `EventDrivenPlugin` trait
- **Provide**: `ResourceHandler` implementations
- **Triggered by**: Entity CRUD operations
#### **2. Sync Plugins**
Pull data from external sources on a schedule:
- **Example**: Okta, Keycloak, Active Directory
- **Implement**: `SyncPlugin` trait
- **Scheduled by**: Cron expressions
- **Triggered by**: Time-based schedule or manual API call
### Generic Plugin Utilities (2025-01-15)
All plugins have access to reusable utilities in `src/plugins/`:
#### **PluginHttpClient**
Generic HTTP client with multiple auth methods:
- Token authentication (DefectDojo)
- Bearer authentication (modern APIs)
- API Key authentication (DependencyTrack)
- Basic authentication (JIRA, Bitbucket)
- Methods: `get()`, `post()`, `put()`, `patch()`, `delete()`
#### **AnnotationHelper**
Consistent API for storing/retrieving plugin metadata:
- `set_id()` / `get_id()` - Store external resource IDs
- `set()` / `get()` - Store arbitrary metadata
- Enforces naming: `{plugin}.com/{resource}-id`
- Prevents annotation conflicts between plugins
#### **FieldMapper**
Maps entity fields to external tool formats:
- Direct field paths: `"metadata.name"`
- Static values: `{"value": "Web Application"}`
- Complex mappings with entity resolution
- Repository integration for cross-entity queries
#### **DateUtils**
Common date/time operations:
- `today()`, `today_plus_days(n)` - Date formatting
- `engagement_date_range(days)` - For time-bound resources
- `iso_timestamp()` - ISO 8601 timestamps
### Plugin Responsibilities
Plugins can:
1. **Define custom entity types** (via protobuf)
2. **React to entity events** (via ResourceHandler trait)
3. **Call external APIs** (using PluginHttpClient)
4. **Store metadata** (using AnnotationHelper)
5. **Map fields** (using FieldMapper)
6. **Schedule syncs** (SyncPlugins with cron)
7. **(Future) Extend gRPC API** (custom services)
### Plugin Lifecycle
```
1. Development
├─ Define protobuf schema (metadata/spec messages)
├─ Implement EventHandler trait
├─ Implement external API client
└─ Add configuration schema
2. Registration
├─ Add to plugins.toml (enabled = true)
└─ Add to Cargo.toml dependencies
3. Build Time
├─ build.rs reads plugins.toml
├─ Generates entities.proto with plugin types
├─ Compiles all protobuf to Rust code
└─ Links plugin into binary
4. Runtime
├─ Load plugin configuration from config file
├─ Initialize plugin handler
├─ Subscribe to event bus
└─ React to entity events
```
### Plugin Configuration System
**File**: `config.toml` (TOML format with `${VAR}` env var substitution)
```toml
# Server configuration
[server]
grpc_host = "[::1]"
grpc_port = 50051
[server.yaml_adapter]
enabled = true
host = "0.0.0.0"
port = 8080
# Database
[database]
url = "${DATABASE_URL}"
max_connections = 10
# Plugin configurations
[plugins.defectdojo]
enabled = true
base_url = "${DEFECTDOJO_API_URL}"
api_token = "${DEFECTDOJO_API_TOKEN}"
[plugins.defectdojo.default_engagement]
auto_create = true
name = "CI/CD Pipeline"
engagement_type = "CI/CD"
[plugins.keycloak]
enabled = false
# base_url = "https://keycloak.company.com"
# realm = "master"
[plugins.dependencytrack]
enabled = false
# base_url = "https://dependencytrack.company.com"
# api_key = "${DEPENDENCYTRACK_API_KEY}"
```
See `config.toml.example` for the full reference with all options documented.
**Configuration loading** (`src/config.rs`):
```rust
pub struct Config {
pub server: ServerConfig,
pub database: DatabaseConfig,
pub security: SecurityConfig,
pub telemetry: TelemetryConfig,
pub plugins: PluginsConfig,
}
impl Config {
pub fn from_file(path: &str) -> Result<Self> {
let content = std::fs::read_to_string(path)?;
// Substitute ${VAR} with environment variable values
let content = substitute_env_vars(&content);
let config: Config = toml::from_str(&content)?;
Ok(config)
}
}
```
**Each plugin implements**:
```rust
pub trait Plugin {
fn name(&self) -> &str;
fn plugin_type(&self) -> PluginType;
fn load_config(&mut self, config: PluginConfig) -> Result<()>;
fn validate_config(&self) -> Result<()>;
}
```
### Event Handler Behavior Configuration
Plugins can configure different behaviors based on:
- Entity kind
- Event type (created/updated/deleted)
- Custom conditions
**Example**: DefectDojo plugin config (`config.toml`)
```toml
[plugins.defectdojo]
enabled = true
base_url = "${DEFECTDOJO_API_URL}"
api_token = "${DEFECTDOJO_API_TOKEN}"
[plugins.defectdojo.default_engagement]
auto_create = true
name = "CI/CD Pipeline"
engagement_type = "CI/CD"
```
**Handler Implementation**:
```rust
impl EventHandler for DefectDojoHandler {
async fn handle_event(&self, event: &EntityEvent) -> EventResult<()> {
// Check if plugin enabled
if !self.config.enabled {
return Ok(());
}
// Match event type to configured behavior
let behavior = match event.event_type {
EntityEventType::Created => &self.config.on_entity_created,
EntityEventType::Updated => &self.config.on_entity_updated,
EntityEventType::Deleted => &self.config.on_entity_deleted,
};
// Find matching rule for this entity kind
for rule in behavior {
if rule.matches(&event) {
return self.execute_action(&rule.action, event).await;
}
}
Ok(())
}
}
```
---
## Future: Plugin API Extensions
**Future capability**: Plugins can expose custom gRPC endpoints.
### Example: DefectDojo Query Plugin
**Plugin extends API with custom service**:
```protobuf
// plugins/defectdojo/proto/defectdojo_service.proto
service DefectDojoService {
// Get findings for a specific entity
rpc GetEntityFindings(GetEntityFindingsRequest) returns (GetEntityFindingsResponse);
// Get all active vulnerabilities
rpc GetActiveVulnerabilities(GetActiveVulnerabilitiesRequest) returns (GetActiveVulnerabilitiesResponse);
// Sync entity with DefectDojo
rpc SyncEntity(SyncEntityRequest) returns (SyncEntityResponse);
}
message GetEntityFindingsRequest {
string entity_id = 1;
string severity_filter = 2; // "Critical", "High", etc.
bool active_only = 3;
}
message GetEntityFindingsResponse {
repeated Finding findings = 1;
int32 total_count = 2;
}
message Finding {
int32 id = 1;
string title = 2;
string severity = 3;
string description = 4;
google.protobuf.Timestamp date = 5;
bool active = 6;
}
```
**Implementation**:
```rust
pub struct DefectDojoServiceImpl {
client: DefectDojoClient,
repository: EntityRepository,
}
#[async_trait]
impl DefectDojoService for DefectDojoServiceImpl {
async fn get_entity_findings(
&self,
request: Request<GetEntityFindingsRequest>,
) -> Result<Response<GetEntityFindingsResponse>, Status> {
let req = request.into_inner();
// Get entity to retrieve DefectDojo product ID
let entity = self.repository.get_by_id(&req.entity_id).await?;
let product_id = entity
.annotations
.get("defectdojo.com/product-id")
.ok_or_else(|| Status::not_found("No DefectDojo product linked"))?;
// Fetch findings from DefectDojo API
let findings = self.client
.get_findings(product_id, &req.severity_filter, req.active_only)
.await?;
Ok(Response::new(GetEntityFindingsResponse {
findings: findings.into_iter().map(|f| f.into()).collect(),
total_count: findings.len() as i32,
}))
}
}
```
**Registration in main.rs**:
```rust
// Register plugin's custom gRPC service
if defectdojo_plugin.is_enabled() {
let defectdojo_service = DefectDojoServiceServer::new(
DefectDojoServiceImpl::new(defectdojo_client, repository.clone())
);
server = server.add_service(defectdojo_service);
}
```
**Client usage**:
```bash
grpcurl -d '{
"entity_id": "service-payment",
"severity_filter": "Critical",
"active_only": true
}' localhost:50051 charybdis.plugins.defectdojo.DefectDojoService/GetEntityFindings
```
---
## Technology Decisions
### Why gRPC Only?
**Decision**: Charybdis core provides only gRPC. Teams add REST/GraphQL as needed.
**Rationale**:
- **Strong typing**: Protobuf schemas ensure correctness across languages
- **Performance**: Binary protocol, efficient for CI/CD use cases
- **Multi-language**: Official clients for Python, Go, Node.js, Java, Rust, C++, C#, etc.
- **Streaming**: Built-in bidirectional streaming (future: real-time updates)
- **Code generation**: Automatic client generation eliminates boilerplate
- **Focus**: Core team maintains one high-quality API surface
**Teams can add REST if needed**:
```
grpc-gateway or Envoy → Charybdis gRPC
```
### Why PostgreSQL + JSONB?
**Decision**: Use PostgreSQL with protobuf BYTEA storage and JSONB annotations.
**Rationale**:
- **Scale**: Handles 1000s of entities easily (tested: 170k resources)
- **JSONB performance**: GIN indexes enable fast annotation queries
- **Familiarity**: Most teams already run PostgreSQL
- **Reliability**: ACID transactions, mature ecosystem
- **Future-proof**: Can add read replicas, partitioning if needed
**Alternative considered**: MongoDB
- **Verdict**: PostgreSQL with JSONB provides same flexibility with better consistency guarantees
### Why Compile-Time Plugins?
**Decision**: Plugins are compile-time integrated, not runtime loaded.
**Rationale**:
- **Type safety**: Rust compiler ensures correctness
- **Performance**: No dynamic loading overhead
- **Simplicity**: No plugin version compatibility matrix
- **Security**: No arbitrary code execution
- **Trade-off**: Requires rebuild to add plugins (acceptable for infrastructure tool)
**Community plugins**: Published as crates, teams include in their build.
### Why Event-Driven?
**Decision**: Plugins react to events after entity persistence.
**Rationale**:
- **Decoupling**: Plugins can't break core CRUD operations
- **Resilience**: Plugin failures are logged, not propagated
- **Async**: Long-running operations don't block API
- **Extensibility**: Add plugins without modifying core
- **Observability**: Events provide audit trail
**Trade-off**: Plugins can't prevent entity creation (validation must be in core or client).
---
## Scale Estimates
### Reference Deployment
**Company**: ~100 engineers
**Backstage Entities**:
- Components: ~1,000
- APIs: ~1,000
- Users: ~700
- Groups: ~200
- Systems: ~50
- Domains: ~150
- Resources: ~170,000
**Charybdis Requirements**:
- PostgreSQL: ~500MB storage (with JSONB annotations)
- Memory: ~256MB for Charybdis service
- CPU: Minimal (mostly I/O bound)
**10x Scale** (1,000 engineers, 1.7M resources):
- PostgreSQL: ~5GB storage
- Memory: ~512MB
- CPU: Still minimal with proper indexing
**Bottlenecks**:
- JSONB queries on annotations (solved with GIN indexes)
- Event bus throughput (solved with Redis/RabbitMQ backend)
- Plugin external API rate limits (solved with plugin-level queuing)
---
## Development Roadmap
> See [VISION.md](VISION.md) for the full product vision and strategic roadmap.
### Phase 0: Catalog Foundation (Done)
- ✅ Zero-migration database architecture
- ✅ gRPC API with protobuf (full entity CRUD)
- ✅ Event bus system (MemoryEventBus)
- ✅ Event dispatcher architecture (generic + plugin-specific)
- ✅ All Backstage entity kinds (Component, System, API, User, Group, Domain, Resource)
- ✅ YAML adapter endpoints (Backstage compatibility)
- ✅ gRPC field masks for partial updates
- ✅ OpenTelemetry observability (traces, metrics, logs)
- ✅ mTLS + RBAC security framework
- ✅ Config loading from TOML with env var substitution
- ✅ Plugin trait architecture (EventDriven + Sync)
- ✅ Generic plugin utilities (HttpClient, AnnotationHelper, FieldMapper, DateUtils)
- ✅ DefectDojo plugin (products, engagements, owner resolution)
- ✅ Keycloak plugin (user/group sync with annotations)
### Phase 1: Security Core (Current Focus)
**Goal**: Native vulnerability management and scan ingestion.
- [ ] Vulnerability/Observation entity kind (protobuf + storage)
- [ ] ScanService gRPC endpoint for scan ingestion
- [ ] SARIF parser (covers majority of modern scanners)
- [ ] CycloneDX parser (SBOMs + vulnerability data)
- [ ] SPDX parser (license data)
- [ ] Assessment workflow (triage, accept risk, remediate)
- [ ] Rules engine for auto-assessment
- [ ] Security gates (severity thresholds per product)
- [ ] License tracking and policy engine
### Phase 2: Compliance & Integrations
**Goal**: Compliance frameworks and integration plugins.
- [ ] Compliance framework mappings (NIS2, SOC2, DORA)
- [ ] VEX document support (CSAF, OpenVEX)
- [ ] Export/reporting (PDF, Excel)
- [ ] Notification plugins (Slack, Teams, email)
- [ ] Issue tracker plugins (Jira, GitHub, GitLab)
- [ ] Redis event bus backend
### Phase 3: Scaffolder & Ecosystem
**Goal**: Service scaffolding and community growth.
- [ ] Service scaffolder (Git-native templates)
- [ ] Event-driven provisioning on scaffold
- [ ] Plugin SDK documentation
- [ ] Helm chart & 1-click deploy
- [ ] Community plugin registry
---
## Success Criteria
### Phase 1 Success (Security Core)
- [ ] Ingest SARIF scan results via gRPC
- [ ] Vulnerabilities linked to catalog entities
- [ ] Rules engine auto-assesses based on severity/component
- [ ] Security gates block on threshold violations
- [ ] Assessment workflow (triage → accept/remediate)
- [ ] License data ingested from CycloneDX/SPDX
### Production Success
- [ ] 1,000+ entities managed with security posture
- [ ] <100ms p99 latency for CRUD operations
- [ ] Single `helm install` deployment
- [ ] Documentation complete
- [ ] 3+ integration plugins in production
---
## Key Decisions Summary
| Decision | Choice | Rationale |
|----------|--------|-----------|
| **API Protocol** | gRPC only | Strong typing, multi-language, performance |
| **Frontend** | Backstage YAML adapter | Compatible with existing Backstage deployments |
| **Security features** | Native (core) | First-class, not plugins. Vulns, compliance, gates in the core |
| **External integrations** | Plugins | Slack, Jira, etc. as event-driven plugins |
| **Database** | PostgreSQL + JSONB | Scale, familiarity, ACID |
| **Plugin Loading** | Compile-time | Type safety, performance, security |
| **Event Model** | Post-persistence | Resilience, decoupling |
| **Schema Evolution** | Protobuf only | Zero migrations |
| **Configuration** | TOML + env vars | Flexibility, Rust-native |
| **Deployment** | Single binary + Docker + K8s | Cloud-native, minimal footprint |
---
## References
- [Backstage Descriptor Format](https://backstage.io/docs/features/software-catalog/descriptor-format)
- [gRPC Documentation](https://grpc.io/docs/)
- [Protocol Buffers Guide](https://protobuf.dev/)
- [PostgreSQL JSONB](https://www.postgresql.org/docs/current/datatype-json.html)
---
## Document Version History
| Version | Date | Changes |
|---------|------|---------|
| 1.0 | 2025-01-03 | Initial comprehensive architecture document |
| 1.1 | 2025-01-15 | Added dual plugin architecture, generic utilities, updated roadmap |
| 2.0 | 2026-04-02 | Pivoted to security-native platform engineering tool. Extracted product vision to VISION.md. Added native vulnerability management, compliance frameworks to architecture. Updated roadmap and success criteria. |
---
**This document covers architecture and technical decisions.**
For product vision and strategic direction, see [VISION.md](VISION.md).
All code and documentation must align with these documents.
+628
View File
@@ -0,0 +1,628 @@
# Core Concepts
Charybdis is a **security-native platform engineering tool** that unifies software catalog, vulnerability management, and compliance posture behind a gRPC API. When entities change or scan results are ingested, the event bus triggers rules evaluation, security gate checks, and plugin integrations automatically.
This page explains the key concepts you need to understand.
## Architecture Overview
```mermaid
graph TB
subgraph "Sources"
A1[CI/CD Pipelines]
A2[Security Scanners]
A3[IaC Tools]
end
subgraph "Charybdis Core"
B1[gRPC API]
B3[Authentication & Authorization]
C1[Entity Service]
C4[Vulnerability Engine]
C5[Security Gates & Rules]
C2[Event Bus]
end
subgraph "Integrations - Plugins"
D1[Slack / Teams]
D2[Jira / GitHub]
D3[Custom Plugins]
end
subgraph "Data Layer"
E1[(PostgreSQL)]
end
A1 -.gRPC.-> B1
A2 -.Scan Results.-> B1
A3 -.gRPC.-> B1
B1 --> B3
B3 --> C1
B3 --> C4
C4 --> C5
C1 --> E1
C4 --> E1
C1 --> C2
C4 --> C2
C2 -.Events.-> D1
C2 -.Events.-> D2
C2 -.Events.-> D3
style C4 fill:#E24A4A,stroke:#8A2E2E,color:#fff
style C5 fill:#E2884A,stroke:#8A5C2E,color:#fff
style C2 fill:#4A90E2,stroke:#2E5C8A,color:#fff
```
## Entities
An **entity** is the core data model in Charybdis, representing any cataloged item in your software ecosystem.
### Entity Structure
Every entity has three main parts:
```mermaid
classDiagram
class Entity {
+string id
+string kind
+metadata
+spec
+annotations
+timestamps
}
class Metadata {
+string name
+string namespace
+string description
+labels
+links
+tags
}
class Spec {
+string type
+string lifecycle
+string owner
+dependencies
}
Entity --> Metadata
Entity --> Spec
```
### Entity Kinds
Charybdis supports all standard Backstage entity kinds:
| Kind | Description | Example |
|------|-------------|---------|
| **Service** | Individual microservices or applications | `payment-api` |
| **Component** | Reusable libraries, SDKs, modules | `auth-sdk` |
| **System** | Collections of services working together | `e-commerce-platform` |
| **API** | Interfaces exposed by components | `payments-rest-api` |
| **User** | Individual people | `john.doe` |
| **Group** | Teams and organizational units | `team-payments` |
| **Domain** | Business domains | `payments`, `shipping` |
| **Resource** | Infrastructure resources | `payments-db`, `cache-cluster` |
#### Example: Service
```json
{
"kind": "Service",
"service_metadata": {
"name": "payment-api",
"namespace": "production",
"description": "Payment processing service",
"labels": { "team": "payments" }
},
"service_spec": {
"type": "service",
"lifecycle": "production",
"owner": "team-payments",
"system": "payment-system"
}
}
```
#### Example: Component
```json
{
"kind": "Component",
"component_metadata": {
"name": "auth-sdk",
"namespace": "shared"
},
"component_spec": {
"type": "library",
"lifecycle": "production",
"owner": "platform-team"
}
}
```
#### Example: System
```json
{
"kind": "System",
"system_metadata": {
"name": "e-commerce-platform",
"namespace": "production",
"description": "Complete e-commerce system"
},
"system_spec": {
"owner": "platform-team",
"domain": "retail"
}
}
```
### Metadata Fields
| Field | Type | Description | Required |
|-------|------|-------------|----------|
| `name` | string | Entity name (unique within namespace) | ✅ |
| `namespace` | string | Logical grouping (e.g., "production", "staging") | ✅ |
| `description` | string | Human-readable description | ❌ |
| `labels` | map | Key-value pairs for categorization | ❌ |
| `tags` | array | Search tags | ❌ |
| `links` | array | External URLs (dashboards, docs, etc.) | ❌ |
### Annotations
Annotations store integration-specific metadata:
```json
{
"annotations": {
"github.com/repo-slug": "myorg/payment-service",
"defectdojo.com/product-id": "123",
"dependencytrack.com/project-uuid": "550e8400...",
"pagerduty.com/service-id": "PXYZ123",
"grafana.com/dashboard-url": "https://..."
}
}
```
**Best Practices**:
- Use domain-style keys (`tool.com/key`)
- Store tool-specific IDs
- Keep values as strings
- Use for integration metadata only
## Events
Charybdis uses an **event-driven architecture** to trigger actions when entities change.
### Event Flow
```mermaid
sequenceDiagram
participant Client
participant API as Entity Service
participant Bus as Event Bus
participant Plugin1 as DefectDojo Plugin
participant Plugin2 as Dependency-Track
Client->>API: CreateEntity(service)
API->>API: Store entity
API->>Bus: Emit EntityCreated event
Bus->>Plugin1: Handle event
Bus->>Plugin2: Handle event
Plugin1-->>Plugin1: Create DefectDojo product
Plugin2-->>Plugin2: Create DT project
API-->>Client: Return created entity
Note over Bus,Plugin2: Asynchronous processing
```
### Event Types
| Event | Trigger | Plugins Receive |
|-------|---------|----------------|
| `EntityCreated` | New entity created | Full entity data |
| `EntityUpdated` | Entity modified | Updated entity + changes |
| `EntityDeleted` | Entity removed | Entity ID + metadata |
### Event Structure
```rust
pub enum EntityEvent {
Created {
entity: Entity,
timestamp: DateTime<Utc>,
},
Updated {
entity: Entity,
previous: Entity,
timestamp: DateTime<Utc>,
},
Deleted {
id: String,
metadata: Metadata,
timestamp: DateTime<Utc>,
},
}
```
## Vulnerabilities & Security (Phase 1 — Planned)
> **Note**: The features described in this section are part of Phase 1 (Security Core) and are not yet implemented. This documents the planned architecture. See [VISION.md](../VISION.md) for the roadmap.
Security is a **first-class concept** in Charybdis, not a plugin. Once Phase 1 is complete, vulnerability management, scan ingestion, security gates, and assessment workflows will be native to the core.
### Vulnerability Lifecycle
```mermaid
sequenceDiagram
participant Scanner
participant API as Charybdis API
participant Rules as Rules Engine
participant Gate as Security Gate
participant Bus as Event Bus
participant Plugin as Slack / Jira
Scanner->>API: IngestScan(SARIF report)
API->>API: Parse & create Vulnerability entities
API->>API: Link vulnerabilities to Component
API->>Rules: Evaluate auto-assessment rules
Rules-->>API: Auto-assess (e.g., accept known low-risk)
API->>Gate: Check security gate thresholds
alt Gate Passed
Gate-->>API: OK
else Gate Failed
Gate->>Bus: GateFailed event
Bus->>Plugin: Alert #security channel
end
API->>Bus: VulnerabilitiesIngested event
Bus->>Plugin: Notify / create tickets
```
### Scan Ingestion
Charybdis ingests scan results natively. You don't need an external vulnerability management tool.
| Format | Coverage | Use Case |
|--------|----------|----------|
| **SARIF** | 60%+ of modern scanners (Semgrep, CodeQL, Trivy, etc.) | SAST, DAST, secrets |
| **CycloneDX** | SBOMs + vulnerability data | SCA, license |
| **SPDX** | License and package data | License compliance |
### Assessments
Each vulnerability can be assessed:
| Status | Meaning |
|--------|---------|
| **Open** | New, unreviewed vulnerability |
| **In Triage** | Under review by security team |
| **Accepted** | Risk accepted with justification |
| **Remediated** | Fixed, pending verification |
| **False Positive** | Not a real vulnerability |
| **Auto-Assessed** | Automatically assessed by rules engine |
### Security Gates
Security gates define thresholds per product:
```
payment-api:
critical: 0 # No critical vulns allowed
high: 5 # Up to 5 high
medium: 20 # Up to 20 medium
```
When a gate is violated, events fire and plugins react (block CI/CD, alert Slack, create Jira tickets).
### Rules Engine
Rules auto-assess vulnerabilities based on patterns:
- Severity + component combination (e.g., "low severity in test dependencies → auto-accept")
- Scanner source (e.g., "all informational from ZAP → auto-accept")
- Known patterns (e.g., "CVE-XXXX already accepted org-wide")
### License Compliance (Phase 2)
Track licenses across your dependency tree:
- Ingest license data from CycloneDX/SPDX
- Define license policies (allowed, restricted, banned)
- Flag violations per component
- Compliance reporting
## Storage Model
Charybdis uses PostgreSQL with JSONB for schema-less storage.
### Database Schema
```sql
CREATE TABLE entities (
id UUID PRIMARY KEY,
kind TEXT NOT NULL,
entity_data BYTEA NOT NULL, -- Protobuf binary
annotations JSONB NOT NULL DEFAULT '{}', -- Plugin metadata (indexed)
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL
);
CREATE INDEX idx_entities_kind ON entities(kind);
CREATE INDEX idx_entities_annotations ON entities USING GIN(annotations);
```
### Why Protobuf + JSONB?
**Advantages**:
- ✅ No schema migrations — new entity kinds via protobuf `oneof`, schema never changes
- ✅ Protobuf binary storage for compact, versioned entity data
- ✅ JSONB annotations for fast querying with GIN indexes
- ✅ Forward/backward compatibility built-in
**Example Query**:
```sql
-- Find entities with specific annotation
SELECT * FROM entities
WHERE annotations->>'defectdojo.com/product-id' = '42';
```
## Security Model
Charybdis implements defense-in-depth security.
### Authentication: mTLS
```mermaid
sequenceDiagram
participant Client
participant TLS as TLS Layer
participant Auth as Auth Interceptor
participant Service as Entity Service
Client->>TLS: Connect with client certificate
TLS->>TLS: Validate certificate
TLS->>Auth: Extract certificate
Auth->>Auth: Parse identity (CN, OU, O)
Auth->>Auth: Map to role
Client->>Auth: Request (with identity)
Auth->>Auth: Check permissions
alt Authorized
Auth->>Service: Forward request
Service-->>Client: Response
else Denied
Auth-->>Client: PermissionDenied error
end
```
### Authorization: RBAC
**Role Mapping**:
```
Certificate (CN, OU, O) → RBAC Role → Permissions
```
**Default Roles**:
| Role | Certificate OU | Permissions |
|------|---------------|-------------|
| `platform` | `platform-team` | Full access (CRUD + list) |
| `automation` | `automation` | Create, read, update, list |
| `plugin` | `plugins` | Read, list only |
### Permissions
| Permission | Operations | Required For |
|------------|-----------|--------------|
| `entity:create` | Create new entities | CreateEntity |
| `entity:read` | Get entity by ID | GetEntity |
| `entity:update` | Modify entities | UpdateEntity |
| `entity:delete` | Remove entities | DeleteEntity |
| `entity:list` | List all entities | ListEntities |
## Plugin System
Core features (catalog, vulnerabilities, compliance) are **native**. Plugins handle **integrations** with external systems.
### Plugin Architecture
```mermaid
graph LR
A[Entity/Vuln Event] --> B[Event Bus]
B --> C{Plugin Manager}
C --> D[Slack Plugin]
C --> E[Jira Plugin]
C --> F[Custom Plugin]
D --> G[Slack API]
E --> H[Jira API]
F --> I[Your Tool API]
style C fill:#4A90E2,stroke:#2E5C8A
```
### What's Native vs. Plugin
| Native (core) | Status | Plugin (integration) | Status |
|---|---|---|---|
| Software catalog | Done | DefectDojo sync | Done |
| Vulnerability management | Phase 1 | Keycloak user sync | Done |
| Security gates & rules | Phase 1 | Slack / Teams notifications | Planned |
| Assessment workflow | Phase 1 | Jira / GitHub issue creation | Planned |
| License compliance | Phase 2 | Custom integrations | Framework ready |
| TechDocs | Future | | |
### Plugin Lifecycle
1. **Configuration** - Load plugin settings from `plugins.toml`
2. **Initialization** - Plugin registers event handlers
3. **Event Processing** - Plugin receives events asynchronously (entity, vulnerability, gate events)
4. **External Integration** - Plugin calls external tool APIs
5. **Error Handling** - Failed plugins don't affect core service
### Plugin Configuration
Example `plugins.toml`:
```toml
[plugins.slack]
enabled = true
webhook_url = "${SLACK_WEBHOOK_URL}"
[[plugins.slack.on_gate_failed]]
channel = "#security-alerts"
[[plugins.slack.on_entity_created]]
channel = "#platform"
```
See [Plugin Guide](plugins.md) for details.
## Data Consistency
### Eventual Consistency
Charybdis uses **eventual consistency** for plugin integrations:
- Entity CRUD operations are **immediately consistent**
- Plugin synchronization is **eventually consistent**
- Events are processed **asynchronously**
```mermaid
graph LR
A[CreateEntity] -->|Immediate| B[Entity Stored]
B -->|Async| C[Event Emitted]
C -->|Async| D[Plugin Processing]
D -->|Eventual| E[External Tool Synced]
style B fill:#00C851
style E fill:#ffbb33
```
### Guarantees
| Operation | Consistency | Guarantee |
|-----------|-------------|-----------|
| Entity CRUD | Strong | Immediate |
| Entity queries | Strong | Read-your-writes |
| Event delivery | At-least-once | May retry |
| Plugin sync | Eventual | Best-effort |
## Performance Characteristics
### Scalability
- **Entities**: Tested with 100,000+ entities
- **Throughput**: 1,000+ requests/second
- **Latency**: Sub-millisecond average
- **Concurrency**: Tokio async runtime
### Resource Usage
Typical resource consumption:
| Component | CPU | Memory | Storage |
|-----------|-----|--------|---------|
| Charybdis | < 5% | ~50 MB | Minimal |
| PostgreSQL | ~10% | ~256 MB | Depends on entity count |
### Optimization Tips
1. **Index annotations** used for frequent queries
2. **Use connection pooling** (built-in)
3. **Enable query caching** in PostgreSQL
4. **Monitor event bus** queue depth
## Backstage Migration
Charybdis includes a YAML adapter for teams migrating from Backstage. This is a **migration path**, not the primary interface.
### How It Works
```mermaid
graph LR
A[Charybdis Entity] --> B[YAML Adapter]
B --> C[Backstage YAML Format]
C --> D[Backstage Catalog]
style B fill:#4A90E2,stroke:#2E5C8A
```
Point Backstage at Charybdis and stop maintaining `catalog-info.yaml` files:
```yaml
catalog:
locations:
- type: url
target: http://charybdis:8080/yaml/locations
```
### Recommended Migration Path
1. **Start** with Charybdis + YAML adapter feeding your existing Backstage
2. **Adopt** Charybdis gRPC API for CI/CD integrations and security scanning
3. **Leverage** event-driven plugins for auto-provisioning (DefectDojo, Jira, etc.)
4. **Optionally retire** Backstage when Charybdis covers your catalog needs
## Best Practices
### Entity Design
**DO**:
- Use descriptive names
- Group by namespace
- Add relevant labels
- Include documentation links
- Set appropriate owners
**DON'T**:
- Store sensitive data in metadata
- Use very long descriptions
- Create deeply nested hierarchies
- Duplicate data across entities
### Naming Conventions
```
<entity-type>-<purpose>-<environment>
Examples:
- payment-api-prod
- user-service-staging
- auth-library
- e-commerce-system
```
### Metadata Organization
```json
{
"labels": {
"team": "payments", // Ownership
"tier": "critical", // Importance
"environment": "production" // Deployment
},
"tags": ["pci-compliant", "public-api"],
"links": [
{ "url": "...", "title": "Dashboard" },
{ "url": "...", "title": "Documentation" }
]
}
```
## Next Steps
- Read the [Vision & Roadmap](../VISION.md) to understand where Charybdis is going
- Configure [Security](security.md) for production (mTLS + RBAC)
- Explore [Plugins](plugins.md) for external integrations
- Review the [Architecture](architecture.md) for technical deep dive
---
**Questions?** [Open an issue](../../issues).
+508
View File
@@ -0,0 +1,508 @@
# Getting Started with Charybdis
This guide will get you from zero to a running Charybdis instance in minutes. By the end, you'll have a working software catalog that can register services via gRPC, ingest scan results, and auto-trigger integrations via event-driven plugins.
## Prerequisites
- **Rust** - 1.70 or later ([install](https://rustup.rs/))
- **PostgreSQL** - 14 or later
- **grpcurl** - For testing (optional, [install](https://github.com/fullstorydev/grpcurl))
## Installation
### Option 1: From Source
```bash
# Clone the repository
git clone https://github.com/charybdis-catalog/charybdis.git
cd charybdis
# Build
cargo build --release
# The binary will be at target/release/charybdis
```
### Option 2: Docker (Coming Soon)
```bash
docker pull charybdis/charybdis:latest
```
## Quick Start
### Step 1: Start PostgreSQL
Using Docker:
```bash
docker run -d \
--name charybdis-postgres \
-e POSTGRES_PASSWORD=mysecretpassword \
-p 5432:5432 \
postgres:15
```
Or use an existing PostgreSQL instance.
### Step 2: Configure Charybdis
**Recommended: Use config.toml**
Copy the example configuration:
```bash
cp config.toml.example config.toml
```
Edit `config.toml` and set your database URL:
```toml
[database]
url = "${DATABASE_URL}"
```
Set the environment variable:
```bash
export DATABASE_URL="postgresql://postgres:mysecretpassword@localhost:5432/postgres"
```
**Alternative: Environment Variables Only (Legacy)**
If you prefer environment variables:
```bash
# Database
export DATABASE_URL="postgresql://postgres:mysecretpassword@localhost:5432/postgres"
# Disable security for quick start
export SECURITY_MTLS_ENABLED=false
export SECURITY_RBAC_ENABLED=false
# Logging
export RUST_LOG=info,charybdis=debug
```
### Step 3: Run Charybdis
```bash
cargo run
```
You should see:
```
INFO charybdis: Database ready
INFO charybdis: Event bus started successfully
INFO charybdis: EntityService server listening on [::1]:50051
```
### Step 4: Verify It's Working
Test with grpcurl:
```bash
# List available services
grpcurl -plaintext localhost:50051 list
# Output:
# charybdis.entities.EntityService
# grpc.reflection.v1.ServerReflection
```
Congratulations! Charybdis is running! 🎉
## Creating Your First Entity
### Using grpcurl
Create a service entity:
```bash
grpcurl -plaintext \
-d '{
"entity": {
"kind": "Service",
"service_metadata": {
"name": "payment-service",
"namespace": "production",
"description": "Core payment processing service"
}
}
}' \
localhost:50051 charybdis.entities.EntityService/CreateEntity
```
Response:
```json
{
"entity": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"kind": "Service",
"serviceMetadata": {
"name": "payment-service",
"namespace": "production",
"description": "Core payment processing service"
},
"createdAt": "2025-11-04T10:00:00Z",
"updatedAt": "2025-11-04T10:00:00Z"
}
}
```
### List All Entities
```bash
grpcurl -plaintext -d '{}' \
localhost:50051 charybdis.entities.EntityService/ListEntities
```
### Get Entity by ID
```bash
grpcurl -plaintext \
-d '{"id": "550e8400-e29b-41d4-a716-446655440000"}' \
localhost:50051 charybdis.entities.EntityService/GetEntity
```
## Entity Types
Charybdis supports three main entity types:
### Service
Individual microservices or applications:
```bash
grpcurl -plaintext -d '{
"entity": {
"kind": "Service",
"service_metadata": {
"name": "user-api",
"namespace": "production",
"description": "User management API"
}
}
}' localhost:50051 charybdis.entities.EntityService/CreateEntity
```
### System
Collections of related services:
```bash
grpcurl -plaintext -d '{
"entity": {
"kind": "System",
"system_metadata": {
"name": "payment-system",
"namespace": "production",
"description": "Complete payment processing system"
}
}
}' localhost:50051 charybdis.entities.EntityService/CreateEntity
```
### Component
Reusable components or libraries:
```bash
grpcurl -plaintext -d '{
"entity": {
"kind": "Component",
"component_spec": {
"type": "library",
"lifecycle": "production",
"owner": "platform-team"
},
"component_metadata": {
"name": "auth-library",
"namespace": "shared",
"description": "Shared authentication library"
}
}
}' localhost:50051 charybdis.entities.EntityService/CreateEntity
```
## Adding Metadata
Entities support rich metadata:
```bash
grpcurl -plaintext -d '{
"entity": {
"kind": "Service",
"service_metadata": {
"name": "payment-service",
"namespace": "production",
"description": "Payment processing",
"labels": {
"team": "payments",
"tier": "critical"
},
"links": [
{
"url": "https://dashboard.company.com/payments",
"title": "Dashboard",
"icon": "dashboard"
}
],
"tags": ["payments", "pci-compliant", "critical"]
},
"annotations": {
"github.com/repo-slug": "myorg/payment-service",
"pagerduty.com/service-id": "PXYZ123"
}
}
}' localhost:50051 charybdis.entities.EntityService/CreateEntity
```
## Integrating with CI/CD
### Example: GitHub Actions
Create a workflow to register services automatically:
```yaml
name: Register Service
on:
push:
branches: [main]
jobs:
register:
runs-on: ubuntu-latest
steps:
- name: Register in Charybdis
run: |
grpcurl -plaintext \
-d '{
"entity": {
"kind": "Service",
"service_metadata": {
"name": "${{ github.event.repository.name }}",
"namespace": "production",
"description": "${{ github.event.repository.description }}"
},
"annotations": {
"github.com/repo-slug": "${{ github.repository }}"
}
}
}' \
your-charybdis-host:50051 \
charybdis.entities.EntityService/CreateEntity
```
### Example: GitLab CI
```yaml
register_service:
stage: deploy
script:
- |
grpcurl -plaintext \
-d "{
\"entity\": {
\"kind\": \"Service\",
\"service_metadata\": {
\"name\": \"${CI_PROJECT_NAME}\",
\"namespace\": \"${CI_ENVIRONMENT_NAME}\"
}
}
}" \
your-charybdis-host:50051 \
charybdis.entities.EntityService/CreateEntity
```
## Enabling Security
For production use, enable mTLS and RBAC:
### Step 1: Generate Certificates
```bash
# Use the provided test script
./test-mtls-rbac.sh
```
This creates:
- `certs/ca.pem` - Certificate Authority
- `certs/server-cert.pem` / `server-key.pem` - Server certificate
- `certs/admin-cert.pem` / `admin-key.pem` - Admin client certificate
### Step 2: Enable Security
```bash
export SECURITY_MTLS_ENABLED=true
export SECURITY_MTLS_SERVER_CERT=./certs/server-cert.pem
export SECURITY_MTLS_SERVER_KEY=./certs/server-key.pem
export SECURITY_MTLS_CLIENT_CA=./certs/ca.pem
export SECURITY_RBAC_ENABLED=true
```
### Step 3: Test with mTLS
```bash
grpcurl \
-cacert certs/ca.pem \
-cert certs/admin-cert.pem \
-key certs/admin-key.pem \
-d '{}' \
localhost:50051 charybdis.entities.EntityService/ListEntities
```
See the [Security Guide](security.md) for detailed configuration.
## Backstage Migration (Optional)
If you currently use Backstage and want to migrate gradually, the built-in YAML adapter serves entities in Backstage format:
```yaml
# backstage app-config.yaml
catalog:
locations:
- type: url
target: http://your-charybdis-host:8080/yaml/locations
rules:
- allow: [Component, System, Service]
```
Backstage will automatically discover and import entities from Charybdis. You can run both in parallel — entities managed via gRPC are immediately visible in Backstage.
## Troubleshooting
### Port Already in Use
```
Error: transport error
```
**Solution**: Check if another process is using port 50051:
```bash
lsof -ti:50051
```
Kill the process or change the port:
```bash
export GRPC_PORT=50052
```
### Database Connection Failed
```
Error: password authentication failed
```
**Solution**: Verify your DATABASE_URL:
```bash
# Test connection
psql "$DATABASE_URL" -c "SELECT 1;"
```
### Permission Denied (with security enabled)
```
Code: PermissionDenied
Message: Role 'X' does not have permission 'Y'
```
**Solution**: Check your certificate and role mappings. See [Security Guide](security.md).
## Next Steps
Now that you have Charybdis running:
1. Learn about [Core Concepts](core-concepts.md) — entities, vulnerabilities, events
2. Read the [Vision & Roadmap](../VISION.md) — where Charybdis is going
3. Configure [Security](security.md) for production (mTLS + RBAC)
4. Explore [Plugins](../plugins/README.md) for external integrations
## Configuration
Charybdis supports two configuration methods:
### 1. Configuration File (Recommended)
Use `config.toml` for structured configuration:
```toml
# config.toml
[server]
grpc_host = "[::1]"
grpc_port = 50051
[database]
url = "${DATABASE_URL}" # Environment variable substitution
[security.mtls]
enabled = false
[security.rbac]
enabled = false
[telemetry]
service_name = "charybdis"
environment = "development"
enable_console = true
```
**Benefits:**
- ✅ Organized by section (server, database, security, telemetry, plugins)
- ✅ Environment variable substitution with `${VAR_NAME}`
- ✅ Comments and documentation inline
- ✅ Easy to version control (excluding secrets)
- ✅ No need to export dozens of environment variables
**Using Environment Variables in config.toml:**
```toml
[database]
url = "${DATABASE_URL}" # Will be substituted at runtime
[plugins.defectdojo]
api_key = "${DEFECTDOJO_API_KEY}" # Secrets stay in environment
```
Then set only the secrets:
```bash
export DATABASE_URL="postgresql://..."
export DEFECTDOJO_API_KEY="secret-key"
```
### 2. Environment Variables (Legacy)
If `config.toml` is not found, Charybdis falls back to environment variables:
| Variable | Default | Description |
|----------|---------|-------------|
| `DATABASE_URL` | (required) | PostgreSQL connection string |
| `GRPC_HOST` | `[::1]` | gRPC server bind address |
| `GRPC_PORT` | `50051` | gRPC server port |
| `RUST_LOG` | `info` | Logging level |
| `SECURITY_MTLS_ENABLED` | `false` | Enable mTLS authentication |
| `SECURITY_RBAC_ENABLED` | `false` | Enable RBAC authorization |
| `OTEL_ENABLE_CONSOLE` | `true` | Enable console logging |
| `OTEL_SERVICE_NAME` | `charybdis` | Service name for telemetry |
### Configuration File Locations
Charybdis looks for configuration files in this order:
1. `./config.toml` (current directory)
2. `./charybdis.toml`
3. `/etc/charybdis/config.toml` (Linux/Unix)
If none are found, it uses environment variables.
See `config.toml.example` for a complete configuration template with all options documented.
---
**Need help?** Check the [troubleshooting guide](troubleshooting.md) or [open an issue](../../issues).
+125
View File
@@ -0,0 +1,125 @@
# Charybdis Documentation
**The security-native platform engineering tool.**
## What is Charybdis?
Charybdis is a platform engineering tool that unifies **software catalog**, **vulnerability management**, and **compliance posture** in a single event-driven platform. Built in Rust, deployed as a single binary.
Instead of running Backstage + DefectDojo + Dependency-Track + a license scanner + a compliance spreadsheet, you run Charybdis.
### The Problems It Solves
1. **Fragmented tooling** — Your software catalog, vulnerability data, license info, and compliance evidence live in 5 different tools that don't talk to each other. Charybdis unifies them.
2. **Static catalog data** — Traditional catalogs rely on YAML files that go stale within weeks. Charybdis is event-driven — CI/CD pipelines and IaC tools register and update entities via gRPC, so the catalog is always accurate.
3. **Security as an afterthought** — In Backstage, security is a plugin. In Charybdis, every entity carries its vulnerability posture, license status, and compliance state natively.
4. **Manual provisioning** — New service? Manually create entries in every tool. With Charybdis, one gRPC call catalogs the service and event-driven plugins handle the rest.
5. **Compliance evidence assembly** — Compliance reporting pulls from real vulnerability and license data, not spreadsheets.
## How It Works
```
CI/CD or Scanner ──gRPC──> Charybdis
├── Catalogs the service (event-driven) ← Done
├── Fires events to plugins (DefectDojo, ...) ← Done
├── Ingests scan results (SARIF, CycloneDX) ← Phase 1
├── Evaluates security gates & rules ← Phase 1
└── Exposes catalog via YAML adapter (Backstage) ← Done
```
**One platform. Your services are cataloged. Your vulns are tracked. Your compliance is visible. In real-time.**
## Key Concepts
| Concept | Description |
|---------|-------------|
| **Entity** | Anything in your software ecosystem: services, systems, components, APIs, users, groups, domains, resources |
| **Vulnerability** | *(Phase 1)* A security finding linked to an entity, ingested from scanner output (SARIF, CycloneDX) |
| **Assessment** | *(Phase 1)* The triage decision on a vulnerability: accept risk, remediate, auto-assessed by rules |
| **Security Gate** | *(Phase 1)* Severity thresholds per product — blocks deployments when violated |
| **Event Bus** | Publishes lifecycle events when entities or vulnerabilities change |
| **Plugin** | Reacts to events to integrate with external systems (Slack, Jira, GitHub, custom) |
| **Annotations** | Key-value metadata on entities for external references (e.g. `github.com/repo-slug`) |
## Architecture
```mermaid
graph LR
A[CI/CD / Scanners] -->|gRPC| B[Charybdis]
B -->|Native| C[Software Catalog]
B -->|Native| D[Vuln Management]
B -->|Native| E[Compliance]
B -->|Events| F[Plugins: Slack / Jira / Custom]
B -->|YAML| G[Backstage - optional]
style B fill:#4A90E2,stroke:#2E5C8A,color:#fff
style D fill:#E24A4A,stroke:#8A2E2E,color:#fff
style E fill:#4AE28A,stroke:#2E8A5C,color:#fff
```
## Documentation
### Getting Started
- [Installation & Quick Start](getting-started.md) — Get Charybdis running and register your first entity
- [Demo Stack](../deploy/DEMO.md) — Full demo with DefectDojo
### Understanding Charybdis
- [Core Concepts](core-concepts.md) — Entities, vulnerabilities, events, and data model
- [Vision & Roadmap](../VISION.md) — Where Charybdis is going and why
- [Architecture](architecture.md) — Technical design decisions
### Configuration
- [Security](security.md) — mTLS authentication and RBAC authorization
- [Plugin Configuration](PLUGIN_CONFIGURATION_GUIDE.md) — Setting up and configuring plugins
### Extending Charybdis
- [Plugin Development](../plugins/README.md) — Build your own integration plugins
## Quick Example
Register a service from your CI/CD pipeline:
```bash
grpcurl -plaintext -d '{
"entity": {
"kind": "Component",
"component_metadata": {
"name": "payment-api",
"description": "Payment processing service"
},
"component_spec": {
"type": "service",
"lifecycle": "production",
"owner": "team-payments"
}
}
}' charybdis:50051 charybdis.entities.EntityService/CreateEntity
```
**What happens next:**
- Entity stored in PostgreSQL with a UUID
- `EntityCreated` event published to the event bus
- Plugins react (e.g., DefectDojo creates a product automatically)
- Entity available via gRPC and YAML adapter
No YAML file to write. No PR to open. No manual provisioning.
## Backstage Migration
Already using Backstage? Charybdis provides a YAML adapter for gradual migration. Point Backstage at Charybdis as a catalog source — entities registered via gRPC are immediately available in Backstage.
```yaml
# backstage app-config.yaml
catalog:
locations:
- type: url
target: http://charybdis:8080/yaml/locations
```
---
**Ready to get started?** Head to the [Getting Started Guide](getting-started.md).
+503
View File
@@ -0,0 +1,503 @@
# Security
Charybdis provides enterprise-grade security with mTLS authentication and RBAC authorization.
## Overview
```mermaid
graph TB
A[Client Request] -->|mTLS Handshake| B[TLS Layer]
B -->|Certificate| C[Auth Interceptor]
C -->|Extract Identity| D{Parse Certificate}
D -->|CN, OU, O| E[Role Mapping]
E -->|Role| F{Permission Check}
F -->|Authorized| G[Entity Service]
F -->|Denied| H[PermissionDenied Error]
G -->|Audit Log| I[Security Events]
style C fill:#f9d71c,stroke:#f9a825
style F fill:#4A90E2,stroke:#2E5C8A
```
## mTLS Authentication
Mutual TLS (mTLS) provides certificate-based client authentication.
### How It Works
1. **Client** presents X.509 certificate during TLS handshake
2. **Server** validates certificate against trusted CA
3. **Charybdis** extracts certificate identity (CN, OU, O)
4. **RBAC Engine** maps identity to role
5. **Permissions** are checked before allowing the request
### Certificate Structure
```
Subject: CN=admin-user, OU=platform-team, O=Charybdis-Dev, C=US
Issuer: CN=Charybdis Root CA, O=Charybdis-Dev, C=US
Validity: Not Before: Nov 3 2025, Not After: Nov 3 2026
```
**Fields Used for Authentication**:
- `CN` (Common Name) - User or service identifier
- `OU` (Organizational Unit) - Team or role identifier
- `O` (Organization) - Organization name
## Configuration
### Environment Variables
```bash
# Enable mTLS
export SECURITY_MTLS_ENABLED=true
# Server certificate and key
export SECURITY_MTLS_SERVER_CERT=./certs/server-cert.pem
export SECURITY_MTLS_SERVER_KEY=./certs/server-key.pem
# Client CA for verification
export SECURITY_MTLS_CLIENT_CA=./certs/ca.pem
# Enable RBAC
export SECURITY_RBAC_ENABLED=true
```
### Generating Certificates
#### Development Certificates
Use the provided test script:
```bash
./test-mtls-rbac.sh
```
This generates:
- `ca.pem` / `ca-key.pem` - Certificate Authority
- `server-cert.pem` / `server-key.pem` - Server certificate
- `admin-cert.pem` / `admin-key.pem` - Admin client (OU=platform-team)
- Various plugin certificates (OU=plugins)
#### Production Certificates
For production, use a proper PKI:
**Options**:
- **Internal PKI**: HashiCorp Vault, cert-manager, step-ca
- **Public CA**: Let's Encrypt (for internet-facing)
- **Enterprise CA**: Your organization's certificate authority
**Requirements**:
- Valid for at least 90 days
- Issued by trusted CA
- Proper subject alternative names (SANs)
- Appropriate key usage extensions
### Certificate Best Practices
**DO**:
- Use short-lived certificates (24-90 days)
- Implement automatic rotation
- Monitor expiration dates
- Use strong key lengths (2048+ bit RSA or 256+ bit ECC)
- Store private keys securely
- Use separate CAs for dev/prod
**DON'T**:
- Use self-signed certs in production
- Share private keys
- Use the same certificate across environments
- Ignore expiration warnings
- Store keys in version control
## RBAC Authorization
Role-Based Access Control (RBAC) enforces fine-grained permissions.
### Role Mapping
Charybdis maps certificate attributes to roles:
```
Certificate OU → RBAC Role → Permissions
```
### Default Roles
| Role | Certificate OU | Description | Permissions |
|------|---------------|-------------|-------------|
| `platform` | `platform-team` | Platform administrators | Full access (CRUD + list) |
| `automation` | `automation` | CI/CD pipelines | Create, read, update, list |
| `plugin` | `plugins` | Integration plugins | Read, list only |
### Permissions
| Permission | Methods | Description |
|------------|---------|-------------|
| `entity:create` | CreateEntity | Create new entities |
| `entity:read` | GetEntity | Read entity by ID |
| `entity:update` | UpdateEntity, PartialUpdateEntity | Modify entities |
| `entity:delete` | DeleteEntity | Delete entities |
| `entity:list` | ListEntities | List all entities |
### Permission Matrix
| Role | Create | Read | Update | Delete | List |
|------|--------|------|--------|--------|------|
| `platform` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `automation` | ✅ | ✅ | ✅ | ❌ | ✅ |
| `plugin` | ❌ | ✅ | ❌ | ❌ | ✅ |
## Usage Examples
### Admin Access (Full Permissions)
```bash
# Create entity
grpcurl \
-cacert certs/ca.pem \
-cert certs/admin-cert.pem \
-key certs/admin-key.pem \
-d '{"entity": {...}}' \
localhost:50051 charybdis.entities.EntityService/CreateEntity
# ✅ SUCCESS
```
### Automation Access (Limited)
```bash
# CI/CD can create
grpcurl \
-cacert certs/ca.pem \
-cert certs/ci-pipeline-cert.pem \
-key certs/ci-pipeline-key.pem \
-d '{"entity": {...}}' \
localhost:50051 charybdis.entities.EntityService/CreateEntity
# ✅ SUCCESS
# But cannot delete
grpcurl \
-cacert certs/ca.pem \
-cert certs/ci-pipeline-cert.pem \
-key certs/ci-pipeline-key.pem \
-d '{"id": "..."}' \
localhost:50051 charybdis.entities.EntityService/DeleteEntity
# ❌ Code: PermissionDenied
# Message: Role 'automation' does not have permission 'entity:delete'
```
### Plugin Access (Read-Only)
```bash
# Plugin can list
grpcurl \
-cacert certs/ca.pem \
-cert certs/defectdojo-plugin-cert.pem \
-key certs/defectdojo-plugin-key.pem \
-d '{}' \
localhost:50051 charybdis.entities.EntityService/ListEntities
# ✅ SUCCESS
# But cannot create
grpcurl \
-cacert certs/ca.pem \
-cert certs/defectdojo-plugin-cert.pem \
-key certs/defectdojo-plugin-key.pem \
-d '{"entity": {...}}' \
localhost:50051 charybdis.entities.EntityService/CreateEntity
# ❌ Code: PermissionDenied
# Message: Role 'plugin' does not have permission 'entity:create'
```
## Custom Role Configuration
### Defining Custom Roles
Edit `src/main.rs::setup_default_rbac_config()`:
```rust
let role_mappings = vec![
// Custom developer role
RoleMapping {
role: "developer".to_string(),
rules: vec![RoleRule {
subject: SubjectMatch {
cn: None,
ou: Some("engineering".to_string()),
o: None,
},
}],
},
];
let mut permissions = HashMap::new();
permissions.insert(
"developer".to_string(),
vec![
"entity:create".to_string(),
"entity:read".to_string(),
"entity:list".to_string(),
],
);
```
### Certificate-Based Mapping
Map specific certificates to roles:
```rust
// Map by CN (specific user/service)
RoleRule {
subject: SubjectMatch {
cn: Some("jenkins-ci".to_string()),
ou: None,
o: None,
},
}
// Map by Organization
RoleRule {
subject: SubjectMatch {
cn: None,
ou: None,
o: Some("External-Partner".to_string()),
},
}
// Combined matching
RoleRule {
subject: SubjectMatch {
cn: Some("admin".to_string()),
ou: Some("platform-team".to_string()),
o: Some("MyCompany".to_string()),
},
}
```
## Audit Logging
All security events are logged for compliance and forensics.
### Event Types
| Event | Logged Information |
|-------|-------------------|
| Authentication Success | Identity (CN, OU, O), timestamp |
| Authentication Failure | Reason, timestamp |
| Authorization Success | Identity, role, method, duration |
| Authorization Denial | Identity, role, method, required permission |
| Certificate Error | Error details, certificate info |
### Log Format
```
2025-11-04T10:16:22Z INFO [AUDIT] Authorization Success
identity: CN=admin-user, OU=platform-team
role: platform
method: /charybdis.entities.EntityService/CreateEntity
permission: entity:create
duration: 0.08ms
2025-11-04T10:16:25Z WARN [AUDIT] Authorization Denied
identity: CN=defectdojo-plugin, OU=plugins
role: plugin
method: /charybdis.entities.EntityService/CreateEntity
required_permission: entity:create
reason: Role 'plugin' does not have permission 'entity:create'
duration: 0.05ms
```
### Audit Configuration
```bash
# Enable audit logging (default: true)
export SECURITY_RBAC_AUDIT_ENABLED=true
# Log all requests (default: true)
export SECURITY_RBAC_AUDIT_LOG_ALL_REQUESTS=true
# Log denied requests (default: true)
export SECURITY_RBAC_AUDIT_LOG_DENIED=true
```
## Reverse Proxy Deployment
For environments where mTLS termination happens at a reverse proxy:
### Architecture
```mermaid
graph LR
A[Client] -->|mTLS| B[Reverse Proxy]
B -->|HTTP + Cert Header| C[Charybdis]
style B fill:#4A90E2,stroke:#2E5C8A
```
### Proxy Configuration
#### Envoy
```yaml
- name: envoy.filters.http.lua
typed_config:
inline_code: |
function envoy_on_request(request_handle)
local cert = request_handle:connection():ssl():peerCertificatePresented()
if cert then
request_handle:headers():add("x-client-cert", cert)
end
end
```
#### nginx
```nginx
location / {
proxy_set_header X-Client-Cert $ssl_client_cert;
proxy_pass http://charybdis:50051;
}
```
### Header Format
Charybdis accepts certificates in these headers:
- `x-forwarded-client-cert` (Envoy/Istio standard)
- `x-client-cert` (nginx)
Format: Base64-encoded DER certificate
## Troubleshooting
### Common Issues
#### 1. Certificate Not Found
```
ERROR: Client certificate required but not provided
```
**Causes**:
- Certificate not sent by client
- Wrong certificate path
- Certificate expired
**Solutions**:
```bash
# Verify certificate is valid
openssl x509 -in cert.pem -noout -text
# Check expiration
openssl x509 -in cert.pem -noout -dates
# Test TLS handshake
openssl s_client -connect localhost:50051 \
-CAfile ca.pem -cert cert.pem -key key.pem
```
#### 2. Permission Denied
```
Code: PermissionDenied
Message: Role 'X' does not have permission 'Y'
```
**Causes**:
- Certificate OU doesn't match any role
- Role lacks required permission
**Solutions**:
```bash
# Check certificate attributes
openssl x509 -in cert.pem -noout -subject
# Review role mappings in src/main.rs
# Verify permission requirements in docs
```
#### 3. No Role Assigned
```
ERROR: No role assigned to identity: CN=..., OU=...
```
**Causes**:
- Certificate OU not configured in role mappings
- Typo in OU value
**Solutions**:
- Add role mapping for the OU
- Generate new certificate with correct OU
- Check role mapping configuration
## Security Hardening
### Production Checklist
- [ ] Use certificates from trusted CA
- [ ] Enable mTLS (`SECURITY_MTLS_ENABLED=true`)
- [ ] Enable RBAC (`SECURITY_RBAC_ENABLED=true`)
- [ ] Implement certificate rotation (90 days max)
- [ ] Monitor certificate expiration
- [ ] Enable audit logging
- [ ] Use TLS 1.3 only
- [ ] Implement rate limiting
- [ ] Set up intrusion detection
- [ ] Regular security audits
### Certificate Rotation
Implement automatic rotation:
```bash
#!/bin/bash
# rotate-certs.sh
# Generate new certificate
./generate-cert.sh
# Gracefully restart Charybdis
kill -HUP $(pidof charybdis)
# Verify new cert is active
sleep 5
openssl s_client -connect localhost:50051 < /dev/null | \
openssl x509 -noout -dates
```
### Monitoring
Monitor security metrics:
- Authentication success/failure rate
- Authorization denial rate
- Certificate expiration warnings
- Audit log anomalies
- Failed login attempts
## Compliance
Charybdis security features support compliance requirements:
| Standard | Supported Features |
|----------|-------------------|
| **SOC 2** | Audit logging, access control, encryption in transit |
| **ISO 27001** | Authentication, authorization, audit trails |
| **PCI DSS** | Encryption, access control, logging |
| **HIPAA** | Access control, audit logging, encryption |
## Next Steps
- 🔌 Configure [Plugins](plugins.md) with proper certificates
- 🚀 Review [Deployment Guide](deployment.md) for production
- 📊 Set up [Monitoring](monitoring.md) for security events
---
**Security Questions?** Open a [security issue](../../security) (for vulnerabilities, use private disclosure).