AkurAI Build
Menu

Public repository / fe9611075d08d4b881b528d06a3e3ad5045ec657

AkurAI-PassVault / crates/passvault-mcp/src/tools.rs

Latest change 2c509452a4982321959914cab8d52b881e8368de - AKURAI-PASSVAULT-6 cargo fmt --all by Ólafur Búi Ólafsson

use akurai_json::Value;
use akurai_storage::BTree;
use passvault_core::crypto::Crypto;
use passvault_core::entities::{normalize_entry_name, Folder, VaultEntry};
use passvault_core::types::Timestamp;
use std::sync::{Arc, Mutex};

fn upper_bound(prefix: &[u8]) -> Vec<u8> {
    let mut bound = prefix.to_vec();
    bound.push(0xff);
    bound
}

fn now() -> Timestamp {
    std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap_or_default()
        .as_secs() as i64
}

pub struct ToolRegistry {
    db: Arc<Mutex<BTree>>,
    crypto: Arc<Crypto>,
}

impl ToolRegistry {
    pub fn new(db: Arc<Mutex<BTree>>, crypto: Arc<Crypto>) -> Self {
        ToolRegistry { db, crypto }
    }

    fn json_rpc_result(&self, id: Option<Value>, result: Value) -> String {
        let mut resp = Vec::new();
        resp.push(("jsonrpc".into(), Value::Str("2.0".into())));
        resp.push(("result".into(), result));
        if let Some(id_val) = id {
            resp.push(("id".into(), id_val));
        }
        Value::Object(resp).to_json()
    }

    fn json_rpc_error(&self, id: Option<Value>, code: i64, message: &str) -> String {
        let mut resp = Vec::new();
        resp.push(("jsonrpc".into(), Value::Str("2.0".into())));
        resp.push((
            "error".into(),
            Value::Object(vec![
                ("code".into(), Value::Int(code)),
                ("message".into(), Value::Str(message.into())),
            ]),
        ));
        if let Some(id_val) = id {
            resp.push(("id".into(), id_val));
        }
        Value::Object(resp).to_json()
    }

    pub fn handle_request(&self, body: &str) -> String {
        let req = match akurai_json::parse(body) {
            Ok(v) => v,
            Err(e) => return self.json_rpc_error(None, -32700, &format!("Parse error: {e}")),
        };

        let method = req.get("method").and_then(|v| v.as_str()).unwrap_or("");
        let id = req.get("id").cloned();

        // JSON-RPC notifications carry no `id` and MUST NOT receive a response body.
        // Return an empty string; the HTTP layer turns this into `202 Accepted` with no
        // body (per the MCP Streamable-HTTP spec). Strict clients (codex/rmcp) error out
        // if a notification gets a JSON-RPC response.
        if id.is_none() || method.starts_with("notifications/") {
            return String::new();
        }

        match method {
            "initialize" => self.json_rpc_result(
                id,
                Value::Object(vec![
                    ("protocolVersion".into(), Value::Str("2024-11-05".into())),
                    (
                        "capabilities".into(),
                        Value::Object(vec![("tools".into(), Value::Object(vec![]))]),
                    ),
                    (
                        "serverInfo".into(),
                        Value::Object(vec![
                            ("name".into(), Value::Str("akurai-passvault".into())),
                            ("version".into(), Value::Str("0.0.1".into())),
                        ]),
                    ),
                ]),
            ),
            "tools/list" => {
                let tools = self.list_tools();
                self.json_rpc_result(
                    id,
                    Value::Object(vec![("tools".into(), Value::Array(tools))]),
                )
            }
            "tools/get" => {
                let params = match req.get("params") {
                    Some(Value::Object(pairs)) => pairs.clone(),
                    _ => vec![],
                };
                let tool_name = params
                    .iter()
                    .find(|(k, _)| k == "name")
                    .and_then(|(_, v)| v.as_str())
                    .unwrap_or("");
                let tools = self.list_tools();
                let tool = tools
                    .into_iter()
                    .find(|t| t.get("name").and_then(|v| v.as_str()) == Some(tool_name));
                match tool {
                    Some(t) => self.json_rpc_result(id, Value::Object(vec![("tool".into(), t)])),
                    None => self.json_rpc_error(id, -32602, &format!("Unknown tool: {tool_name}")),
                }
            }
            "tools/call" => {
                let params = match req.get("params") {
                    Some(Value::Object(pairs)) => pairs.clone(),
                    _ => vec![],
                };
                let tool_name = params
                    .iter()
                    .find(|(k, _)| k == "name")
                    .and_then(|(_, v)| v.as_str())
                    .unwrap_or("");
                let arguments: Vec<(String, Value)> = params
                    .iter()
                    .find(|(k, _)| k == "arguments")
                    .and_then(|(_, v)| match v {
                        Value::Object(pairs) => Some(pairs.clone()),
                        _ => None,
                    })
                    .unwrap_or_default();

                match self.execute(tool_name, &arguments) {
                    Ok(result) => self.json_rpc_result(
                        id,
                        Value::Object(vec![(
                            "content".into(),
                            Value::Array(vec![Value::Object(vec![
                                ("type".into(), Value::Str("text".into())),
                                ("text".into(), Value::Str(result.to_json())),
                            ])]),
                        )]),
                    ),
                    Err(e) => self.json_rpc_error(id, -32603, &format!("Tool error: {e}")),
                }
            }
            _ => self.json_rpc_error(id, -32601, &format!("Method not found: {method}")),
        }
    }

    pub fn list_tools(&self) -> Vec<Value> {
        vec![
            self.tool_def(
                "list_entries",
                "List all vault entries (name, uri, username)",
                vec![],
            ),
            self.tool_def(
                "get_entry",
                "Get a single entry by ID with all fields",
                vec![("id", "number", "Entry ID", true)],
            ),
            self.tool_def(
                "create_entry",
                "Create a new vault entry",
                vec![
                    ("name", "string", "Entry name", true),
                    ("username", "string", "Username", false),
                    ("password", "string", "Password", false),
                    ("uri", "string", "URI/URL", false),
                    ("notes", "string", "Notes", false),
                    ("folderId", "number", "Folder ID", false),
                ],
            ),
            self.tool_def(
                "update_entry",
                "Update an existing entry",
                vec![
                    ("id", "number", "Entry ID", true),
                    ("name", "string", "Entry name", false),
                    ("username", "string", "Username", false),
                    ("password", "string", "Password", false),
                    ("uri", "string", "URI/URL", false),
                    ("notes", "string", "Notes", false),
                    ("folderId", "number", "Folder ID", false),
                ],
            ),
            self.tool_def(
                "delete_entry",
                "Delete a vault entry",
                vec![("id", "number", "Entry ID", true)],
            ),
            self.tool_def(
                "generate_password",
                "Generate a random password",
                vec![
                    ("length", "number", "Password length (default 24)", false),
                    (
                        "symbols",
                        "boolean",
                        "Include symbols (default true)",
                        false,
                    ),
                ],
            ),
            self.tool_def(
                "search_entries",
                "Search entries by name, URI, or username",
                vec![("query", "string", "Search query", true)],
            ),
            self.tool_def("list_folders", "List all folders", vec![]),
            self.tool_def(
                "create_folder",
                "Create a new folder",
                vec![("name", "string", "Folder name", true)],
            ),
            self.tool_def(
                "delete_folder",
                "Delete a folder",
                vec![("id", "number", "Folder ID", true)],
            ),
            self.tool_def(
                "system_status",
                "Report PassVault storage health and record counts",
                vec![],
            ),
            self.tool_def(
                "provision_workspace_secret",
                "Create a workspace secret reference without returning the value",
                vec![
                    ("organizationId", "string", "Organization ID", true),
                    ("workspaceId", "string", "Workspace ID", true),
                    ("name", "string", "Secret name", true),
                    ("value", "string", "Secret value", true),
                    ("notes", "string", "Metadata notes", false),
                ],
            ),
            self.tool_def(
                "get_workspace_secret_metadata",
                "Get secret metadata (name, rotation status) — never the value",
                vec![
                    ("organizationId", "string", "Organization ID", true),
                    ("workspaceId", "string", "Workspace ID", true),
                    ("id", "number", "Secret ID", true),
                ],
            ),
            self.tool_def(
                "get_workspace_secret_value",
                "Get secret value — requires a grant with sufficient visibility",
                vec![
                    ("organizationId", "string", "Organization ID", true),
                    ("workspaceId", "string", "Workspace ID", true),
                    ("id", "number", "Secret ID", true),
                    ("identityId", "number", "Service identity ID", true),
                    ("purpose", "string", "Access purpose for audit", true),
                ],
            ),
            self.tool_def(
                "list_workspace_secrets",
                "List all secrets in a workspace (metadata only)",
                vec![
                    ("organizationId", "string", "Organization ID", true),
                    ("workspaceId", "string", "Workspace ID", true),
                ],
            ),
            self.tool_def(
                "delete_workspace_secret",
                "Delete a workspace secret (idempotent)",
                vec![
                    ("organizationId", "string", "Organization ID", true),
                    ("workspaceId", "string", "Workspace ID", true),
                    ("id", "number", "Secret ID", true),
                ],
            ),
            self.tool_def(
                "create_service_identity",
                "Create a service identity for workspace secret access",
                vec![
                    ("organizationId", "string", "Organization ID", true),
                    ("workspaceId", "string", "Workspace ID", true),
                    ("name", "string", "Identity name", true),
                ],
            ),
            self.tool_def(
                "list_service_identities",
                "List service identities in a workspace",
                vec![
                    ("organizationId", "string", "Organization ID", true),
                    ("workspaceId", "string", "Workspace ID", true),
                ],
            ),
            self.tool_def(
                "delete_service_identity",
                "Delete a service identity and its grants (idempotent)",
                vec![
                    ("organizationId", "string", "Organization ID", true),
                    ("workspaceId", "string", "Workspace ID", true),
                    ("id", "number", "Identity ID", true),
                ],
            ),
            self.tool_def(
                "create_secret_grant",
                "Grant a service identity access to a secret with a visibility level",
                vec![
                    ("organizationId", "string", "Organization ID", true),
                    ("workspaceId", "string", "Workspace ID", true),
                    ("identityId", "number", "Identity ID", true),
                    ("secretId", "number", "Secret ID", true),
                    (
                        "visibility",
                        "string",
                        "metadata | hidden_programmatic | forbidden_display",
                        true,
                    ),
                ],
            ),
            self.tool_def(
                "list_secret_grants",
                "List grants, optionally filtered by secretId",
                vec![
                    ("organizationId", "string", "Organization ID", true),
                    ("workspaceId", "string", "Workspace ID", true),
                    (
                        "secretId",
                        "number",
                        "Filter by secret ID (optional)",
                        false,
                    ),
                ],
            ),
            self.tool_def(
                "delete_secret_grant",
                "Delete a grant (idempotent)",
                vec![
                    ("organizationId", "string", "Organization ID", true),
                    ("workspaceId", "string", "Workspace ID", true),
                    ("id", "number", "Grant ID", true),
                ],
            ),
            self.tool_def(
                "rotate_secret",
                "Rotate a secret value with optional overlap window for rollback",
                vec![
                    ("organizationId", "string", "Organization ID", true),
                    ("workspaceId", "string", "Workspace ID", true),
                    ("id", "number", "Secret ID", true),
                    ("newValue", "string", "New secret value", true),
                    (
                        "overlapSeconds",
                        "number",
                        "Overlap window before cutover (optional)",
                        false,
                    ),
                ],
            ),
            self.tool_def(
                "rollback_secret",
                "Rollback a rotated secret to its previous value",
                vec![
                    ("organizationId", "string", "Organization ID", true),
                    ("workspaceId", "string", "Workspace ID", true),
                    ("id", "number", "Secret ID", true),
                ],
            ),
            self.tool_def(
                "revoke_workspace_secret",
                "Deactivate a secret (idempotent)",
                vec![
                    ("organizationId", "string", "Organization ID", true),
                    ("workspaceId", "string", "Workspace ID", true),
                    ("id", "number", "Secret ID", true),
                ],
            ),
            self.tool_def(
                "deprovision_workspace",
                "Remove all workspace-scoped data (idempotent)",
                vec![
                    ("organizationId", "string", "Organization ID", true),
                    ("workspaceId", "string", "Workspace ID", true),
                ],
            ),
            self.tool_def(
                "list_audit_entries",
                "List audit entries for a workspace",
                vec![
                    ("organizationId", "string", "Organization ID", true),
                    ("workspaceId", "string", "Workspace ID", true),
                ],
            ),
        ]
    }

    fn tool_def(
        &self,
        name: &str,
        description: &str,
        params: Vec<(&str, &str, &str, bool)>,
    ) -> Value {
        let mut properties = Vec::new();
        let mut required = Vec::new();
        for (pname, ptype, pdesc, is_required) in params {
            properties.push((
                pname.to_string(),
                Value::Object(vec![
                    ("type".into(), Value::Str(ptype.into())),
                    ("description".into(), Value::Str(pdesc.into())),
                ]),
            ));
            if is_required {
                required.push(Value::Str(pname.to_string()));
            }
        }
        Value::Object(vec![
            ("name".into(), Value::Str(name.into())),
            ("description".into(), Value::Str(description.into())),
            (
                "inputSchema".into(),
                Value::Object(vec![
                    ("type".into(), Value::Str("object".into())),
                    ("properties".into(), Value::Object(properties)),
                    ("required".into(), Value::Array(required)),
                ]),
            ),
        ])
    }

    pub fn execute(&self, tool_name: &str, args: &[(String, Value)]) -> Result<Value, String> {
        match tool_name {
            "list_entries" => self.list_entries(),
            "get_entry" => self.get_entry(args),
            "create_entry" => self.create_entry(args),
            "update_entry" => self.update_entry(args),
            "delete_entry" => self.delete_entry(args),
            "generate_password" => self.generate_password(args),
            "search_entries" => self.search_entries(args),
            "list_folders" => self.list_folders(),
            "create_folder" => self.create_folder(args),
            "delete_folder" => self.delete_folder(args),
            "system_status" => self.system_status(),
            "provision_workspace_secret" => self.provision_workspace_secret(args),
            "get_workspace_secret_metadata" => self.get_workspace_secret_metadata(args),
            "get_workspace_secret_value" => self.get_workspace_secret_value(args),
            "list_workspace_secrets" => self.list_workspace_secrets(args),
            "delete_workspace_secret" => self.delete_workspace_secret(args),
            "create_service_identity" => self.create_service_identity(args),
            "list_service_identities" => self.list_service_identities(args),
            "delete_service_identity" => self.delete_service_identity(args),
            "create_secret_grant" => self.create_secret_grant(args),
            "list_secret_grants" => self.list_secret_grants(args),
            "delete_secret_grant" => self.delete_secret_grant(args),
            "rotate_secret" => self.rotate_secret(args),
            "rollback_secret" => self.rollback_secret(args),
            "revoke_workspace_secret" => self.revoke_workspace_secret(args),
            "deprovision_workspace" => self.deprovision_workspace(args),
            "list_audit_entries" => self.list_audit_entries(args),
            _ => Err(format!("unknown tool: {tool_name}")),
        }
    }
    fn system_status(&self) -> Result<Value, String> {
        let entries = self.scan_entries()?.len() as i64;
        let folders = match self.list_folders()? {
            Value::Array(values) => values.len() as i64,
            _ => 0,
        };
        Ok(Value::Object(vec![
            ("status".into(), Value::Str("ok".into())),
            ("entries".into(), Value::Int(entries)),
            ("folders".into(), Value::Int(folders)),
        ]))
    }

    fn scan_entries(&self) -> Result<Vec<(i64, VaultEntry)>, String> {
        let prefix = b"vault:entry:";
        let start = prefix.to_vec();
        let end = upper_bound(prefix);
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let entries = db.range(&start, &end).map_err(|e| format!("range: {e}"))?;
        let mut results = Vec::new();
        for (key, val) in entries {
            let key_str = String::from_utf8_lossy(&key).to_string();
            if key_str.contains("counter") {
                continue;
            }
            let id = key_str
                .strip_prefix("vault:entry:")
                .and_then(|s| s.parse::<i64>().ok())
                .unwrap_or(0);
            let decrypted = self
                .crypto
                .decrypt(&val)
                .map_err(|e| format!("decrypt: {e}"))?;
            if let Ok(v) = akurai_json::parse(&String::from_utf8_lossy(&decrypted)) {
                if let Ok(entry) = VaultEntry::from_value(&v) {
                    results.push((id, entry));
                }
            }
        }
        Ok(results)
    }

    fn get_entry_raw(&self, id: i64) -> Result<VaultEntry, String> {
        let key = format!("vault:entry:{id}");
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let bytes = db
            .get(key.as_bytes())
            .map_err(|e| format!("storage: {e}"))?
            .ok_or_else(|| "entry not found".to_string())?;
        let decrypted = self
            .crypto
            .decrypt(&bytes)
            .map_err(|e| format!("decrypt: {e}"))?;
        let v = akurai_json::parse(&String::from_utf8_lossy(&decrypted))
            .map_err(|e| format!("json: {e}"))?;
        VaultEntry::from_value(&v).map_err(|e| format!("parse: {e}"))
    }

    fn list_entries(&self) -> Result<Value, String> {
        let entries = self.scan_entries()?;
        Ok(Value::Array(
            entries
                .into_iter()
                .map(|(_, e)| e.summary_value())
                .collect(),
        ))
    }

    fn get_entry(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let id = get_i64(args, "id").ok_or("missing id")?;
        let entry = self.get_entry_raw(id)?;
        Ok(entry.to_value())
    }

    fn create_entry(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let name = normalize_entry_name(&get_str(args, "name").ok_or("name is required")?)
            .map_err(|e| e.to_string())?;
        let username = get_str(args, "username").unwrap_or_default();
        let password = get_str(args, "password").unwrap_or_default();
        let uri = get_str(args, "uri").unwrap_or_default();
        let notes = get_str(args, "notes").unwrap_or_default();
        let folder_id = get_i64(args, "folderId");
        let ts = now();

        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let next_id = next_counter(&mut db, "vault:entry:counter");

        let entry = VaultEntry {
            id: next_id,
            name,
            username,
            password,
            uri,
            notes,
            folder_id,
            created_at: ts,
            updated_at: ts,
        };

        let encrypted = self
            .crypto
            .encrypt(entry.to_value().to_json().as_bytes())
            .map_err(|e| format!("encrypt: {e}"))?;
        db.insert(format!("vault:entry:{next_id}").as_bytes(), &encrypted)
            .map_err(|e| format!("insert: {e}"))?;
        db.commit().map_err(|e| format!("commit: {e}"))?;

        Ok(entry.to_value())
    }

    fn update_entry(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let id = get_i64(args, "id").ok_or("missing id")?;
        let mut entry = self.get_entry_raw(id)?;

        if let Some(v) = get_str(args, "name") {
            entry.name = normalize_entry_name(&v).map_err(|e| e.to_string())?;
        }
        if let Some(v) = get_str(args, "username") {
            entry.username = v;
        }
        if let Some(v) = get_str(args, "password") {
            entry.password = v;
        }
        if let Some(v) = get_str(args, "uri") {
            entry.uri = v;
        }
        if let Some(v) = get_str(args, "notes") {
            entry.notes = v;
        }
        if let Some(value) = get_arg(args, "folderId") {
            entry.folder_id = value.as_i64();
        }
        entry.updated_at = now();

        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let encrypted = self
            .crypto
            .encrypt(entry.to_value().to_json().as_bytes())
            .map_err(|e| format!("encrypt: {e}"))?;
        db.insert(format!("vault:entry:{id}").as_bytes(), &encrypted)
            .map_err(|e| format!("insert: {e}"))?;
        db.commit().map_err(|e| format!("commit: {e}"))?;

        Ok(entry.to_value())
    }

    fn delete_entry(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let id = get_i64(args, "id").ok_or("missing id")?;
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let removed = db
            .delete(format!("vault:entry:{id}").as_bytes())
            .map_err(|e| format!("delete: {e}"))?;
        if !removed {
            return Err(format!("entry {id} not found"));
        }
        db.commit().map_err(|e| format!("commit: {e}"))?;
        Ok(Value::Object(vec![("deleted".into(), Value::Bool(true))]))
    }

    fn generate_password(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let length = get_i64(args, "length").unwrap_or(24).clamp(8, 128) as usize;
        let symbols = get_bool(args, "symbols").unwrap_or(true);

        let mut charset = Vec::new();
        charset.extend(b'a'..=b'z');
        charset.extend(b'A'..=b'Z');
        charset.extend(b'0'..=b'9');
        if symbols {
            charset.extend(b"!@#$%^&*()_+-=[]{}|;:,.<>?");
        }

        use rand::Rng;
        let password: String = (0..length)
            .map(|_| charset[rand::thread_rng().gen_range(0..charset.len())] as char)
            .collect();

        Ok(Value::Object(vec![(
            "password".into(),
            Value::Str(password),
        )]))
    }

    fn search_entries(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let query = get_str(args, "query")
            .ok_or("missing query")?
            .to_lowercase();
        let entries = self.scan_entries()?;
        let results: Vec<Value> = entries
            .into_iter()
            .filter(|(_, e)| {
                e.name.to_lowercase().contains(&query)
                    || e.uri.to_lowercase().contains(&query)
                    || e.username.to_lowercase().contains(&query)
                    || e.notes.to_lowercase().contains(&query)
            })
            .map(|(_, e)| e.summary_value())
            .collect();
        Ok(Value::Array(results))
    }

    fn list_folders(&self) -> Result<Value, String> {
        let prefix = b"vault:folder:";
        let start = prefix.to_vec();
        let end = upper_bound(prefix);
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let entries = db.range(&start, &end).map_err(|e| format!("range: {e}"))?;
        let folders: Vec<Value> = entries
            .into_iter()
            .filter_map(|(_, val)| {
                let v = akurai_json::parse(&String::from_utf8_lossy(&val)).ok()?;
                Folder::from_value(&v).ok().map(|f| f.to_value())
            })
            .collect();
        Ok(Value::Array(folders))
    }

    fn create_folder(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let name = get_str(args, "name").ok_or("name is required")?;
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let next_id = next_counter(&mut db, "vault:folder:counter");

        let folder = Folder {
            id: next_id,
            name,
            created_at: now(),
        };
        db.insert(
            format!("vault:folder:{next_id}").as_bytes(),
            folder.to_value().to_json().as_bytes(),
        )
        .map_err(|e| format!("insert: {e}"))?;
        db.commit().map_err(|e| format!("commit: {e}"))?;

        Ok(folder.to_value())
    }

    fn delete_folder(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let id = get_i64(args, "id").ok_or("missing id")?;
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let removed = db
            .delete(format!("vault:folder:{id}").as_bytes())
            .map_err(|e| format!("delete: {e}"))?;
        if !removed {
            return Err(format!("folder {id} not found"));
        }
        db.commit().map_err(|e| format!("commit: {e}"))?;
        Ok(Value::Object(vec![("deleted".into(), Value::Bool(true))]))
    }

    // ── Workspace key helpers ──────────────────────────────────────────

    fn ws_key(&self, org: &str, ws: &str, suffix: &str) -> String {
        format!("ws:{org}:{ws}:{suffix}")
    }

    fn ws_counter(&self, db: &mut BTree, org: &str, ws: &str, kind: &str) -> i64 {
        next_counter(db, &format!("ws:{org}:{ws}:{kind}:counter"))
    }

    #[allow(clippy::type_complexity)]
    fn ws_scan_prefix(
        &self,
        db: &mut BTree,
        org: &str,
        ws: &str,
        kind: &str,
    ) -> Result<KeyValuePairs, String> {
        let prefix = format!("ws:{org}:{ws}:{kind}:");
        let start = prefix.as_bytes().to_vec();
        let end = upper_bound(&start);
        db.range(&start, &end).map_err(|e| format!("range: {e}"))
    }

    #[allow(clippy::too_many_arguments)]
    fn write_audit(
        &self,
        db: &mut BTree,
        org: &str,
        ws: &str,
        actor: &str,
        action: &str,
        target: &str,
        purpose: &str,
        result: &str,
    ) -> Result<(), String> {
        let id = self.ws_counter(db, org, ws, "audit");
        let entry = passvault_core::entities::AuditEntry {
            id,
            organization_id: org.to_string(),
            workspace_id: ws.to_string(),
            actor: actor.to_string(),
            action: action.to_string(),
            target: target.to_string(),
            purpose: purpose.to_string(),
            result: result.to_string(),
            timestamp: now(),
        };
        db.insert(
            self.ws_key(org, ws, &format!("audit:{id}")).as_bytes(),
            entry.to_value().to_json().as_bytes(),
        )
        .map_err(|e| format!("audit write: {e}"))
    }

    // ── Service Identity tools ─────────────────────────────────────────

    fn create_service_identity(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let org = get_str(args, "organizationId").ok_or("missing organizationId")?;
        let ws = get_str(args, "workspaceId").ok_or("missing workspaceId")?;
        let name = get_str(args, "name").ok_or("missing name")?;
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let id = self.ws_counter(&mut db, &org, &ws, "identity");
        let identity = passvault_core::entities::ServiceIdentity {
            id,
            organization_id: org.clone(),
            workspace_id: ws.clone(),
            name,
            created_at: now(),
        };
        let key = self.ws_key(&org, &ws, &format!("identity:{id}"));
        db.insert(key.as_bytes(), identity.to_value().to_json().as_bytes())
            .map_err(|e| format!("insert: {e}"))?;
        self.write_audit(
            &mut db,
            &org,
            &ws,
            "system",
            "create_identity",
            &identity.name,
            "provisioning",
            "allowed",
        )?;
        db.commit().map_err(|e| format!("commit: {e}"))?;
        Ok(identity.to_value())
    }

    fn list_service_identities(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let org = get_str(args, "organizationId").ok_or("missing organizationId")?;
        let ws = get_str(args, "workspaceId").ok_or("missing workspaceId")?;
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let rows = self.ws_scan_prefix(&mut db, &org, &ws, "identity")?;
        let identities: Vec<Value> = rows
            .into_iter()
            .filter_map(|(_, val)| {
                let v = akurai_json::parse(&String::from_utf8_lossy(&val)).ok()?;
                passvault_core::entities::ServiceIdentity::from_value(&v)
                    .ok()
                    .map(|i| i.to_value())
            })
            .collect();
        Ok(Value::Array(identities))
    }

    fn delete_service_identity(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let org = get_str(args, "organizationId").ok_or("missing organizationId")?;
        let ws = get_str(args, "workspaceId").ok_or("missing workspaceId")?;
        let id = get_i64(args, "id").ok_or("missing id")?;
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let removed = db
            .delete(self.ws_key(&org, &ws, &format!("identity:{id}")).as_bytes())
            .map_err(|e| format!("delete: {e}"))?;
        if !removed {
            return Ok(Value::Object(vec![("deleted".into(), Value::Bool(false))]));
        }
        // Remove all grants for this identity
        let grant_rows = self.ws_scan_prefix(&mut db, &org, &ws, "grant")?;
        for (key, val) in &grant_rows {
            if let Ok(v) = akurai_json::parse(&String::from_utf8_lossy(val)) {
                if let Ok(g) = passvault_core::entities::SecretGrant::from_value(&v) {
                    if g.identity_id == id {
                        let _ = db.delete(key).map_err(|e| format!("delete grant: {e}"));
                    }
                }
            }
        }
        self.write_audit(
            &mut db,
            &org,
            &ws,
            "system",
            "delete_identity",
            &format!("identity:{id}"),
            "deprovisioning",
            "allowed",
        )?;
        db.commit().map_err(|e| format!("commit: {e}"))?;
        Ok(Value::Object(vec![("deleted".into(), Value::Bool(true))]))
    }

    // ── Secret Grant tools ─────────────────────────────────────────────

    fn create_secret_grant(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let org = get_str(args, "organizationId").ok_or("missing organizationId")?;
        let ws = get_str(args, "workspaceId").ok_or("missing workspaceId")?;
        let identity_id = get_i64(args, "identityId").ok_or("missing identityId")?;
        let secret_id = get_i64(args, "secretId").ok_or("missing secretId")?;
        let visibility = get_str(args, "visibility").unwrap_or_else(|| "metadata".to_string());
        // Validate visibility
        if passvault_core::types::GrantVisibility::parse(&visibility).is_none() {
            return Err(format!("invalid visibility: {visibility}"));
        }
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let id = self.ws_counter(&mut db, &org, &ws, "grant");
        let grant = passvault_core::entities::SecretGrant {
            id,
            organization_id: org.clone(),
            workspace_id: ws.clone(),
            identity_id,
            secret_id,
            visibility,
            created_at: now(),
        };
        db.insert(
            self.ws_key(&org, &ws, &format!("grant:{id}")).as_bytes(),
            grant.to_value().to_json().as_bytes(),
        )
        .map_err(|e| format!("insert: {e}"))?;
        self.write_audit(
            &mut db,
            &org,
            &ws,
            "system",
            "create_grant",
            &format!("identity:{identity_id}->secret:{secret_id}"),
            "provisioning",
            "allowed",
        )?;
        db.commit().map_err(|e| format!("commit: {e}"))?;
        Ok(grant.to_value())
    }

    fn list_secret_grants(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let org = get_str(args, "organizationId").ok_or("missing organizationId")?;
        let ws = get_str(args, "workspaceId").ok_or("missing workspaceId")?;
        let secret_id = get_i64(args, "secretId");
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let rows = self.ws_scan_prefix(&mut db, &org, &ws, "grant")?;
        let grants: Vec<Value> = rows
            .into_iter()
            .filter_map(|(_, val)| {
                let v = akurai_json::parse(&String::from_utf8_lossy(&val)).ok()?;
                let g = passvault_core::entities::SecretGrant::from_value(&v).ok()?;
                if let Some(sid) = secret_id {
                    if g.secret_id != sid {
                        return None;
                    }
                }
                Some(g.to_value())
            })
            .collect();
        Ok(Value::Array(grants))
    }

    fn delete_secret_grant(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let org = get_str(args, "organizationId").ok_or("missing organizationId")?;
        let ws = get_str(args, "workspaceId").ok_or("missing workspaceId")?;
        let id = get_i64(args, "id").ok_or("missing id")?;
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let removed = db
            .delete(self.ws_key(&org, &ws, &format!("grant:{id}")).as_bytes())
            .map_err(|e| format!("delete: {e}"))?;
        if !removed {
            return Ok(Value::Object(vec![("deleted".into(), Value::Bool(false))]));
        }
        self.write_audit(
            &mut db,
            &org,
            &ws,
            "system",
            "delete_grant",
            &format!("grant:{id}"),
            "deprovisioning",
            "allowed",
        )?;
        db.commit().map_err(|e| format!("commit: {e}"))?;
        Ok(Value::Object(vec![("deleted".into(), Value::Bool(true))]))
    }

    // ── Managed Workspace Secret tools ─────────────────────────────────

    fn provision_workspace_secret(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let org = get_str(args, "organizationId").ok_or("missing organizationId")?;
        let ws = get_str(args, "workspaceId").ok_or("missing workspaceId")?;
        let name = get_str(args, "name").ok_or("missing name")?;
        let value = get_str(args, "value").ok_or("missing value")?;
        let notes = get_str(args, "notes").unwrap_or_default();
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let id = self.ws_counter(&mut db, &org, &ws, "secret");
        let secret = passvault_core::entities::WorkspaceSecret {
            id,
            organization_id: org.clone(),
            workspace_id: ws.clone(),
            name: name.clone(),
            value,
            notes,
            active: true,
            rotation_id: None,
            created_at: now(),
            updated_at: now(),
        };
        let encrypted = self
            .crypto
            .encrypt(secret.to_value().to_json().as_bytes())
            .map_err(|e| format!("encrypt: {e}"))?;
        db.insert(
            self.ws_key(&org, &ws, &format!("secret:{id}")).as_bytes(),
            &encrypted,
        )
        .map_err(|e| format!("insert: {e}"))?;
        self.write_audit(
            &mut db,
            &org,
            &ws,
            "system",
            "provision",
            &name,
            "provisioning",
            "allowed",
        )?;
        db.commit().map_err(|e| format!("commit: {e}"))?;
        // Return metadata only — never the value
        let meta = secret.metadata_value();
        Ok(meta)
    }

    fn get_workspace_secret_metadata(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let org = get_str(args, "organizationId").ok_or("missing organizationId")?;
        let ws = get_str(args, "workspaceId").ok_or("missing workspaceId")?;
        let id = get_i64(args, "id").ok_or("missing id")?;
        let secret = self.get_ws_secret_raw(&org, &ws, id)?;
        Ok(secret.metadata_value())
    }

    fn get_workspace_secret_value(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let org = get_str(args, "organizationId").ok_or("missing organizationId")?;
        let ws = get_str(args, "workspaceId").ok_or("missing workspaceId")?;
        let id = get_i64(args, "id").ok_or("missing id")?;
        let identity_id = get_i64(args, "identityId").ok_or("missing identityId")?;
        let purpose = get_str(args, "purpose").ok_or("missing purpose")?;
        // Check grant visibility
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let grant_rows = self.ws_scan_prefix(&mut db, &org, &ws, "grant")?;
        let mut allowed = false;
        let mut visibility = "metadata".to_string();
        for (_, val) in &grant_rows {
            if let Ok(v) = akurai_json::parse(&String::from_utf8_lossy(val)) {
                if let Ok(g) = passvault_core::entities::SecretGrant::from_value(&v) {
                    if g.identity_id == identity_id && g.secret_id == id {
                        allowed = true;
                        visibility = g.visibility.clone();
                        break;
                    }
                }
            }
        }
        drop(db);
        if !allowed {
            let mut db2 = self.db.lock().map_err(|e| format!("lock: {e}"))?;
            self.write_audit(
                &mut db2,
                &org,
                &ws,
                &format!("identity:{identity_id}"),
                "retrieve_denied",
                &format!("secret:{id}"),
                &purpose,
                "denied",
            )?;
            db2.commit().ok();
            return Err("access denied: no grant for this identity/secret pair".to_string());
        }
        if visibility == "metadata" {
            return Err("access denied: grant is metadata-only".to_string());
        }
        let secret = self.get_ws_secret_raw(&org, &ws, id)?;
        let mut db2 = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        self.write_audit(
            &mut db2,
            &org,
            &ws,
            &format!("identity:{identity_id}"),
            "retrieve",
            &secret.name,
            &purpose,
            "allowed",
        )?;
        db2.commit().ok();
        let mut resp = secret.to_value();
        if let Value::Object(ref mut pairs) = resp {
            pairs.push(("_grantVisibility".into(), Value::Str(visibility.clone())));
        }
        Ok(resp)
    }

    fn list_workspace_secrets(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let org = get_str(args, "organizationId").ok_or("missing organizationId")?;
        let ws = get_str(args, "workspaceId").ok_or("missing workspaceId")?;
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let rows = self.ws_scan_prefix(&mut db, &org, &ws, "secret")?;
        let secrets: Vec<Value> = rows
            .into_iter()
            .filter_map(|(_, encrypted)| {
                let decrypted = self.crypto.decrypt(&encrypted).ok()?;
                let v = akurai_json::parse(&String::from_utf8_lossy(&decrypted)).ok()?;
                passvault_core::entities::WorkspaceSecret::from_value(&v)
                    .ok()
                    .map(|s| s.metadata_value())
            })
            .collect();
        Ok(Value::Array(secrets))
    }

    fn delete_workspace_secret(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let org = get_str(args, "organizationId").ok_or("missing organizationId")?;
        let ws = get_str(args, "workspaceId").ok_or("missing workspaceId")?;
        let id = get_i64(args, "id").ok_or("missing id")?;
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let removed = db
            .delete(self.ws_key(&org, &ws, &format!("secret:{id}")).as_bytes())
            .map_err(|e| format!("delete: {e}"))?;
        let _ = db.delete(self.ws_key(&org, &ws, &format!("rotation:{id}")).as_bytes());
        let _ = db.delete(
            self.ws_key(&org, &ws, &format!("secret_prev:{id}"))
                .as_bytes(),
        );
        self.write_audit(
            &mut db,
            &org,
            &ws,
            "system",
            "delete_secret",
            &format!("secret:{id}"),
            "deprovisioning",
            if removed { "allowed" } else { "noop" },
        )?;
        db.commit().map_err(|e| format!("commit: {e}"))?;
        Ok(Value::Object(vec![(
            "deleted".into(),
            Value::Bool(removed),
        )]))
    }

    // ── Rotation tools ─────────────────────────────────────────────────

    fn rotate_secret(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let org = get_str(args, "organizationId").ok_or("missing organizationId")?;
        let ws = get_str(args, "workspaceId").ok_or("missing workspaceId")?;
        let id = get_i64(args, "id").ok_or("missing id")?;
        let new_value = get_str(args, "newValue").ok_or("missing newValue")?;
        let overlap_secs = get_i64(args, "overlapSeconds");

        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let key = self.ws_key(&org, &ws, &format!("secret:{id}"));
        let current_bytes = db
            .get(key.as_bytes())
            .map_err(|e| format!("storage: {e}"))?
            .ok_or("secret not found")?;
        let decrypted = self
            .crypto
            .decrypt(&current_bytes)
            .map_err(|e| format!("decrypt: {e}"))?;
        let v = akurai_json::parse(&String::from_utf8_lossy(&decrypted))
            .map_err(|e| format!("json: {e}"))?;
        let mut secret = passvault_core::entities::WorkspaceSecret::from_value(&v)
            .map_err(|e| format!("parse: {e}"))?;

        // Hash current value for rollback tracking
        let prev_hash = {
            use std::hash::{Hash, Hasher};
            let mut hasher = std::collections::hash_map::DefaultHasher::new();
            secret.value.hash(&mut hasher);
            format!("{:x}", hasher.finish())
        };

        // Save previous version for rollback
        db.insert(
            self.ws_key(&org, &ws, &format!("secret_prev:{id}"))
                .as_bytes(),
            &current_bytes,
        )
        .map_err(|e| format!("save prev: {e}"))?;

        // Update secret with new value
        secret.value = new_value;
        secret.updated_at = now();
        let rotation_id = self.ws_counter(&mut db, &org, &ws, "rotation");
        secret.rotation_id = Some(rotation_id);

        let phase = if overlap_secs.is_some() {
            "overlap"
        } else {
            "active"
        };
        let overlap_until = overlap_secs.map(|secs| now() + secs);
        let rotation = passvault_core::entities::RotationState {
            id: rotation_id,
            secret_id: id,
            previous_value_hash: Some(prev_hash),
            phase: phase.to_string(),
            activated_at: now(),
            overlap_until,
            created_at: now(),
        };

        let encrypted = self
            .crypto
            .encrypt(secret.to_value().to_json().as_bytes())
            .map_err(|e| format!("encrypt: {e}"))?;
        db.insert(key.as_bytes(), &encrypted)
            .map_err(|e| format!("insert: {e}"))?;
        db.insert(
            self.ws_key(&org, &ws, &format!("rotation:{id}")).as_bytes(),
            rotation.to_value().to_json().as_bytes(),
        )
        .map_err(|e| format!("rotation insert: {e}"))?;

        self.write_audit(
            &mut db,
            &org,
            &ws,
            "system",
            "rotate",
            &secret.name,
            "rotation",
            "allowed",
        )?;
        db.commit().map_err(|e| format!("commit: {e}"))?;
        Ok(Value::Object(vec![
            ("rotationId".into(), Value::Int(rotation_id)),
            ("phase".into(), Value::Str(phase.to_string())),
        ]))
    }

    fn rollback_secret(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let org = get_str(args, "organizationId").ok_or("missing organizationId")?;
        let ws = get_str(args, "workspaceId").ok_or("missing workspaceId")?;
        let id = get_i64(args, "id").ok_or("missing id")?;
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;

        let rotation_key = self.ws_key(&org, &ws, &format!("rotation:{id}"));
        let rotation_bytes = db
            .get(rotation_key.as_bytes())
            .map_err(|e| format!("storage: {e}"))?
            .ok_or("no rotation found for this secret")?;
        let rot_v = akurai_json::parse(&String::from_utf8_lossy(&rotation_bytes))
            .map_err(|e| format!("json: {e}"))?;
        let rotation = passvault_core::entities::RotationState::from_value(&rot_v)
            .map_err(|e| format!("parse: {e}"))?;

        if rotation.phase == "rolled_back" {
            return Err("already rolled back".to_string());
        }

        let prev_key = self.ws_key(&org, &ws, &format!("secret_prev:{id}"));
        let prev_bytes = db
            .get(prev_key.as_bytes())
            .map_err(|e| format!("storage: {e}"))?
            .ok_or("no previous version available")?;

        let decrypted = self
            .crypto
            .decrypt(&prev_bytes)
            .map_err(|e| format!("decrypt: {e}"))?;
        let v = akurai_json::parse(&String::from_utf8_lossy(&decrypted))
            .map_err(|e| format!("json: {e}"))?;
        let mut secret = passvault_core::entities::WorkspaceSecret::from_value(&v)
            .map_err(|e| format!("parse: {e}"))?;
        secret.updated_at = now();

        let encrypted = self
            .crypto
            .encrypt(secret.to_value().to_json().as_bytes())
            .map_err(|e| format!("encrypt: {e}"))?;
        let secret_key = self.ws_key(&org, &ws, &format!("secret:{id}"));
        db.insert(secret_key.as_bytes(), &encrypted)
            .map_err(|e| format!("insert: {e}"))?;

        let mut updated_rotation = rotation;
        updated_rotation.phase = "rolled_back".to_string();
        db.insert(
            rotation_key.as_bytes(),
            updated_rotation.to_value().to_json().as_bytes(),
        )
        .map_err(|e| format!("rotation update: {e}"))?;
        let _ = db.delete(prev_key.as_bytes());

        self.write_audit(
            &mut db,
            &org,
            &ws,
            "system",
            "rollback",
            &secret.name,
            "rotation",
            "allowed",
        )?;
        db.commit().map_err(|e| format!("commit: {e}"))?;
        Ok(Value::Object(vec![(
            "rolledBack".into(),
            Value::Bool(true),
        )]))
    }

    fn revoke_workspace_secret(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let org = get_str(args, "organizationId").ok_or("missing organizationId")?;
        let ws = get_str(args, "workspaceId").ok_or("missing workspaceId")?;
        let id = get_i64(args, "id").ok_or("missing id")?;
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let key = self.ws_key(&org, &ws, &format!("secret:{id}"));
        let bytes = match db
            .get(key.as_bytes())
            .map_err(|e| format!("storage: {e}"))?
        {
            Some(b) => b,
            None => return Ok(Value::Object(vec![("revoked".into(), Value::Bool(false))])),
        };
        let decrypted = self
            .crypto
            .decrypt(&bytes)
            .map_err(|e| format!("decrypt: {e}"))?;
        let v = akurai_json::parse(&String::from_utf8_lossy(&decrypted))
            .map_err(|e| format!("json: {e}"))?;
        let mut secret = passvault_core::entities::WorkspaceSecret::from_value(&v)
            .map_err(|e| format!("parse: {e}"))?;
        secret.active = false;
        secret.updated_at = now();
        let encrypted = self
            .crypto
            .encrypt(secret.to_value().to_json().as_bytes())
            .map_err(|e| format!("encrypt: {e}"))?;
        db.insert(key.as_bytes(), &encrypted)
            .map_err(|e| format!("insert: {e}"))?;
        self.write_audit(
            &mut db,
            &org,
            &ws,
            "system",
            "revoke",
            &secret.name,
            "deprovisioning",
            "allowed",
        )?;
        db.commit().map_err(|e| format!("commit: {e}"))?;
        Ok(Value::Object(vec![("revoked".into(), Value::Bool(true))]))
    }

    /// Deprovision an entire workspace — idempotent removal of all workspace-scoped data.
    fn deprovision_workspace(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let org = get_str(args, "organizationId").ok_or("missing organizationId")?;
        let ws = get_str(args, "workspaceId").ok_or("missing workspaceId")?;
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let prefix = format!("ws:{org}:{ws}:");
        let start = prefix.as_bytes().to_vec();
        let end = upper_bound(&start);
        let rows: Vec<Vec<u8>> = db
            .range(&start, &end)
            .map_err(|e| format!("range: {e}"))?
            .into_iter()
            .map(|(k, _)| k)
            .collect();
        let count = rows.len() as i64;
        for key in &rows {
            let _ = db.delete(key);
        }
        let audit_id = now();
        let audit = passvault_core::entities::AuditEntry {
            id: audit_id,
            organization_id: org.clone(),
            workspace_id: ws.clone(),
            actor: "system".to_string(),
            action: "deprovision".to_string(),
            target: "workspace".to_string(),
            purpose: "deprovisioning".to_string(),
            result: "allowed".to_string(),
            timestamp: now(),
        };
        db.insert(
            format!("deprovision_audit:{org}:{ws}:{audit_id}").as_bytes(),
            audit.to_value().to_json().as_bytes(),
        )
        .ok();
        db.commit().map_err(|e| format!("commit: {e}"))?;
        Ok(Value::Object(vec![
            ("removedKeys".into(), Value::Int(count)),
            ("deprovisioned".into(), Value::Bool(true)),
        ]))
    }

    // ── Audit tools ────────────────────────────────────────────────────

    fn list_audit_entries(&self, args: &[(String, Value)]) -> Result<Value, String> {
        let org = get_str(args, "organizationId").ok_or("missing organizationId")?;
        let ws = get_str(args, "workspaceId").ok_or("missing workspaceId")?;
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let rows = self.ws_scan_prefix(&mut db, &org, &ws, "audit")?;
        let mut entries: Vec<Value> = rows
            .into_iter()
            .filter_map(|(_, val)| {
                let v = akurai_json::parse(&String::from_utf8_lossy(&val)).ok()?;
                passvault_core::entities::AuditEntry::from_value(&v)
                    .ok()
                    .map(|a| a.to_value())
            })
            .collect();
        // Also check deprovision audit entries at the global key
        let depro_prefix = format!("deprovision_audit:{org}:{ws}:");
        let depro_start = depro_prefix.as_bytes().to_vec();
        let depro_end = upper_bound(&depro_start);
        if let Ok(depro_rows) = db.range(&depro_start, &depro_end) {
            for (_, val) in depro_rows {
                if let Ok(v) = akurai_json::parse(&String::from_utf8_lossy(&val)) {
                    if let Ok(a) = passvault_core::entities::AuditEntry::from_value(&v) {
                        entries.push(a.to_value());
                    }
                }
            }
        }
        entries.sort_by(|a, b| {
            let ats = a.get("timestamp").and_then(|v| v.as_i64()).unwrap_or(0);
            let bts = b.get("timestamp").and_then(|v| v.as_i64()).unwrap_or(0);
            bts.cmp(&ats)
        });
        Ok(Value::Array(entries))
    }

    fn get_ws_secret_raw(
        &self,
        org: &str,
        ws: &str,
        id: i64,
    ) -> Result<passvault_core::entities::WorkspaceSecret, String> {
        let key = self.ws_key(org, ws, &format!("secret:{id}"));
        let mut db = self.db.lock().map_err(|e| format!("lock: {e}"))?;
        let bytes = db
            .get(key.as_bytes())
            .map_err(|e| format!("storage: {e}"))?
            .ok_or("secret not found")?;
        let decrypted = self
            .crypto
            .decrypt(&bytes)
            .map_err(|e| format!("decrypt: {e}"))?;
        let v = akurai_json::parse(&String::from_utf8_lossy(&decrypted))
            .map_err(|e| format!("json: {e}"))?;
        passvault_core::entities::WorkspaceSecret::from_value(&v).map_err(|e| format!("parse: {e}"))
    }
}
type KeyValuePairs = Vec<(Vec<u8>, Vec<u8>)>;
fn get_arg<'a>(args: &'a [(String, Value)], name: &str) -> Option<&'a Value> {
    args.iter().find(|(k, _)| k == name).map(|(_, v)| v)
}

fn get_str(args: &[(String, Value)], name: &str) -> Option<String> {
    get_arg(args, name).and_then(|v| v.as_str().map(String::from))
}

fn get_i64(args: &[(String, Value)], name: &str) -> Option<i64> {
    get_arg(args, name).and_then(|v| v.as_i64())
}

fn get_bool(args: &[(String, Value)], name: &str) -> Option<bool> {
    get_arg(args, name).and_then(|v| v.as_bool())
}

fn next_counter(db: &mut BTree, key: &str) -> i64 {
    let next = db
        .get(key.as_bytes())
        .ok()
        .flatten()
        .and_then(|v| std::str::from_utf8(&v).ok()?.parse::<i64>().ok())
        .unwrap_or(0)
        + 1;
    let _ = db.insert(key.as_bytes(), next.to_string().as_bytes());
    next
}