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

+35
View File
@@ -221,3 +221,38 @@ mod tests {
assert_eq!(none["warning"], false);
}
}
/// Refuse a request when a budget or the owner's quota is used up.
/// `provider` is the provider name ("fal" for images).
pub fn check(app: &App, provider: &str, owner_id: Option<&str>) -> Result<()> {
let cfg = app.config();
let from = month_start();
if let Some(user) = owner_id.and_then(|id| app.store.user(id).ok().flatten()) {
if let Some(q) = user.monthly_quota.filter(|q| *q > 0.0) {
let spent = app.store.user_cost_since(&user.id, from)?;
if spent >= q {
bail!(
"monthly quota of {q:.2} {cur} for user {} is used up ({spent:.2} {cur} spent) – ask an admin to raise it",
user.username,
cur = cfg.server.currency
);
}
}
}
if cfg.server.budget_action != "block" {
return Ok(());
}
let spent = app.store.cost_by_provider(from)?;
let total: f64 = spent.values().sum();
if cfg.server.monthly_budget > 0.0 && total >= cfg.server.monthly_budget {
bail!("the monthly budget of {:.2} {} is used up", cfg.server.monthly_budget, cfg.server.currency);
}
let limit = if provider == "fal" { cfg.images.monthly_budget } else { cfg.providers.get(provider).and_then(|p| p.monthly_budget) };
if let Some(b) = limit.filter(|b| *b > 0.0) {
let s = spent.get(provider).copied().unwrap_or(0.0);
if s >= b {
bail!("the monthly budget of {b:.2} {} for {provider} is used up", cfg.server.currency);
}
}
Ok(())
}