- Database v8 migration: tags, pinned, avg_startup_ms columns - Security scanning with CVE matching and batch scan - Bundled library extraction and vulnerability reports - Desktop notification system for security alerts - Backup/restore system for AppImage configurations - i18n framework with gettext support - Runtime analysis and Wayland compatibility detection - AppStream metadata and Flatpak-style build support - File watcher module for live directory monitoring - Preferences panel with GSettings integration - CLI interface for headless operation - Detail view: tabbed layout with ViewSwitcher in title bar, health score, sandbox controls, changelog links - Library view: sort dropdown, context menu enhancements - Dashboard: system status, disk usage, launch history - Security report page with scan and export - Packaging: meson build, PKGBUILD, metainfo
1136 lines
36 KiB
Markdown
1136 lines
36 KiB
Markdown
# Phase 5 - Ecosystem
|
|
|
|
**Goal: Become the standard AppImage management tool.**
|
|
|
|
Phase 5 is the post-release expansion phase. Each feature is designed to be independently shippable - no feature blocks another. They're ordered by impact and dependency, not strict sequence.
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
| # | Feature | Scope | New Modules | DB Tables | Priority |
|
|
|---|---------|-------|-------------|-----------|----------|
|
|
| 1 | Config backup/restore across versions | Medium | backup.rs | 2 | High |
|
|
| 2 | Exportable security reports | Medium | report.rs | 1 | High |
|
|
| 3 | CVE push notifications | Medium | notification.rs | 1 | High |
|
|
| 4 | AppImage developer tools (validate) | Small | - (extends cli.rs) | 0 | High |
|
|
| 5 | ARM64/aarch64 support | Small | - (extends existing) | 0 | Medium |
|
|
| 6 | Wayland runtime detection (post-launch) | Medium | - (extends wayland.rs) | 1 | Medium |
|
|
| 7 | Community sandbox profile sharing | Large | sandbox/profiles.rs | 2 | Medium |
|
|
| 8 | AppImage catalog integration | Large | catalog.rs | 2 | Medium |
|
|
| 9 | Batch re-packaging (FUSE runtime updates) | Large | repackager.rs | 1 | Low |
|
|
| 10 | Multi-user / system-wide mode | Medium | - (extends existing) | 0 | Low |
|
|
| 11 | GNOME Software / KDE Discover plugin | Large | appstream plugin | 0 | Low |
|
|
| 12 | Qt6 frontend | Very Large | ui_qt6/ + D-Bus daemon | 0 | Low |
|
|
|
|
---
|
|
|
|
## Feature 1: Config Backup/Restore Across AppImage Versions
|
|
|
|
### Why it matters
|
|
|
|
When an AppImage is updated, its config/data files (in ~/.config/appname, ~/.local/share/appname, etc.) may be incompatible with the new version. Users currently have no way to rollback their settings. Driftwood already discovers these paths via footprint.rs - we just need to snapshot them before updating.
|
|
|
|
### Design
|
|
|
|
**Trigger points:**
|
|
- Automatic: before every update (if preference enabled)
|
|
- Manual: "Backup Config" button in detail view
|
|
- CLI: `driftwood backup <path>` and `driftwood restore <path>`
|
|
|
|
**Backup format:**
|
|
- tar.xz archive in `~/.local/share/driftwood/backups/`
|
|
- Naming: `{app_id}-{version}-{timestamp}.tar.xz`
|
|
- Manifest JSON embedded as first file in archive
|
|
- Includes: all paths from footprint discovery with confidence >= Medium
|
|
|
|
**Restore flow:**
|
|
1. User picks a backup from the list (sorted by date, grouped by app)
|
|
2. Preview dialog shows what will be restored (config vs data vs cache)
|
|
3. User can selectively exclude cache files
|
|
4. Extract to original paths, preserving permissions
|
|
5. Record restore event in database
|
|
|
|
### New module: src/core/backup.rs (~350 lines)
|
|
|
|
```rust
|
|
pub struct BackupManifest {
|
|
pub app_name: String,
|
|
pub app_version: String,
|
|
pub created_at: String,
|
|
pub paths: Vec<BackupEntry>,
|
|
pub total_size: u64,
|
|
}
|
|
|
|
pub struct BackupEntry {
|
|
pub original_path: String,
|
|
pub path_type: String, // config, data, cache
|
|
pub relative_path: String, // path inside archive
|
|
pub size_bytes: u64,
|
|
}
|
|
|
|
pub fn create_backup(db: &Database, appimage_id: i64) -> Result<PathBuf>
|
|
pub fn restore_backup(backup_path: &Path, selective: &[String]) -> Result<()>
|
|
pub fn list_backups(app_id: &str) -> Result<Vec<BackupManifest>>
|
|
pub fn delete_backup(backup_path: &Path) -> Result<()>
|
|
pub fn auto_cleanup_old_backups(retention_days: u32) -> Result<u32>
|
|
```
|
|
|
|
### Database changes (migration v5)
|
|
|
|
```sql
|
|
CREATE TABLE config_backups (
|
|
id INTEGER PRIMARY KEY,
|
|
appimage_id INTEGER REFERENCES appimages(id) ON DELETE CASCADE,
|
|
app_version TEXT,
|
|
archive_path TEXT NOT NULL,
|
|
archive_size INTEGER,
|
|
checksum TEXT,
|
|
created_at TEXT NOT NULL,
|
|
path_count INTEGER,
|
|
restored_count INTEGER DEFAULT 0,
|
|
last_restored_at TEXT
|
|
);
|
|
|
|
CREATE TABLE backup_entries (
|
|
id INTEGER PRIMARY KEY,
|
|
backup_id INTEGER REFERENCES config_backups(id) ON DELETE CASCADE,
|
|
original_path TEXT NOT NULL,
|
|
path_type TEXT NOT NULL,
|
|
size_bytes INTEGER
|
|
);
|
|
```
|
|
|
|
### GSettings
|
|
|
|
```xml
|
|
<key name="auto-backup-before-update" type="b">
|
|
<default>true</default>
|
|
</key>
|
|
<key name="backup-retention-days" type="i">
|
|
<default>30</default>
|
|
</key>
|
|
```
|
|
|
|
### UI changes
|
|
|
|
- Detail view: new "Config Backup" section between Usage and Security
|
|
- "Create Backup" button
|
|
- List of existing backups with date/size
|
|
- "Restore" and "Delete" buttons per backup
|
|
- Preferences: Behavior page gets backup retention setting
|
|
- Update dialog: after successful update, show "Config backed up" confirmation
|
|
|
|
### CLI
|
|
|
|
```
|
|
driftwood backup ~/Apps/MyApp.AppImage
|
|
driftwood restore ~/Apps/MyApp.AppImage --list
|
|
driftwood restore ~/Apps/MyApp.AppImage --backup-id 3
|
|
driftwood backup --cleanup # remove backups older than retention period
|
|
```
|
|
|
|
### Integration with updater
|
|
|
|
In `update_dialog.rs` and `updater.rs`, before `perform_update()`:
|
|
1. Check `auto-backup-before-update` setting
|
|
2. If enabled, call `backup::create_backup()`
|
|
3. Store backup_id in `update_history` row
|
|
4. On update failure, offer automatic restore
|
|
|
|
### Dependencies
|
|
|
|
- `tar` crate (or shell out to `tar`) for archive creation
|
|
- `xz2` crate for compression (or shell out to `xz`)
|
|
- Existing `footprint.rs` for path discovery (no changes needed there)
|
|
|
|
---
|
|
|
|
## Feature 2: Exportable Security Reports
|
|
|
|
### Why it matters
|
|
|
|
Organizations deploying AppImages need audit trails. A sysadmin managing 50 desktops with AppImages needs to generate a report showing which apps have known CVEs, export it as JSON/HTML, and attach it to a compliance ticket.
|
|
|
|
### Design
|
|
|
|
**Export formats:**
|
|
- **JSON** - machine-readable, for integration with SIEM/ticketing systems
|
|
- **HTML** - human-readable, styled single-file report for email/printing
|
|
- **CSV** - spreadsheet-friendly flat format
|
|
|
|
**Report types:**
|
|
- Single app: all CVEs for one AppImage
|
|
- Full audit: all AppImages with their CVE status
|
|
- Summary: counts only, no CVE details
|
|
|
|
### New module: src/core/report.rs (~250 lines)
|
|
|
|
```rust
|
|
pub enum ReportFormat { Json, Html, Csv }
|
|
pub enum ReportScope { SingleApp(i64), AllApps, Summary }
|
|
|
|
pub struct SecurityReport {
|
|
pub generated_at: String,
|
|
pub driftwood_version: String,
|
|
pub scope: String,
|
|
pub apps: Vec<AppSecurityEntry>,
|
|
pub totals: CveSummary,
|
|
}
|
|
|
|
pub struct AppSecurityEntry {
|
|
pub name: String,
|
|
pub version: Option<String>,
|
|
pub path: String,
|
|
pub libraries_scanned: usize,
|
|
pub cve_summary: CveSummary,
|
|
pub findings: Vec<CveFinding>,
|
|
}
|
|
|
|
pub struct CveFinding {
|
|
pub cve_id: String,
|
|
pub severity: String,
|
|
pub cvss_score: Option<f64>,
|
|
pub summary: String,
|
|
pub library_name: String,
|
|
pub library_version: String,
|
|
pub fixed_version: Option<String>,
|
|
}
|
|
|
|
pub fn generate_report(db: &Database, scope: ReportScope, format: ReportFormat) -> Result<Vec<u8>>
|
|
pub fn generate_json_report(report: &SecurityReport) -> Result<String>
|
|
pub fn generate_html_report(report: &SecurityReport) -> Result<String>
|
|
pub fn generate_csv_report(report: &SecurityReport) -> Result<String>
|
|
```
|
|
|
|
### HTML report template
|
|
|
|
Embedded in the binary as a const string. Clean, printable layout:
|
|
- Header with generation timestamp and Driftwood version
|
|
- Summary table: total apps, total CVEs by severity
|
|
- Per-app sections with severity badges
|
|
- Library version comparison (bundled vs system)
|
|
- Footer with disclaimer about heuristic detection
|
|
|
|
### Database changes
|
|
|
|
```sql
|
|
CREATE TABLE exported_reports (
|
|
id INTEGER PRIMARY KEY,
|
|
scope TEXT NOT NULL,
|
|
format TEXT NOT NULL,
|
|
file_path TEXT,
|
|
generated_at TEXT NOT NULL,
|
|
app_count INTEGER,
|
|
cve_count INTEGER
|
|
);
|
|
```
|
|
|
|
### UI changes
|
|
|
|
- Dashboard: "Export Security Report" button in the security summary card
|
|
- Security report view: "Export" button in header bar with format picker
|
|
- Detail view security section: "Export" link
|
|
|
|
### CLI
|
|
|
|
```
|
|
driftwood security --export json --output report.json
|
|
driftwood security --export html --output report.html
|
|
driftwood security ~/Apps/MyApp.AppImage --export csv
|
|
```
|
|
|
|
### Dependencies
|
|
|
|
- No new crates needed. HTML templating via format!() strings. JSON via serde_json (already used). CSV via manual formatting.
|
|
|
|
---
|
|
|
|
## Feature 3: CVE Push Notifications
|
|
|
|
### Why it matters
|
|
|
|
Users shouldn't have to manually check for new CVEs. When a critical vulnerability is published in OpenSSL or libcurl and affects a bundled library in their AppImages, they should get a desktop notification immediately.
|
|
|
|
### Design
|
|
|
|
**Notification flow:**
|
|
1. Background scan runs on schedule (daily by default)
|
|
2. Scan compares current CVE matches against previously known ones
|
|
3. New CRITICAL or HIGH findings trigger a desktop notification
|
|
4. Notification includes: app name, severity, affected library
|
|
5. Clicking notification opens Driftwood's security report view
|
|
|
|
**Notification deduplication:**
|
|
- Track which CVEs have been notified per app
|
|
- Don't re-notify for the same CVE unless severity changes
|
|
- Batch multiple findings into one notification per app
|
|
|
|
### New module: src/core/notification.rs (~200 lines)
|
|
|
|
```rust
|
|
pub struct CveNotification {
|
|
pub app_name: String,
|
|
pub severity: String,
|
|
pub cve_count: usize,
|
|
pub affected_libraries: Vec<String>,
|
|
}
|
|
|
|
pub fn send_desktop_notification(notif: &CveNotification) -> Result<()>
|
|
pub fn check_and_notify(db: &Database) -> Result<Vec<CveNotification>>
|
|
pub fn has_been_notified(db: &Database, appimage_id: i64, cve_id: &str) -> Result<bool>
|
|
pub fn mark_notified(db: &Database, appimage_id: i64, cve_id: &str) -> Result<()>
|
|
```
|
|
|
|
**Desktop notification mechanism:**
|
|
- Use `notify-rust` crate (wraps org.freedesktop.Notifications D-Bus)
|
|
- Works on both GNOME and KDE without changes
|
|
- Supports actions ("View Report", "Dismiss")
|
|
- Supports urgency levels (critical = persistent notification)
|
|
|
|
### Database changes
|
|
|
|
```sql
|
|
CREATE TABLE cve_notifications (
|
|
id INTEGER PRIMARY KEY,
|
|
appimage_id INTEGER REFERENCES appimages(id) ON DELETE CASCADE,
|
|
cve_id TEXT NOT NULL,
|
|
severity TEXT NOT NULL,
|
|
notified_at TEXT NOT NULL,
|
|
user_action TEXT, -- 'viewed', 'dismissed', NULL
|
|
acted_at TEXT,
|
|
UNIQUE(appimage_id, cve_id)
|
|
);
|
|
```
|
|
|
|
### GSettings
|
|
|
|
```xml
|
|
<key name="security-notifications" type="b">
|
|
<default>true</default>
|
|
</key>
|
|
<key name="security-notification-threshold" type="s">
|
|
<default>'high'</default>
|
|
<summary>Minimum severity for notifications</summary>
|
|
<description>critical, high, medium, or low</description>
|
|
</key>
|
|
<key name="security-scan-schedule" type="s">
|
|
<default>'daily'</default>
|
|
<summary>Background security scan frequency</summary>
|
|
<description>daily, weekly, or manual</description>
|
|
</key>
|
|
```
|
|
|
|
### UI changes
|
|
|
|
- Preferences: Security page gets notification toggle, threshold picker, schedule picker
|
|
- Dashboard: notification history section showing recent alerts
|
|
|
|
### CLI
|
|
|
|
```
|
|
driftwood security --notify # run scan and send notifications for new findings
|
|
```
|
|
|
|
### Dependencies
|
|
|
|
- `notify-rust` crate (~lightweight, wraps D-Bus notifications)
|
|
|
|
### Integration with existing scan
|
|
|
|
In `window.rs` auto-scan-on-startup flow, after scanning completes:
|
|
1. If `security-notifications` enabled, run `check_and_notify()`
|
|
2. New CVEs found -> send notifications
|
|
3. Store notification records for deduplication
|
|
|
|
---
|
|
|
|
## Feature 4: AppImage Developer Tools (Validate)
|
|
|
|
### Why it matters
|
|
|
|
AppImage developers need a way to validate their builds before distribution. Currently they use `appimaged` or manual inspection. Driftwood can leverage its existing analysis engine to provide a comprehensive validation report.
|
|
|
|
### Design
|
|
|
|
New `dev` subcommand group in the CLI:
|
|
|
|
```
|
|
driftwood dev validate <path> # comprehensive validation
|
|
driftwood dev check-libs <path> # library health check
|
|
driftwood dev wayland-check <path> # Wayland compatibility analysis
|
|
driftwood dev export-metadata <path> --format json # dump extracted metadata
|
|
```
|
|
|
|
### Implementation (extends cli.rs, ~200 lines)
|
|
|
|
**`driftwood dev validate <path>`** runs all checks:
|
|
|
|
1. **ELF validation** - valid header, correct magic bytes, architecture
|
|
2. **AppImage type detection** - Type 1 or Type 2, magic bytes present
|
|
3. **SquashFS integrity** - can we extract/mount the payload?
|
|
4. **Desktop entry** - present, valid, has required fields (Name, Exec, Icon)
|
|
5. **Icon** - present, correct size (at least 256x256), valid format
|
|
6. **Update info** - present in .upd_info section, valid format, reachable URL
|
|
7. **Executable bit** - set correctly
|
|
8. **Library audit** - bundled libs with known CVEs, deprecated libraries
|
|
9. **Wayland readiness** - toolkit detection, missing platform plugins
|
|
10. **FUSE compatibility** - runtime type, extract-and-run support
|
|
11. **Architecture** - x86_64 vs aarch64 vs armv7, matches system
|
|
|
|
**Output format:**
|
|
```
|
|
Validation Report - MyApp-1.0-x86_64.AppImage
|
|
==============================================
|
|
[PASS] Valid ELF header (Type 2 AppImage)
|
|
[PASS] SquashFS payload intact (47 MB)
|
|
[PASS] Desktop entry: myapp.desktop
|
|
[WARN] Icon is 128x128 - recommend 256x256 or SVG
|
|
[PASS] Update info: gh-releases-zsync|user|repo|latest|*.zsync
|
|
[FAIL] libssl.so.1.1 has 3 CRITICAL CVEs - upgrade to OpenSSL 3.x
|
|
[PASS] Wayland: GTK4 detected - native Wayland support
|
|
[PASS] Executable bit set
|
|
[INFO] Architecture: x86_64
|
|
|
|
Score: 7/9 checks passed, 1 warning, 1 failure
|
|
```
|
|
|
|
**`driftwood dev check-libs <path>`** output:
|
|
```
|
|
Bundled Library Health Check
|
|
============================
|
|
Library Version System CVEs Status
|
|
libssl.so.1.1 1.1.1k 3.0.13 3 C OUTDATED - 3 major versions behind
|
|
libcurl.so.4 8.1.0 8.5.0 0 Minor update available
|
|
zlib.so.1 1.3.1 1.3.1 0 Current
|
|
libpng16.so.16 1.6.43 1.6.43 0 Current
|
|
```
|
|
|
|
**`driftwood dev wayland-check <path>`** output:
|
|
```
|
|
Wayland Compatibility Analysis
|
|
==============================
|
|
Detected toolkit: GTK4 (via libgtk-4.so.1)
|
|
Wayland status: Native
|
|
Wayland libraries: libwayland-client.so.0 present
|
|
Platform plugins: N/A (GTK4 uses native backend)
|
|
Recommended env vars: none needed
|
|
```
|
|
|
|
### No new modules needed
|
|
|
|
All functionality exists in inspector.rs, security.rs, wayland.rs, fuse.rs, and discovery.rs. The `dev` commands are thin wrappers that call existing functions and format the output.
|
|
|
|
### No database changes
|
|
|
|
Developer tools are stateless - they analyze a file and print results.
|
|
|
|
---
|
|
|
|
## Feature 5: ARM64/aarch64 Support
|
|
|
|
### Why it matters
|
|
|
|
ARM64 Linux desktops are growing (Raspberry Pi 5, Apple Silicon via Asahi Linux, Qualcomm Snapdragon laptops). AppImages for aarch64 exist and Driftwood should handle them correctly.
|
|
|
|
### Design
|
|
|
|
**What needs to change:**
|
|
|
|
1. **Architecture detection** - already exists in inspector.rs (`detect_architecture()`). Just needs to handle `aarch64` and `armv7l` in addition to `x86_64`.
|
|
|
|
2. **Architecture mismatch warning** - when an AppImage's architecture doesn't match the host system, show a warning badge and explain that it can't run natively.
|
|
|
|
3. **QEMU/binfmt_misc detection** - check if the system can run foreign-arch binaries via binfmt_misc:
|
|
```rust
|
|
pub fn can_run_foreign_arch(arch: &str) -> bool {
|
|
// Check /proc/sys/fs/binfmt_misc/ for registered interpreters
|
|
// e.g., /proc/sys/fs/binfmt_misc/qemu-aarch64
|
|
Path::new(&format!("/proc/sys/fs/binfmt_misc/qemu-{}", arch)).exists()
|
|
}
|
|
```
|
|
|
|
4. **Library view badge** - show architecture badge on cards when the AppImage is for a different arch than the host.
|
|
|
|
5. **Cross-compilation of Driftwood itself** - ensure Cargo.toml doesn't use x86_64-specific dependencies. Current deps (gtk4-rs, rusqlite bundled, sha2, ureq) all support aarch64.
|
|
|
|
### Implementation (~50 lines of changes across existing files)
|
|
|
|
- `inspector.rs`: extend `detect_architecture()` to return `aarch64`, `armv7l`, `i686` in addition to `x86_64`
|
|
- `launcher.rs`: before launching, check arch match. If mismatch, check binfmt_misc. If neither, return `LaunchResult::Failed` with helpful message.
|
|
- `app_card.rs`: show architecture badge when arch != host
|
|
- `cli.rs status`: show host architecture
|
|
|
|
### No new modules, no database changes
|
|
|
|
The `architecture` column already exists in the `appimages` table.
|
|
|
|
---
|
|
|
|
## Feature 6: Wayland Runtime Detection (Post-Launch)
|
|
|
|
### Why it matters
|
|
|
|
Static analysis (inspecting bundled .so files) catches most cases but can't tell you what actually happens when the app runs. Some apps bundle both X11 and Wayland backends and pick at runtime based on environment. Post-launch analysis confirms the actual behavior.
|
|
|
|
### Design
|
|
|
|
**How it works:**
|
|
1. Launch the AppImage (via launcher.rs)
|
|
2. Wait 2-3 seconds for the app to initialize
|
|
3. Inspect the running process:
|
|
- Check /proc/PID/fd for Wayland socket connections
|
|
- Check /proc/PID/environ for GDK_BACKEND, QT_QPA_PLATFORM
|
|
- Query GNOME Shell Introspect D-Bus interface for window info
|
|
- Check if process has X11 connections via /proc/PID/fd -> .X11-unix
|
|
4. Store the result as `runtime_wayland_status` alongside the existing static analysis
|
|
|
|
**Implementation - extends wayland.rs (~150 lines)**
|
|
|
|
```rust
|
|
pub struct RuntimeWaylandAnalysis {
|
|
pub pid: u32,
|
|
pub has_wayland_socket: bool,
|
|
pub has_x11_connection: bool,
|
|
pub detected_backend: Option<String>, // "wayland", "x11", "both"
|
|
pub env_vars: HashMap<String, String>, // relevant env vars from /proc
|
|
}
|
|
|
|
pub fn analyze_running_process(pid: u32) -> Result<RuntimeWaylandAnalysis>
|
|
pub fn check_wayland_socket(pid: u32) -> bool
|
|
pub fn check_x11_connection(pid: u32) -> bool
|
|
pub fn get_process_env_var(pid: u32, var: &str) -> Option<String>
|
|
```
|
|
|
|
**Integration with launcher.rs:**
|
|
- After `cmd.spawn()` succeeds, optionally spawn a delayed analysis task
|
|
- Store result in database: `runtime_wayland_status` column (new)
|
|
- Only run on first launch or when user requests re-analysis
|
|
|
|
### Database changes
|
|
|
|
```sql
|
|
ALTER TABLE appimages ADD COLUMN runtime_wayland_status TEXT;
|
|
ALTER TABLE appimages ADD COLUMN runtime_wayland_checked TEXT;
|
|
```
|
|
|
|
### GSettings
|
|
|
|
```xml
|
|
<key name="analyze-wayland-on-launch" type="b">
|
|
<default>false</default>
|
|
<summary>Check actual Wayland usage after launching</summary>
|
|
<description>Inspects the running process to confirm whether it uses native Wayland or XWayland</description>
|
|
</key>
|
|
```
|
|
|
|
### UI changes
|
|
|
|
- Detail view runtime section: show both static analysis AND runtime analysis results when available
|
|
- Badge could show "Confirmed native Wayland" vs "Confirmed XWayland" after runtime check
|
|
|
|
---
|
|
|
|
## Feature 7: Community Sandbox Profile Sharing
|
|
|
|
### Why it matters
|
|
|
|
Writing Firejail profiles is tedious. Most users won't do it. But security-conscious users who DO write profiles could share them, creating a crowdsourced library of per-app sandbox configs. Think of it like Firefox add-ons but for Firejail profiles.
|
|
|
|
### Design
|
|
|
|
**Architecture:**
|
|
- Local profile storage in `~/.config/driftwood/sandbox/` (one .profile file per app)
|
|
- Remote registry at a simple HTTPS API (could be a static GitHub repo initially)
|
|
- Profiles are plain-text Firejail .profile files with Driftwood metadata header
|
|
|
|
**Profile format:**
|
|
```ini
|
|
# Driftwood Sandbox Profile
|
|
# App: Firefox
|
|
# Version: 1.0
|
|
# Author: username
|
|
# Created: 2026-03-01
|
|
# Description: Restricts Firefox to Documents and Downloads
|
|
|
|
include disable-common.inc
|
|
include disable-devel.inc
|
|
whitelist ${HOME}/Documents
|
|
whitelist ${HOME}/Downloads
|
|
caps.drop all
|
|
netfilter
|
|
nonewprivs
|
|
noroot
|
|
seccomp
|
|
```
|
|
|
|
### New module: src/core/sandbox.rs (~400 lines)
|
|
|
|
```rust
|
|
pub struct SandboxProfile {
|
|
pub id: Option<i64>,
|
|
pub app_name: String,
|
|
pub profile_version: String,
|
|
pub author: String,
|
|
pub description: String,
|
|
pub content: String,
|
|
pub created_at: String,
|
|
pub downloads: u32,
|
|
pub source: ProfileSource, // Local, Community, Firejail-Default
|
|
}
|
|
|
|
pub enum ProfileSource {
|
|
Local,
|
|
Community { registry_id: String },
|
|
FirejailDefault,
|
|
}
|
|
|
|
// Local management
|
|
pub fn save_profile(profile: &SandboxProfile) -> Result<PathBuf>
|
|
pub fn load_profile(app_name: &str) -> Result<Option<SandboxProfile>>
|
|
pub fn delete_profile(app_name: &str) -> Result<()>
|
|
pub fn list_local_profiles() -> Result<Vec<SandboxProfile>>
|
|
|
|
// Community registry
|
|
pub fn search_community_profiles(app_name: &str) -> Result<Vec<SandboxProfile>>
|
|
pub fn download_community_profile(registry_id: &str) -> Result<SandboxProfile>
|
|
pub fn submit_profile(profile: &SandboxProfile) -> Result<String>
|
|
|
|
// Default profile generation
|
|
pub fn generate_default_profile(app_name: &str, permissions: &[Permission]) -> String
|
|
```
|
|
|
|
### Community registry - simple approach
|
|
|
|
Start with a GitHub repository as the "registry":
|
|
- `profiles/` directory with one JSON file per app
|
|
- CI validates profile syntax
|
|
- Users submit profiles via PR
|
|
- Driftwood fetches the raw JSON index file
|
|
|
|
This avoids building a web service initially. The index file:
|
|
```json
|
|
{
|
|
"profiles": [
|
|
{
|
|
"id": "firefox-strict",
|
|
"app_name": "Firefox",
|
|
"author": "contributor1",
|
|
"description": "Strict sandbox for Firefox",
|
|
"url": "https://raw.githubusercontent.com/.../firefox-strict.profile",
|
|
"downloads": 42
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Database changes
|
|
|
|
```sql
|
|
CREATE TABLE sandbox_profiles (
|
|
id INTEGER PRIMARY KEY,
|
|
app_name TEXT NOT NULL,
|
|
profile_version TEXT,
|
|
author TEXT,
|
|
description TEXT,
|
|
content TEXT NOT NULL,
|
|
source TEXT NOT NULL,
|
|
registry_id TEXT,
|
|
created_at TEXT,
|
|
applied_to_appimage_id INTEGER REFERENCES appimages(id)
|
|
);
|
|
|
|
CREATE TABLE sandbox_profile_history (
|
|
id INTEGER PRIMARY KEY,
|
|
profile_id INTEGER REFERENCES sandbox_profiles(id) ON DELETE CASCADE,
|
|
action TEXT NOT NULL, -- 'applied', 'removed', 'updated'
|
|
timestamp TEXT NOT NULL
|
|
);
|
|
```
|
|
|
|
### UI changes
|
|
|
|
- Detail view sandbox section: "Browse Profiles" button next to the Firejail toggle
|
|
- Profile browser dialog: search, preview, apply
|
|
- Profile editor: text area with syntax hints for advanced users
|
|
|
|
### CLI
|
|
|
|
```
|
|
driftwood sandbox list # list local profiles
|
|
driftwood sandbox search firefox # search community profiles
|
|
driftwood sandbox apply firefox-strict # apply a community profile
|
|
driftwood sandbox generate ~/Apps/MyApp.AppImage # generate default profile
|
|
```
|
|
|
|
---
|
|
|
|
## Feature 8: AppImage Catalog Integration
|
|
|
|
### Why it matters
|
|
|
|
Currently users must find AppImages manually (GitHub releases, AppImageHub website, developer sites). A built-in catalog lets users browse, search, and install AppImages from curated sources directly within Driftwood.
|
|
|
|
### Design
|
|
|
|
**Catalog sources:**
|
|
1. **AppImageHub** (appimage.github.io) - the existing community catalog
|
|
- JSON API available
|
|
- ~1500 listed applications
|
|
2. **GitHub Releases** - search GitHub for repos with AppImage releases
|
|
- Uses GitHub API with pagination
|
|
- Filter by `*.AppImage` in release assets
|
|
3. **Custom sources** - user-defined URLs pointing to a JSON index
|
|
|
|
**Catalog flow:**
|
|
1. User opens "Browse Catalog" from hamburger menu
|
|
2. Driftwood fetches/caches the catalog index
|
|
3. User searches/browses by category
|
|
4. User clicks "Install" on an app
|
|
5. Driftwood downloads to ~/Applications, verifies integrity, adds to library
|
|
6. Optional: auto-integrate into desktop menu
|
|
|
|
### New module: src/core/catalog.rs (~400 lines)
|
|
|
|
```rust
|
|
pub struct CatalogSource {
|
|
pub id: Option<i64>,
|
|
pub name: String,
|
|
pub url: String,
|
|
pub source_type: CatalogType,
|
|
pub enabled: bool,
|
|
pub last_synced: Option<String>,
|
|
}
|
|
|
|
pub enum CatalogType {
|
|
AppImageHub,
|
|
GitHubSearch,
|
|
Custom,
|
|
}
|
|
|
|
pub struct CatalogApp {
|
|
pub name: String,
|
|
pub description: Option<String>,
|
|
pub categories: Vec<String>,
|
|
pub latest_version: Option<String>,
|
|
pub download_url: String,
|
|
pub icon_url: Option<String>,
|
|
pub homepage: Option<String>,
|
|
pub file_size: Option<u64>,
|
|
pub architecture: Option<String>,
|
|
}
|
|
|
|
pub fn sync_catalog(source: &CatalogSource) -> Result<Vec<CatalogApp>>
|
|
pub fn search_catalog(db: &Database, query: &str) -> Result<Vec<CatalogApp>>
|
|
pub fn install_from_catalog(app: &CatalogApp, install_dir: &Path) -> Result<PathBuf>
|
|
pub fn fetch_appimage_hub_index() -> Result<Vec<CatalogApp>>
|
|
```
|
|
|
|
### Database changes
|
|
|
|
```sql
|
|
CREATE TABLE catalog_sources (
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
url TEXT NOT NULL UNIQUE,
|
|
source_type TEXT NOT NULL,
|
|
enabled INTEGER DEFAULT 1,
|
|
last_synced TEXT,
|
|
app_count INTEGER DEFAULT 0
|
|
);
|
|
|
|
CREATE TABLE catalog_apps (
|
|
id INTEGER PRIMARY KEY,
|
|
source_id INTEGER REFERENCES catalog_sources(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
description TEXT,
|
|
categories TEXT,
|
|
latest_version TEXT,
|
|
download_url TEXT NOT NULL,
|
|
icon_url TEXT,
|
|
homepage TEXT,
|
|
file_size INTEGER,
|
|
architecture TEXT,
|
|
cached_at TEXT
|
|
);
|
|
```
|
|
|
|
### UI changes
|
|
|
|
- New "Browse Catalog" page accessible from hamburger menu or empty state
|
|
- Catalog browser: AdwNavigationPage with search, category filter, app cards
|
|
- Each catalog app card shows: name, description, size, "Install" button
|
|
- Download progress via AdwToast or inline progress bar
|
|
- Preferences: "Catalog Sources" section to enable/disable sources
|
|
|
|
### CLI
|
|
|
|
```
|
|
driftwood catalog search firefox
|
|
driftwood catalog install firefox --source appimage-hub
|
|
driftwood catalog sync # refresh catalog index
|
|
driftwood catalog list # list available apps
|
|
```
|
|
|
|
### Privacy consideration
|
|
|
|
Catalog fetches send HTTP requests to external servers. Clearly disclose this:
|
|
- Default: AppImageHub enabled, GitHub search disabled (requires API token)
|
|
- Custom sources added manually by user
|
|
- No telemetry or tracking sent from Driftwood
|
|
|
|
---
|
|
|
|
## Feature 9: Batch Re-Packaging (FUSE Runtime Updates)
|
|
|
|
### Why it matters
|
|
|
|
Many AppImages ship with the old fuse2-only runtime. The new type2-runtime (updated Jan 2026) supports fuse2, fuse3, and a static runtime that eliminates FUSE dependency entirely. Batch re-packaging replaces the old runtime binary with the new one, fixing FUSE issues across the user's entire library in one operation.
|
|
|
|
### Design
|
|
|
|
**How AppImage runtime replacement works:**
|
|
1. An AppImage is: `[ELF runtime binary] + [SquashFS payload]`
|
|
2. The runtime binary is the first N bytes (typically 180-240 KB)
|
|
3. The SquashFS payload starts at a known offset stored in the ELF
|
|
4. To replace the runtime: extract the offset, concatenate new runtime + old payload
|
|
5. Copy permissions, verify the result launches correctly
|
|
|
|
**Safety:**
|
|
- Always create a backup of the original file before modifying
|
|
- Verify the new file is a valid AppImage before replacing
|
|
- Rollback if verification fails
|
|
- Show detailed preview of what will change
|
|
|
|
### New module: src/core/repackager.rs (~350 lines)
|
|
|
|
```rust
|
|
pub struct RuntimeInfo {
|
|
pub runtime_size: u64,
|
|
pub payload_offset: u64,
|
|
pub runtime_type: String, // "old-fuse2", "new-multi", "static"
|
|
pub runtime_version: Option<String>,
|
|
}
|
|
|
|
pub struct RepackageResult {
|
|
pub original_path: PathBuf,
|
|
pub backup_path: PathBuf,
|
|
pub new_runtime: String,
|
|
pub old_size: u64,
|
|
pub new_size: u64,
|
|
pub fuse_status_before: String,
|
|
pub fuse_status_after: String,
|
|
}
|
|
|
|
pub fn detect_runtime(appimage_path: &Path) -> Result<RuntimeInfo>
|
|
pub fn extract_payload_offset(appimage_path: &Path) -> Result<u64>
|
|
pub fn replace_runtime(appimage_path: &Path, new_runtime: &Path, keep_backup: bool) -> Result<RepackageResult>
|
|
pub fn batch_replace_runtimes(db: &Database, new_runtime: &Path) -> Result<Vec<RepackageResult>>
|
|
pub fn download_latest_runtime() -> Result<PathBuf>
|
|
pub fn verify_appimage_integrity(appimage_path: &Path) -> Result<bool>
|
|
```
|
|
|
|
### UI changes
|
|
|
|
- Dashboard: "Update Runtimes" card showing how many AppImages use the old runtime
|
|
- Batch dialog: list of AppImages to update with checkboxes, preview of changes
|
|
- Per-app detail view: runtime info in the FUSE section, "Update Runtime" button
|
|
|
|
### CLI
|
|
|
|
```
|
|
driftwood dev repackage ~/Apps/MyApp.AppImage --runtime latest
|
|
driftwood dev repackage --all --dry-run # show what would change
|
|
driftwood dev repackage --all # batch update all
|
|
```
|
|
|
|
### Database changes
|
|
|
|
```sql
|
|
CREATE TABLE runtime_updates (
|
|
id INTEGER PRIMARY KEY,
|
|
appimage_id INTEGER REFERENCES appimages(id) ON DELETE CASCADE,
|
|
old_runtime TEXT,
|
|
new_runtime TEXT,
|
|
backup_path TEXT,
|
|
updated_at TEXT,
|
|
success INTEGER
|
|
);
|
|
```
|
|
|
|
### Risk mitigation
|
|
|
|
- Large red warning: "This modifies your AppImage files. Backups are created automatically."
|
|
- Dry-run mode shows what would change without modifying anything
|
|
- Each modified AppImage is verified before the backup is deleted
|
|
- If verification fails, automatic rollback
|
|
|
|
---
|
|
|
|
## Feature 10: Multi-User / System-Wide Mode
|
|
|
|
### Why it matters
|
|
|
|
In shared workstations, labs, or family computers, AppImages in /opt or /usr/local/share should be manageable by an admin and usable by all users. Currently Driftwood is single-user only.
|
|
|
|
### Design
|
|
|
|
**Two modes:**
|
|
1. **User mode** (default) - current behavior, per-user database and config
|
|
2. **System mode** - system-wide AppImage directory, shared database, per-user launch tracking
|
|
|
|
**System mode details:**
|
|
- System AppImage directory: `/opt/appimages/` or configurable
|
|
- System database: `/var/lib/driftwood/driftwood.db` (readable by all, writable by admin)
|
|
- Per-user overlay: launch history and preferences remain per-user
|
|
- Integration: system .desktop files go to `/usr/local/share/applications/`
|
|
- CLI: `driftwood --system scan` / `driftwood --system list`
|
|
|
|
### Implementation (~200 lines across existing modules)
|
|
|
|
**Changes to database.rs:**
|
|
```rust
|
|
pub fn open_system() -> Result<Database> {
|
|
let path = PathBuf::from("/var/lib/driftwood/driftwood.db");
|
|
Self::open_at(&path)
|
|
}
|
|
|
|
pub fn open_at(path: &Path) -> Result<Database> {
|
|
// Same as open() but with explicit path
|
|
}
|
|
```
|
|
|
|
**Changes to launcher.rs:**
|
|
- Add `launched_by` field to launch events
|
|
- Track which user launched each app
|
|
|
|
**Changes to integrator.rs:**
|
|
- System mode: install to `/usr/local/share/applications/` instead of `~/.local/share/applications/`
|
|
- Requires elevated permissions (polkit or sudo)
|
|
|
|
**Changes to cli.rs:**
|
|
- Add `--system` global flag
|
|
- Route to system database when flag is present
|
|
|
|
**Changes to config.rs:**
|
|
```rust
|
|
pub fn data_dir(system_mode: bool) -> PathBuf {
|
|
if system_mode {
|
|
PathBuf::from("/var/lib/driftwood")
|
|
} else {
|
|
dirs::data_dir().unwrap().join("driftwood")
|
|
}
|
|
}
|
|
```
|
|
|
|
### No new database tables
|
|
|
|
Same schema, different file location.
|
|
|
|
### Polkit integration
|
|
|
|
For system-mode operations that need root:
|
|
- Create a polkit policy file: `data/app.driftwood.Driftwood.policy`
|
|
- Actions: `app.driftwood.manage-system-appimages`
|
|
- Use `pkexec` or D-Bus activation for elevated operations
|
|
|
|
---
|
|
|
|
## Feature 11: GNOME Software / KDE Discover Plugin
|
|
|
|
### Why it matters
|
|
|
|
Users expect to find and manage applications through their desktop's software center. A GNOME Software plugin would let users browse AppImages alongside Flatpaks and native packages.
|
|
|
|
### Design
|
|
|
|
**GNOME Software plugin approach:**
|
|
- GNOME Software supports plugins via `gs_plugin_*` C API
|
|
- Plugin provides: search results, app details, install/remove actions
|
|
- Source data: Driftwood's catalog + locally discovered AppImages
|
|
|
|
**Alternative approach (simpler):**
|
|
- Generate AppStream catalog XML from Driftwood's database
|
|
- GNOME Software can read local AppStream catalogs
|
|
- Place catalog at `~/.local/share/swcatalog/xml/driftwood.xml`
|
|
- This requires no C plugin - just generating the right XML
|
|
|
|
### Implementation
|
|
|
|
**Simpler path - AppStream catalog generation:**
|
|
|
|
```rust
|
|
// In src/core/appstream.rs (~150 lines)
|
|
pub fn generate_appstream_catalog(db: &Database) -> Result<String> {
|
|
let apps = db.get_all_appimages()?;
|
|
let mut xml = String::from("<?xml version=\"1.0\"?>\n<components version=\"0.16\">\n");
|
|
for app in &apps {
|
|
xml.push_str(&format!(
|
|
"<component type=\"desktop-application\">\n\
|
|
<id>appimage.{}</id>\n\
|
|
<name>{}</name>\n\
|
|
<summary>{}</summary>\n\
|
|
<pkgname>{}</pkgname>\n\
|
|
<launchable type=\"desktop-id\">{}</launchable>\n\
|
|
</component>\n",
|
|
make_app_id(app.app_name.as_deref().unwrap_or(&app.filename)),
|
|
app.app_name.as_deref().unwrap_or(&app.filename),
|
|
app.description.as_deref().unwrap_or(""),
|
|
app.filename,
|
|
app.desktop_file.as_deref().unwrap_or(""),
|
|
));
|
|
}
|
|
xml.push_str("</components>\n");
|
|
Ok(xml)
|
|
}
|
|
|
|
pub fn install_appstream_catalog(db: &Database) -> Result<PathBuf>
|
|
```
|
|
|
|
**Full plugin path (future):**
|
|
- Write a GNOME Software plugin in C that calls Driftwood's D-Bus interface
|
|
- This requires the D-Bus daemon from Feature 12
|
|
- Not practical until the daemon exists
|
|
|
|
### No database changes
|
|
|
|
---
|
|
|
|
## Feature 12: Qt6 Frontend
|
|
|
|
### Why it matters
|
|
|
|
KDE Plasma users prefer Qt-native applications. While GTK4 apps work on KDE, a native Qt6 frontend would provide better visual integration with Breeze theme, KDE file dialogs, and Plasma notifications.
|
|
|
|
### Design
|
|
|
|
**Architecture: shared core, separate UI**
|
|
|
|
The key insight: Driftwood's `src/core/` has zero GTK dependencies. It's pure Rust business logic. A Qt6 frontend can use the same core directly as a Rust library.
|
|
|
|
**Approach options:**
|
|
|
|
1. **Rust + Qt6 via cxx-qt** - Write Qt6 UI in Rust using cxx-qt bindings. Same language, same build system.
|
|
|
|
2. **D-Bus daemon + Qt6 C++ frontend** - Driftwood runs as a D-Bus service (the daemon from the design doc). Qt6 frontend communicates via D-Bus. Two separate binaries.
|
|
|
|
3. **Shared Rust library + Qt6 QML frontend** - Compile core/ as a cdylib. Qt6 frontend calls it via FFI. Mixed Rust+C++.
|
|
|
|
**Recommended: Option 1 (cxx-qt)**
|
|
|
|
```
|
|
driftwood-qt/
|
|
Cargo.toml # depends on driftwood-core (workspace member)
|
|
build.rs # cxx-qt build integration
|
|
src/
|
|
main.rs # Qt application entry
|
|
bridge.rs # cxx-qt bridge definitions
|
|
qml/
|
|
Main.qml # main window
|
|
AppList.qml # library view
|
|
AppDetail.qml # detail view
|
|
Settings.qml # preferences
|
|
```
|
|
|
|
**Prerequisites:**
|
|
- Extract `src/core/` into a separate crate (`driftwood-core`) within a Cargo workspace
|
|
- This is a significant refactoring but benefits both frontends
|
|
- The GTK4 frontend becomes `driftwood-gtk` depending on `driftwood-core`
|
|
|
|
### Workspace structure
|
|
|
|
```toml
|
|
# Root Cargo.toml
|
|
[workspace]
|
|
members = ["driftwood-core", "driftwood-gtk", "driftwood-qt"]
|
|
|
|
# driftwood-core/Cargo.toml
|
|
[package]
|
|
name = "driftwood-core"
|
|
# No GTK dependencies - pure Rust + rusqlite + ureq + sha2 + etc.
|
|
|
|
# driftwood-gtk/Cargo.toml
|
|
[package]
|
|
name = "driftwood-gtk"
|
|
[dependencies]
|
|
driftwood-core = { path = "../driftwood-core" }
|
|
gtk = { version = "0.11", package = "gtk4" }
|
|
adw = { version = "0.9", package = "libadwaita" }
|
|
|
|
# driftwood-qt/Cargo.toml
|
|
[package]
|
|
name = "driftwood-qt"
|
|
[dependencies]
|
|
driftwood-core = { path = "../driftwood-core" }
|
|
cxx-qt = "0.7"
|
|
```
|
|
|
|
### Scope
|
|
|
|
This is a very large feature. The workspace refactoring alone is significant. The Qt6 UI needs to replicate:
|
|
- Library view (grid + list)
|
|
- Detail view (9 sections)
|
|
- Dashboard
|
|
- Preferences
|
|
- All dialogs (update, security, duplicate, cleanup, integration)
|
|
|
|
**Recommended phasing:**
|
|
1. Extract driftwood-core crate (refactoring, no new features)
|
|
2. Build minimal Qt6 frontend with library view + detail view
|
|
3. Add remaining views incrementally
|
|
|
|
---
|
|
|
|
## Implementation Order
|
|
|
|
Based on dependencies, impact, and effort:
|
|
|
|
**Wave 1 - Quick wins (can start immediately, independent):**
|
|
- Feature 4: Developer tools (extends CLI, uses existing analysis)
|
|
- Feature 5: ARM64 support (small changes across existing modules)
|
|
- Feature 2: Exportable security reports (new module, straightforward)
|
|
|
|
**Wave 2 - High impact (foundation for later features):**
|
|
- Feature 1: Config backup/restore (enables safe updates)
|
|
- Feature 3: CVE push notifications (requires notify-rust dep)
|
|
- Feature 6: Wayland runtime detection (extends existing module)
|
|
|
|
**Wave 3 - Community and ecosystem:**
|
|
- Feature 7: Community sandbox profiles (needs remote registry design)
|
|
- Feature 8: AppImage catalog integration (needs API integration)
|
|
|
|
**Wave 4 - Advanced (larger scope, lower priority):**
|
|
- Feature 9: Batch re-packaging (modifies user files - needs extra care)
|
|
- Feature 10: Multi-user mode (polkit integration, permission model)
|
|
- Feature 11: GNOME Software plugin (AppStream catalog generation first)
|
|
- Feature 12: Qt6 frontend (workspace refactoring prerequisite)
|
|
|
|
---
|
|
|
|
## New Dependencies
|
|
|
|
| Feature | New Crate | Purpose |
|
|
|---------|-----------|---------|
|
|
| 1 (Backup) | `tar` + `xz2` or shell to tar/xz | Archive creation |
|
|
| 3 (Notifications) | `notify-rust` | Desktop notifications via D-Bus |
|
|
| 12 (Qt6) | `cxx-qt` | Qt6 bindings for Rust |
|
|
|
|
All other features use existing dependencies (ureq for HTTP, serde_json for JSON, rusqlite for database).
|
|
|
|
## Database Migration Plan
|
|
|
|
Phase 5 features add up to 8 new tables. These should be grouped into 2-3 migrations:
|
|
|
|
**Migration v5 (Wave 1-2 features):**
|
|
- config_backups + backup_entries
|
|
- exported_reports
|
|
- cve_notifications
|
|
- runtime_wayland columns on appimages
|
|
|
|
**Migration v6 (Wave 3-4 features):**
|
|
- catalog_sources + catalog_apps
|
|
- sandbox_profiles + sandbox_profile_history
|
|
- runtime_updates
|
|
|
|
This keeps each migration focused and testable.
|