Add security hardening, Ghidra, recreate skill, checkpoints, shells, web search and themes

- Sandbox (bubblewrap) for non-admin shell/git, MCP per-user rights, hard budgets and quotas,
  login rate limiting, audit log and backups
- Ghidra headless decompilation (ARM64 natives built from source), binary_info tool,
  clean room / deep workflow per session
- Recreate skill and commands for analysing and re-building old software
- File checkpoints with rewind/restore, background shells (bash_output, kill_shell)
- web_search tool (DuckDuckGo, SearXNG, Brave, Tavily)
- In-app dialogs and toasts instead of browser dialogs
- 15 base themes (trilium-themes palettes plus Nord, VS Code, GitHub, Catppuccin, One,
  Dracula, Rose Pine, Everforest, Kanagawa) with light/dark/auto switcher and license notes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-08 19:12:30 +01:00
1 parent fab9add5d5
commit 5b569b6835
108 files changed
+6785 -279

No files matched your search

+52
View File
@@ -21,6 +21,38 @@ pub struct Config {
pub craft: CraftConfig,
pub auth: AuthConfig,
pub images: ImagesConfig,
pub sandbox: SandboxConfig,
pub search: SearchConfig,
}
/// Web search for the web_search tool.
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(default)]
pub struct SearchConfig {
/// "duckduckgo" (no key), "searxng", "brave" or "tavily".
pub provider: String,
/// SearXNG base URL.
pub url: String,
pub api_key: String,
}
impl Default for SearchConfig {
fn default() -> Self {
Self { provider: "duckduckgo".into(), url: String::new(), api_key: String::new() }
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(default)]
pub struct SandboxConfig {
/// "auto": sandbox shell/git of non-admin users, "always": everybody, "off": nobody.
pub mode: String,
}
impl Default for SandboxConfig {
fn default() -> Self {
Self { mode: "auto".into() }
}
}
/// Image generation via fal.ai.
@@ -152,6 +184,8 @@ pub struct ServerConfig {
pub monthly_budget: f64,
/// Warn when this share of a budget is used (0.0–1.0).
pub budget_warning: f64,
/// "warn" (default) or "block": refuse new model requests once a budget is used up.
pub budget_action: String,
}
impl Default for ServerConfig {
@@ -168,6 +202,7 @@ impl Default for ServerConfig {
mcp_direct_limit: 15,
monthly_budget: 0.0,
budget_warning: 0.8,
budget_action: "warn".into(),
}
}
}
@@ -292,6 +327,20 @@ pub struct McpConfig {
/// "auto" (default), "always" or "on_demand": how the server's tools reach the model.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub load: Option<String>,
/// Who may use this server besides admins: usernames or "*" for everybody.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub users: Vec<String>,
}
impl Config {
/// Whether a session owner may use an MCP server (no owner = legacy/admin).
pub fn mcp_allowed(&self, server: &str, user: Option<&crate::store::User>) -> bool {
match user {
None => true,
Some(u) if u.is_admin() => true,
Some(u) => self.mcp.get(server).is_some_and(|m| m.users.iter().any(|x| x == "*" || x.eq_ignore_ascii_case(&u.username))),
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
@@ -533,6 +582,9 @@ pub fn redacted_json(cfg: &Config) -> serde_json::Value {
if let Some(t) = v.pointer_mut("/images/admin_key") {
mask(t);
}
if let Some(t) = v.pointer_mut("/search/api_key") {
mask(t);
}
v
}