secretumvault/docs/architecture/complete-architecture.md
2025-12-26 15:13:36 +00:00

36 KiB

SecretumVault - Complete Post-Quantum Secrets Management System

Version: 3.0
Date: 2025-12-21
Author: Jesús Pérez (Kit Digital / Rust Las Palmas)
Project Name: secretumvault or svault

Executive Summary

SecretumVault es un sistema completo de gestión de secretos con:

  • Post-Quantum Crypto desde el diseño (no retrofitted)
  • Secrets Management completo (KV, dynamic, transit)
  • Cedar Policies (modern authorization, no ACL legacy)
  • Multi-Backend Crypto (aws-lc-rs, RustCrypto, Tongsuo)
  • Encryption as a Service (EaaS)
  • Storage Flexible (SurrealDB, filesystem, etcd)
  • API Compatible con HashiCorp Vault (migration path)
  • Rust Native (memory safe, high performance)

NO es:

  • NO tiene auth complejo (OIDC, LDAP) - solo lo esencial
  • NO tiene ACL legacy - usa Cedar policies
  • NO compite con Enterprise Vault features

Es ideal para:

  • Kit Digital projects (PYMES españolas)
  • Aplicaciones que necesitan PQC ahora
  • Infraestructura moderna (Kubernetes, SurrealDB)
  • Compliance NIS2 + post-quantum readiness

Table of Contents

  1. Core Concepts
  2. Architecture
  3. Secrets Engines
  4. Authorization with Cedar
  5. Crypto Backends
  6. Storage Backends
  7. API Design
  8. Deployment Modes
  9. Integration Examples
  10. Implementation Roadmap

Core Concepts

What is SecretumVault?

SecretumVault = Secrets Manager + Encryption Service + Key Management
             + Cedar Policies + Post-Quantum Crypto

Key Features

  1. Secrets Management

    • Key/Value secrets (static)
    • Dynamic secrets (database, cloud)
    • Transit encryption (EaaS)
    • SSH/TLS certificate generation
  2. Post-Quantum Ready

    • ML-KEM for key encapsulation
    • ML-DSA for signatures
    • Hybrid modes (classical + PQC)
    • Future-proof key rotation
  3. Modern Authorization

    • Cedar policy language (AWS open-source)
    • Attribute-based access control (ABAC)
    • No legacy ACL complexity
    • Policy-as-Code
  4. High Performance

    • Rust native (no GC pauses)
    • Async/await throughout
    • Optimized crypto backends
    • Efficient storage

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                      CLIENT LAYER                               │
├─────────────────────────────────────────────────────────────────┤
│  REST API  │  CLI  │  Rust SDK  │  Language SDKs (future)       │
└──────┬──────────────────────────────────────────────────────────┘
       │
┌──────▼──────────────────────────────────────────────────────────┐
│                   QUANTUMVAULT CORE                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────┐  ┌──────────────┐  ┌──────────────────────┐  │
│  │   Request   │  │ Auth/Policy  │  │   Secrets Router     │  │
│  │   Handler   │→ │   Engine     │→ │   (Path-based)       │  │
│  └─────────────┘  └──────────────┘  └──────────────────────┘  │
│                         │                      │                │
│                         ↓                      ↓                │
│              ┌──────────────────┐   ┌────────────────────┐     │
│              │  Cedar Policies  │   │  Secrets Engines   │     │
│              │  (Authorization) │   │  (KV, Transit,     │     │
│              └──────────────────┘   │   Dynamic, PKI)    │     │
│                                     └────────────────────┘     │
│                                              │                  │
└──────────────────────────────────────────────┼──────────────────┘
                                               │
       ┌───────────────────────────────────────┼───────────────┐
       │                                       │               │
┌──────▼────────┐  ┌──────────────▼──────┐  ┌▼──────────────┐ │
│ Crypto Layer  │  │  Storage Layer      │  │  Seal/Unseal  │ │
├───────────────┤  ├─────────────────────┤  ├───────────────┤ │
│ • aws-lc-rs   │  │ • SurrealDB         │  │ • Master Key  │ │
│ • RustCrypto  │  │ • Filesystem        │  │ • Shamir SSS  │ │
│ • Tongsuo     │  │ • etcd/Consul       │  │ • Auto-unseal │ │
│ • OpenSSL     │  │ • PostgreSQL        │  │   (KMS)       │ │
└───────────────┘  └─────────────────────┘  └───────────────┘ │
                                                                │
└────────────────────────────────────────────────────────────────┘

Secrets Engines

SecretumVault soporta múltiples "engines" montados en paths:

1. KV Engine (Key-Value Secrets)

Path: secret/

// API
POST   /v1/secret/data/myapp/database    # Write secret
GET    /v1/secret/data/myapp/database    # Read secret
DELETE /v1/secret/data/myapp/database    # Delete secret
GET    /v1/secret/metadata/myapp/database # Get metadata

Implementación:

pub struct KVEngine {
    storage: Arc<dyn StorageBackend>,
    versioned: bool, // KV v1 o v2
}

#[derive(Serialize, Deserialize)]
pub struct SecretData {
    pub data: HashMap<String, serde_json::Value>,
    pub metadata: SecretMetadata,
}

#[derive(Serialize, Deserialize)]
pub struct SecretMetadata {
    pub version: u64,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
    pub deleted: bool,
    pub destroyed: bool,
}

impl SecretsEngine for KVEngine {
    async fn write(
        &self,
        path: &str,
        data: HashMap<String, serde_json::Value>,
    ) -> Result<SecretMetadata> {
        let mut metadata = SecretMetadata {
            version: self.get_next_version(path).await?,
            created_at: Utc::now(),
            updated_at: Utc::now(),
            deleted: false,
            destroyed: false,
        };
        
        let secret = SecretData { data, metadata: metadata.clone() };
        
        // Encrypt data before storing
        let encrypted = self.encrypt_secret(&secret).await?;
        
        self.storage.store_secret(path, &encrypted).await?;
        
        Ok(metadata)
    }
    
    async fn read(&self, path: &str) -> Result<SecretData> {
        let encrypted = self.storage.get_secret(path).await?;
        let secret = self.decrypt_secret(&encrypted).await?;
        
        if secret.metadata.deleted {
            return Err(Error::SecretDeleted);
        }
        
        Ok(secret)
    }
}

2. Transit Engine (Encryption as a Service)

Path: transit/

Permite a aplicaciones cifrar/descifrar datos sin manejar claves directamente.

// API
POST /v1/transit/encrypt/my-key    # Encrypt data
POST /v1/transit/decrypt/my-key    # Decrypt data
POST /v1/transit/sign/my-key       # Sign data (PQC)
POST /v1/transit/verify/my-key     # Verify signature
POST /v1/transit/rewrap/my-key     # Re-encrypt with new key version

Implementación:

pub struct TransitEngine {
    crypto: Arc<dyn CryptoBackend>,
    key_manager: Arc<KeyManager>,
}

#[derive(Deserialize)]
pub struct EncryptRequest {
    pub plaintext: String, // Base64
    pub context: Option<String>, // Additional authenticated data
}

#[derive(Serialize)]
pub struct EncryptResponse {
    pub ciphertext: String, // vault:v1:base64ciphertext
}

impl SecretsEngine for TransitEngine {
    async fn handle_encrypt(
        &self,
        key_name: &str,
        req: EncryptRequest,
    ) -> Result<EncryptResponse> {
        // 1. Decode plaintext
        let plaintext = base64::decode(&req.plaintext)?;
        
        // 2. Get encryption key (latest version)
        let key = self.key_manager.get_key(key_name).await?;
        
        // 3. Encrypt with crypto backend
        let ciphertext = match key.algorithm {
            KeyAlgorithm::Aes256Gcm => {
                self.crypto.encrypt_symmetric(&key.key_data, &plaintext, SymmetricAlgorithm::Aes256Gcm)?
            }
            KeyAlgorithm::ChaCha20Poly1305 => {
                self.crypto.encrypt_symmetric(&key.key_data, &plaintext, SymmetricAlgorithm::ChaCha20Poly1305)?
            }
            _ => return Err(Error::InvalidAlgorithm),
        };
        
        // 4. Format response (Vault-compatible)
        let encoded = format!(
            "vault:v{}:{}",
            key.version,
            base64::encode(&ciphertext)
        );
        
        Ok(EncryptResponse {
            ciphertext: encoded,
        })
    }
    
    async fn handle_sign(
        &self,
        key_name: &str,
        data: &[u8],
    ) -> Result<String> {
        let key = self.key_manager.get_key(key_name).await?;
        
        // Firmar con PQC (ML-DSA)
        let signature = self.crypto.sign(&key.private_key, data)?;
        
        Ok(format!(
            "vault:v{}:{}",
            key.version,
            base64::encode(&signature)
        ))
    }
}

3. PKI Engine (Certificate Authority)

Path: pki/

Genera certificados X.509 (RSA/ECDSA tradicional + PQC experimental).

pub struct PKIEngine {
    crypto: Arc<dyn CryptoBackend>,
    ca_bundle: Option<CABundle>,
}

#[derive(Deserialize)]
pub struct IssueCertRequest {
    pub common_name: String,
    pub alt_names: Vec<String>,
    pub ttl: String, // e.g., "720h"
    pub algorithm: CertAlgorithm, // RSA2048, ECDSA256, ML-DSA-65
}

impl SecretsEngine for PKIEngine {
    async fn issue_cert(&self, req: IssueCertRequest) -> Result<Certificate> {
        // 1. Generate keypair
        let keypair = self.crypto.generate_keypair(
            req.algorithm.to_key_algorithm()
        )?;
        
        // 2. Create CSR
        let csr = create_csr(&keypair, &req.common_name, &req.alt_names)?;
        
        // 3. Sign with CA
        let cert = self.ca_bundle
            .as_ref()
            .ok_or(Error::NoCAConfigured)?
            .sign_csr(&csr, parse_duration(&req.ttl)?)?;
        
        Ok(cert)
    }
}

4. Dynamic Secrets Engine

Path: database/, aws/, gcp/

Genera credenciales dinámicas on-demand.

pub struct DatabaseEngine {
    connections: HashMap<String, DatabaseConnection>,
}

#[derive(Deserialize)]
pub struct GetCredsRequest {
    pub role: String, // e.g., "readonly", "admin"
}

#[derive(Serialize)]
pub struct DatabaseCreds {
    pub username: String,
    pub password: String,
    pub lease_duration: u64, // seconds
}

impl SecretsEngine for DatabaseEngine {
    async fn get_credentials(
        &self,
        role: &str,
    ) -> Result<DatabaseCreds> {
        let role_config = self.get_role_config(role).await?;
        
        // Generate random username/password
        let username = format!("v-{}-{}", role, generate_id());
        let password = generate_secure_password(32);
        
        // Create user in database
        let db = self.connections.get(&role_config.connection)
            .ok_or(Error::ConnectionNotFound)?;
        
        db.execute(&format!(
            "CREATE USER '{}' IDENTIFIED BY '{}';",
            username, password
        )).await?;
        
        // Grant permissions
        for stmt in &role_config.creation_statements {
            db.execute(stmt).await?;
        }
        
        // Schedule revocation
        self.schedule_revocation(&username, role_config.ttl).await?;
        
        Ok(DatabaseCreds {
            username,
            password,
            lease_duration: role_config.ttl.as_secs(),
        })
    }
}

Authorization with Cedar

En lugar de ACL tradicional, usamos Cedar (AWS open-source policy language).

Cedar Overview

Cedar es:

  • Expresivo y declarativo
  • Verificable formalmente
  • Policy-as-Code
  • Usado por AWS (AVP)

Policy Example

Archivo: policies/allow-read-secrets.cedar

// Permitir a developers leer secrets de su proyecto
permit(
    principal in Group::"developers",
    action == Action::"read",
    resource in Project::"kit-digital"
)
when {
    context.environment == "development" ||
    context.environment == "staging"
};

// Permitir a CI/CD leer solo secrets de deployment
permit(
    principal == ServiceAccount::"github-actions",
    action in [Action::"read", Action::"encrypt"],
    resource in Path::"secret/deployments/*"
)
when {
    context.ip_address in [
        "192.30.252.0/22",  // GitHub Actions IPs
        "185.199.108.0/22"
    ]
};

// Permitir a admins todo
permit(
    principal in Group::"admins",
    action,
    resource
);

// Denegar escritura a production desde dev environments
forbid(
    principal,
    action in [Action::"write", Action::"delete"],
    resource in Path::"secret/production/*"
)
when {
    context.environment != "production"
};

Cedar Integration

Archivo: src/auth/cedar.rs

use cedar_policy::{Authorizer, Context, Entities, PolicySet, Request};

pub struct CedarAuthz {
    authorizer: Authorizer,
    policies: PolicySet,
    entities: Entities,
}

impl CedarAuthz {
    pub fn from_files(policy_dir: &str) -> Result<Self> {
        let policies = load_policies_from_dir(policy_dir)?;
        let entities = load_entities()?; // Groups, users, etc.
        
        Ok(Self {
            authorizer: Authorizer::new(),
            policies,
            entities,
        })
    }
    
    pub fn is_authorized(
        &self,
        principal: &str,
        action: &str,
        resource: &str,
        context: RequestContext,
    ) -> Result<bool> {
        let request = Request::new(
            Some(principal.parse()?),
            Some(action.parse()?),
            Some(resource.parse()?),
            Context::from_json_value(serde_json::to_value(context)?)?,
        )?;
        
        let response = self.authorizer.is_authorized(
            &request,
            &self.policies,
            &self.entities,
        );
        
        Ok(response.decision() == cedar_policy::Decision::Allow)
    }
}

#[derive(Serialize)]
pub struct RequestContext {
    pub environment: String,
    pub ip_address: String,
    pub timestamp: i64,
    pub mfa_verified: bool,
}

// Integración con Vault
impl Vault {
    async fn handle_request(
        &self,
        req: VaultRequest,
        auth_token: &str,
    ) -> Result<VaultResponse> {
        // 1. Validate token
        let principal = self.auth.validate_token(auth_token).await?;
        
        // 2. Build context
        let context = RequestContext {
            environment: req.headers.get("X-Vault-Environment")
                .unwrap_or("unknown").to_string(),
            ip_address: req.remote_addr.to_string(),
            timestamp: Utc::now().timestamp(),
            mfa_verified: self.auth.is_mfa_verified(&principal).await?,
        };
        
        // 3. Authorize with Cedar
        let authorized = self.cedar.is_authorized(
            &principal.id,
            &req.operation, // "read", "write", "delete"
            &req.path,      // "secret/production/db"
            context,
        )?;
        
        if !authorized {
            return Err(Error::PermissionDenied);
        }
        
        // 4. Route to appropriate engine
        self.route_request(req).await
    }
}

Entities Definition

Archivo: entities.json

[
  {
    "uid": {"type": "User", "id": "alice"},
    "attrs": {
      "email": "alice@example.com",
      "groups": ["developers", "admins"]
    },
    "parents": [
      {"type": "Group", "id": "developers"},
      {"type": "Group", "id": "admins"}
    ]
  },
  {
    "uid": {"type": "ServiceAccount", "id": "github-actions"},
    "attrs": {
      "type": "ci-cd",
      "trusted": true
    }
  },
  {
    "uid": {"type": "Group", "id": "developers"},
    "attrs": {},
    "parents": []
  }
]

Crypto Backends

Backend Interface (ya definido anteriormente)

pub trait CryptoBackend: Debug + Send + Sync {
    fn backend_type(&self) -> BackendType;
    
    // Asymmetric
    fn generate_keypair(&self, algorithm: KeyAlgorithm) -> Result<KeyPair>;
    fn sign(&self, key: &PrivateKey, data: &[u8]) -> Result<Vec<u8>>;
    fn verify(&self, key: &PublicKey, data: &[u8], sig: &[u8]) -> Result<bool>;
    
    // Symmetric
    fn encrypt_symmetric(&self, key: &[u8], data: &[u8], alg: SymmetricAlgorithm) -> Result<Vec<u8>>;
    fn decrypt_symmetric(&self, key: &[u8], data: &[u8], alg: SymmetricAlgorithm) -> Result<Vec<u8>>;
    
    // KEM (for PQC)
    fn kem_encapsulate(&self, key: &PublicKey) -> Result<(Vec<u8>, Vec<u8>)>;
    fn kem_decapsulate(&self, key: &PrivateKey, ct: &[u8]) -> Result<Vec<u8>>;
    
    // Random
    fn random_bytes(&self, len: usize) -> Result<Vec<u8>>;
}

Hybrid Crypto Mode

Para defense-in-depth, soportar hybrid:

pub struct HybridBackend {
    classical: Arc<dyn CryptoBackend>, // OpenSSL/aws-lc (RSA/ECDSA)
    pqc: Arc<dyn CryptoBackend>,       // aws-lc-rs (ML-KEM/ML-DSA)
}

impl CryptoBackend for HybridBackend {
    fn kem_encapsulate(&self, key: &PublicKey) -> Result<(Vec<u8>, Vec<u8>)> {
        // Encapsulate with both
        let (ct_classical, ss_classical) = self.classical.kem_encapsulate(key)?;
        let (ct_pqc, ss_pqc) = self.pqc.kem_encapsulate(key)?;
        
        // Combine ciphertexts
        let combined_ct = [ct_classical, ct_pqc].concat();
        
        // Derive shared secret from both using KDF
        let combined_ss = kdf_combine(&ss_classical, &ss_pqc)?;
        
        Ok((combined_ct, combined_ss))
    }
}

Storage Backends

Storage Trait

#[async_trait]
pub trait StorageBackend: Debug + Send + Sync {
    // Secrets
    async fn store_secret(&self, path: &str, data: &EncryptedData) -> Result<()>;
    async fn get_secret(&self, path: &str) -> Result<EncryptedData>;
    async fn delete_secret(&self, path: &str) -> Result<()>;
    async fn list_secrets(&self, prefix: &str) -> Result<Vec<String>>;
    
    // Keys
    async fn store_key(&self, key: &StoredKey) -> Result<()>;
    async fn get_key(&self, key_id: &str) -> Result<StoredKey>;
    
    // Policies
    async fn store_policy(&self, name: &str, policy: &str) -> Result<()>;
    async fn get_policy(&self, name: &str) -> Result<String>;
    async fn list_policies(&self) -> Result<Vec<String>>;
    
    // Leases (for dynamic secrets)
    async fn store_lease(&self, lease: &Lease) -> Result<()>;
    async fn get_lease(&self, lease_id: &str) -> Result<Lease>;
    async fn delete_lease(&self, lease_id: &str) -> Result<()>;
    async fn list_expiring_leases(&self, before: DateTime<Utc>) -> Result<Vec<Lease>>;
}

SurrealDB Implementation

pub struct SurrealDBBackend {
    db: Surreal<Client>,
}

#[async_trait]
impl StorageBackend for SurrealDBBackend {
    async fn store_secret(&self, path: &str, data: &EncryptedData) -> Result<()> {
        let record = SecretRecord {
            path: path.to_string(),
            data: data.clone(),
            created_at: Utc::now(),
            updated_at: Utc::now(),
        };
        
        let _: Option<SecretRecord> = self.db
            .create(("secrets", path))
            .content(record)
            .await?;
        
        Ok(())
    }
    
    async fn list_expiring_leases(&self, before: DateTime<Utc>) -> Result<Vec<Lease>> {
        let leases: Vec<Lease> = self.db
            .query("SELECT * FROM leases WHERE expires_at <= $before")
            .bind(("before", before))
            .await?
            .take(0)?;
        
        Ok(leases)
    }
}

API Design

Vault-Compatible REST API

// Core paths
GET    /v1/sys/health              // Health check
POST   /v1/sys/init                // Initialize vault
POST   /v1/sys/unseal              // Unseal vault
POST   /v1/sys/seal                // Seal vault
GET    /v1/sys/seal-status         // Get seal status

// Auth
POST   /v1/auth/token/create       // Create token
POST   /v1/auth/token/revoke       // Revoke token
POST   /v1/auth/token/lookup       // Lookup token

// Secrets (KV)
GET    /v1/secret/data/:path       // Read secret
POST   /v1/secret/data/:path       // Write secret
DELETE /v1/secret/data/:path       // Delete secret
GET    /v1/secret/metadata/:path   // Get metadata
LIST   /v1/secret/metadata/:path   // List secrets

// Transit (EaaS)
POST   /v1/transit/encrypt/:key    // Encrypt data
POST   /v1/transit/decrypt/:key    // Decrypt data
POST   /v1/transit/sign/:key       // Sign data (PQC)
POST   /v1/transit/verify/:key     // Verify signature
POST   /v1/transit/keys/:key       // Create key
POST   /v1/transit/keys/:key/rotate // Rotate key

// PKI
POST   /v1/pki/root/generate       // Generate root CA
POST   /v1/pki/issue/:role         // Issue certificate
POST   /v1/pki/revoke              // Revoke certificate

// Database (Dynamic)
GET    /v1/database/creds/:role    // Get dynamic creds
POST   /v1/database/config/:name   // Configure connection

// Policies (Cedar)
GET    /v1/sys/policies            // List policies
GET    /v1/sys/policy/:name        // Read policy
PUT    /v1/sys/policy/:name        // Write policy
DELETE /v1/sys/policy/:name        // Delete policy

Request/Response Format

#[derive(Serialize, Deserialize)]
pub struct VaultRequest {
    pub path: String,
    pub operation: Operation, // Read, Write, Delete, List
    pub data: Option<serde_json::Value>,
    pub headers: HashMap<String, String>,
}

#[derive(Serialize, Deserialize)]
pub struct VaultResponse {
    pub request_id: String,
    pub lease_id: Option<String>,
    pub renewable: bool,
    pub lease_duration: u64,
    pub data: Option<serde_json::Value>,
    pub warnings: Vec<String>,
    pub auth: Option<AuthInfo>,
}

Deployment Modes

1. Standalone Server

# Start server
svault server --config svault.toml

# Initialize
svault operator init

# Unseal
svault operator unseal <key>

2. Kubernetes Deployment

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: secretumvault
spec:
  serviceName: secretumvault
  replicas: 3
  selector:
    matchLabels:
      app: secretumvault
  template:
    metadata:
      labels:
        app: secretumvault
    spec:
      containers:
      - name: vault
        image: secretumvault:latest
        ports:
        - containerPort: 8200
          name: api
        env:
        - name: QVAULT_BACKEND
          value: "aws-lc"
        - name: QVAULT_STORAGE
          value: "surrealdb"
        volumeMounts:
        - name: config
          mountPath: /etc/secretumvault
        - name: data
          mountPath: /var/secretumvault
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi

3. Library Mode (Embedded)

// En tu aplicación
use secretumvault::{Vault, VaultConfig};

#[tokio::main]
async fn main() -> Result<()> {
    let config = VaultConfig::builder()
        .crypto_backend(BackendType::AwsLc)
        .storage_backend(StorageType::SurrealDB)
        .build()?;
    
    let vault = Vault::new(config).await?;
    
    // Usar vault embebido
    vault.write("secret/myapp/db", json!({
        "username": "admin",
        "password": "secret123"
    })).await?;
    
    Ok(())
}

Integration Examples

1. Con RustyVault (como Backend)

SecretumVault puede servir como crypto backend de RustyVault:

// RustyVault usa SecretumVault para crypto
use secretumvault::CryptoService;

pub struct RustyVaultWithPQC {
    secrets: RustyVaultCore,
    crypto: Arc<CryptoService>, // SecretumVault crypto
}

impl RustyVaultWithPQC {
    async fn sign_certificate(&self, csr: &[u8]) -> Result<Vec<u8>> {
        // Usar SecretumVault para firmar con ML-DSA
        self.crypto.sign("ca-key", csr).await
    }
}

2. Con provctl (Orchestration)

use secretumvault::Vault;

async fn provision_secure_machine(machine_id: &str) -> Result<()> {
    let vault = Vault::connect("https://vault.internal:8200").await?;
    
    // Get dynamic SSH credentials
    let ssh_creds = vault.read(&format!("ssh/creds/{}", machine_id)).await?;
    
    // Encrypt machine config
    let encrypted_config = vault.transit_encrypt(
        "machine-config-key",
        &machine_config.as_bytes()
    ).await?;
    
    // Deploy
    deploy_machine(machine_id, &ssh_creds, &encrypted_config).await?;
    
    Ok(())
}

3. Con Nushell Scripts

# vault-ops.nu
export def get-db-creds [role: string] {
    let response = (
        http get http://vault:8200/v1/database/creds/$role
        -H [X-Vault-Token $env.VAULT_TOKEN]
    )
    
    {
        username: $response.data.username,
        password: $response.data.password,
        expires: ($response.lease_duration | into datetime)
    }
}

export def encrypt-file [file: string, key: string] {
    let plaintext = (open $file | encode base64)
    
    let response = (
        http post http://vault:8200/v1/transit/encrypt/$key {
            plaintext: $plaintext
        } -H [X-Vault-Token $env.VAULT_TOKEN]
    )
    
    $response.data.ciphertext | save $"($file).encrypted"
}

# Uso
get-db-creds "readonly" | save db-creds.json
encrypt-file "sensitive.txt" "app-key"

4. Con Nickel Config

# vault-bootstrap.ncl
let Vault = {
  server = "https://vault.internal:8200",
  
  policies = {
    developers = m%"
      permit(
        principal in Group::"developers",
        action == Action::"read",
        resource in Path::"secret/dev/*"
      );
    "%,
    
    ci_cd = m%"
      permit(
        principal == ServiceAccount::"github-actions",
        action in [Action::"read", Action::"encrypt"],
        resource in Path::"secret/deployments/*"
      );
    "%,
  },
  
  secrets = {
    database = {
      connection = "postgresql://vault-db:5432/app",
      roles = {
        readonly = {
          creation_statements = [
            "CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';",
            "GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";"
          ],
          ttl = "1h"
        },
        admin = {
          creation_statements = [
            "CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}' SUPERUSER;"
          ],
          ttl = "15m"
        }
      }
    }
  }
}

{
  vault = Vault
}

Implementation Roadmap

Phase 1: Foundation (Week 1-3)

Week 1: Core Architecture

  • Project structure
  • Storage trait + filesystem implementation
  • Basic crypto backend (OpenSSL)
  • Seal/Unseal mechanism with Shamir

Week 2: KV Engine

  • KV secrets engine (v2 with versioning)
  • Encryption at rest
  • API handlers for KV
  • CLI: read/write/delete

Week 3: Auth & Cedar

  • Token-based authentication
  • Cedar policy integration
  • Policy evaluation
  • Basic ACL tests

Phase 2: Crypto Engines (Week 4-6)

Week 4: Transit Engine

  • Encrypt/decrypt endpoints
  • Key rotation
  • Multiple algorithm support
  • Rewrap functionality

Week 5: PQC Integration

  • aws-lc-rs backend
  • ML-KEM implementation
  • ML-DSA signatures
  • Hybrid mode (classical + PQC)

Week 6: PKI Engine

  • Root CA generation
  • Certificate issuance (RSA/ECDSA)
  • CRL/OCSP support
  • PQC certificates (experimental)

Phase 3: Dynamic Secrets (Week 7-8)

Week 7: Database Engine

  • PostgreSQL support
  • MySQL support
  • Lease management
  • Automatic revocation

Week 8: Storage Backends

  • SurrealDB implementation
  • etcd/Consul support
  • Migration tools

Phase 4: Production Ready (Week 9-10)

Week 9: High Availability

  • Leader election
  • Replication
  • Auto-unseal with KMS
  • Performance optimizations

Week 10: Polish & Docs

  • API documentation (OpenAPI)
  • Deployment guides
  • Security audit
  • Benchmarks

Configuration Example

Archivo: svault.toml

[vault]
# Crypto backend
crypto_backend = "aws-lc"  # aws-lc | rustcrypto | tongsuo | openssl

# Storage backend
storage_backend = "surrealdb"  # surrealdb | filesystem | etcd | consul

[server]
address = "0.0.0.0:8200"
tls_cert = "/etc/svault/tls/cert.pem"
tls_key = "/etc/svault/tls/key.pem"

# Mutual TLS (opcional)
tls_client_ca = "/etc/svault/tls/ca.pem"

[storage.surrealdb]
endpoint = "ws://localhost:8000"
namespace = "vault"
database = "production"
username = "vault"
password = "${SURREAL_PASSWORD}"

[storage.filesystem]
path = "/var/svault/data"

[seal]
type = "shamir"  # shamir | auto | transit
shares = 5
threshold = 3

# Auto-unseal con KMS (opcional)
[seal.auto]
type = "aws-kms"  # aws-kms | gcp-kms | azure-kv
key_id = "arn:aws:kms:..."

[auth.cedar]
policies_dir = "/etc/svault/policies"
entities_file = "/etc/svault/entities.json"

[engines]
# Secrets engines montados
kv = { path = "secret/", versioned = true }
transit = { path = "transit/" }
pki = { path = "pki/" }
database = { path = "database/" }

[logging]
level = "info"
format = "json"
output = "/var/log/svault/vault.log"

[telemetry]
prometheus_port = 9090

Project Structure

secretumvault/
├── Cargo.toml
├── README.md
├── config/
│   └── svault.toml.example
│
├── src/
│   ├── lib.rs
│   ├── main.rs                     # Server binary
│   │
│   ├── core/
│   │   ├── mod.rs
│   │   ├── vault.rs                # Main Vault struct
│   │   ├── seal.rs                 # Seal/Unseal logic
│   │   └── router.rs               # Request routing
│   │
│   ├── engines/
│   │   ├── mod.rs
│   │   ├── kv.rs                   # KV engine
│   │   ├── transit.rs              # Transit engine
│   │   ├── pki.rs                  # PKI engine
│   │   └── database.rs             # Dynamic secrets
│   │
│   ├── auth/
│   │   ├── mod.rs
│   │   ├── token.rs                # Token auth
│   │   └── cedar.rs                # Cedar integration
│   │
│   ├── crypto/
│   │   ├── mod.rs
│   │   ├── backend.rs              # Trait
│   │   ├── aws_lc.rs
│   │   ├── rustcrypto.rs
│   │   └── tongsuo.rs
│   │
│   ├── storage/
│   │   ├── mod.rs
│   │   ├── filesystem.rs
│   │   ├── surrealdb.rs
│   │   ├── etcd.rs
│   │   └── consul.rs
│   │
│   ├── api/
│   │   ├── mod.rs
│   │   ├── server.rs
│   │   └── handlers/
│   │       ├── sys.rs
│   │       ├── secret.rs
│   │       ├── transit.rs
│   │       └── pki.rs
│   │
│   ├── shamir/
│   │   └── mod.rs
│   │
│   └── error.rs
│
├── bin/
│   └── svault.rs                   # CLI tool
│
├── policies/
│   ├── default.cedar
│   ├── developers.cedar
│   └── ci-cd.cedar
│
├── scripts/
│   ├── vault-ops.nu                # Nushell helpers
│   └── init-vault.sh
│
├── configs/
│   ├── vault-config.ncl
│   └── bootstrap.ncl
│
└── tests/
    ├── integration/
    ├── engines/
    └── auth/

Cargo.toml

[package]
name = "secretumvault"
version = "0.1.0"
edition = "2021"
authors = ["Jesús Pérez <contact@jesusperez.pro>"]
description = "Post-quantum ready secrets management system"
license = "Apache-2.0"

[features]
default = ["aws-lc", "filesystem", "server"]

# Crypto backends
aws-lc = ["aws-lc-rs"]
rustcrypto = ["ml-kem", "ml-dsa", "slh-dsa"]
tongsuo = ["dep:tongsuo"]
openssl = ["dep:openssl"]

# Storage backends
filesystem = []
surrealdb-storage = ["surrealdb"]
etcd-storage = ["etcd-client"]
consul-storage = ["consulrs"]

# Components
server = ["axum", "tower-http"]
cli = ["clap"]
cedar = ["cedar-policy"]

[dependencies]
# Core
tokio = { version = "1.35", features = ["full"] }
async-trait = "0.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
toml = "0.8"
thiserror = "1.0"
anyhow = "1.0"
chrono = { version = "0.4", features = ["serde"] }
tracing = "0.1"
tracing-subscriber = "0.3"

# Crypto
aws-lc-rs = { version = "1.9", features = ["unstable"], optional = true }
ml-kem = { version = "0.2", optional = true }
ml-dsa = { version = "0.2", optional = true }
slh-dsa = { version = "0.2", optional = true }
openssl = { version = "0.10", optional = true }
tongsuo = { version = "0.3", optional = true }

# Shamir Secret Sharing
sharks = "0.5"

# Cedar policies
cedar-policy = { version = "3.0", optional = true }

# Storage
surrealdb = { version = "1.5", optional = true }
etcd-client = { version = "0.13", optional = true }
consulrs = { version = "0.1", optional = true }

# Server
axum = { version = "0.7", optional = true, features = ["macros"] }
tower-http = { version = "0.5", optional = true, features = ["cors", "trace"] }

# CLI
clap = { version = "4.5", optional = true, features = ["derive"] }

# Utilities
uuid = { version = "1.6", features = ["v4", "serde"] }
base64 = "0.21"
hex = "0.4"

[dev-dependencies]
tempfile = "3.8"
wiremock = "0.5"

[[bin]]
name = "svault"
path = "src/main.rs"

[[bin]]
name = "svault-cli"
path = "bin/svault.rs"
required-features = ["cli"]

¿Por qué SecretumVault vs. Solo Crypto Service?

Feature Crypto Service Solo SecretumVault Completo
Secrets Management KV + Dynamic
Encryption as a Service ⚠️ Básico Transit engine completo
Authorization Cedar policies
PKI/Certificates Full CA
Dynamic Secrets Database, cloud
Lease Management Auto-revocation
Audit Logging Completo
High Availability Replication
Vault API Compatible Migration path

Next Steps

  1. Validar arquitectura - ¿Tiene sentido este scope?
  2. Naming - ¿secretumvault, svault, otro?
  3. Prioridades - ¿Qué implementar primero?
  4. Cedar policies - ¿Es la mejor opción o prefieres ACL simple?
  5. Integración RustyVault - ¿Backend? ¿Fork? ¿Proyecto separado?

¿Empezamos con Phase 1 en Claude Code?