Skip to main content

haloforge_plugin_api/
permissions.rs

1/// Fine-grained permissions a plugin must declare in its manifest.
2/// The host checks these at install time (user approval) and at runtime (sandbox enforcement).
3#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
4#[serde(tag = "type", content = "value", rename_all = "snake_case")]
5pub enum Permission {
6    /// Read any host table.
7    DatabaseReadAll,
8    /// Read a specific host table (e.g. "launch_profiles").
9    DatabaseRead(String),
10    /// Write to a specific host table. Restricted tier.
11    DatabaseWrite(String),
12    /// Create tables in the plugin's own namespace.
13    DatabaseCreateTables,
14
15    /// Read any filesystem path (prompts user on first use).
16    FilesystemRead,
17    /// Read within the HaloForge app-data directory only.
18    FilesystemReadAppData,
19    /// Write any filesystem path (prompts user on first use).
20    FilesystemWrite,
21    /// Write within the HaloForge app-data directory only.
22    FilesystemWriteAppData,
23
24    /// Make outbound HTTP requests to any URL.
25    NetworkHttp,
26    /// Make outbound HTTP requests to a specific domain only.
27    NetworkHttpDomain(String),
28
29    /// Register new Tauri IPC commands.
30    IpcRegister,
31
32    /// Emit events on the app event bus.
33    EventsEmit,
34    /// Listen to app lifecycle events.
35    EventsListen,
36
37    /// Inject into UI slots (implied by capability_levels 1/2).
38    UiInject,
39
40    /// Spawn any child process (high risk — Restricted tier).
41    ProcessSpawn,
42    /// Spawn only executables from a declared whitelist.
43    ProcessSpawnWhitelist(Vec<String>),
44
45    /// Show desktop toast notifications.
46    Notifications,
47
48    /// Read the clipboard.
49    ClipboardRead,
50    /// Write to the clipboard.
51    ClipboardWrite,
52
53    /// Navigate within the host UI (module switches, opening settings tabs).
54    HostNavigation,
55    /// Read stable host UI state such as the active module or settings tab.
56    HostAppStateRead,
57    /// Consume file-open intents routed by the host shell.
58    HostFileIntents,
59    /// Open stable host file and directory picker dialogs.
60    HostFileDialogs,
61    /// Reuse the host AIChat transport and model selection.
62    HostAIChatAccess,
63    /// Read the active host theme and design tokens.
64    HostThemeRead,
65    /// Subscribe to stable host events exposed to plugins.
66    HostEventSubscribe,
67
68    /// Read app config (theme, language).
69    AppConfigRead,
70}
71
72impl Permission {
73    /// Approval tier for this permission.
74    pub fn tier(&self) -> PermissionTier {
75        match self {
76            Self::UiInject
77            | Self::EventsListen
78            | Self::DatabaseCreateTables
79            | Self::AppConfigRead
80            | Self::Notifications
81            | Self::HostAppStateRead
82            | Self::HostThemeRead => PermissionTier::Transparent,
83
84            Self::DatabaseReadAll
85            | Self::DatabaseRead(_)
86            | Self::IpcRegister
87            | Self::EventsEmit
88            | Self::NetworkHttpDomain(_)
89            | Self::HostNavigation
90            | Self::HostFileIntents
91            | Self::HostFileDialogs
92            | Self::HostAIChatAccess
93            | Self::HostEventSubscribe => PermissionTier::Standard,
94
95            Self::FilesystemRead
96            | Self::FilesystemWrite
97            | Self::FilesystemReadAppData
98            | Self::FilesystemWriteAppData
99            | Self::NetworkHttp
100            | Self::ProcessSpawnWhitelist(_)
101            | Self::ClipboardRead
102            | Self::ClipboardWrite => PermissionTier::Sensitive,
103
104            Self::DatabaseWrite(_)
105            | Self::ProcessSpawn => PermissionTier::Restricted,
106        }
107    }
108
109    /// Human-readable description shown in the permission prompt.
110    pub fn description(&self) -> String {
111        match self {
112            Self::DatabaseReadAll           => "Read all app data".into(),
113            Self::DatabaseRead(t)           => format!("Read table: {t}"),
114            Self::DatabaseWrite(t)          => format!("Write to table: {t}"),
115            Self::DatabaseCreateTables      => "Create plugin-owned database tables".into(),
116            Self::FilesystemRead            => "Read files from your filesystem".into(),
117            Self::FilesystemReadAppData     => "Read files in the app data directory".into(),
118            Self::FilesystemWrite           => "Write files to your filesystem".into(),
119            Self::FilesystemWriteAppData    => "Write files in the app data directory".into(),
120            Self::NetworkHttp               => "Make outbound HTTP requests".into(),
121            Self::NetworkHttpDomain(d)      => format!("Make HTTP requests to: {d}"),
122            Self::IpcRegister               => "Register new app commands".into(),
123            Self::EventsEmit                => "Emit app events".into(),
124            Self::EventsListen              => "Listen to app lifecycle events".into(),
125            Self::UiInject                  => "Inject UI components".into(),
126            Self::ProcessSpawn              => "Spawn arbitrary child processes".into(),
127            Self::ProcessSpawnWhitelist(v)  => format!("Spawn processes: {}", v.join(", ")),
128            Self::Notifications             => "Show desktop notifications".into(),
129            Self::ClipboardRead             => "Read the clipboard".into(),
130            Self::ClipboardWrite            => "Write to the clipboard".into(),
131            Self::HostNavigation            => "Navigate within HaloForge".into(),
132            Self::HostAppStateRead          => "Read HaloForge UI state".into(),
133            Self::HostFileIntents           => "Receive file-open intents from HaloForge".into(),
134            Self::HostFileDialogs           => "Open HaloForge file and directory dialogs".into(),
135            Self::HostAIChatAccess          => "Use HaloForge AI models and chat transport".into(),
136            Self::HostThemeRead             => "Read HaloForge theme tokens".into(),
137            Self::HostEventSubscribe        => "Subscribe to HaloForge host events".into(),
138            Self::AppConfigRead             => "Read app configuration".into(),
139        }
140    }
141}
142
143#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
144pub enum PermissionTier {
145    /// Auto-granted at install time with no user prompt.
146    Transparent = 0,
147    /// Shown once at install time; user approves/denies.
148    Standard = 1,
149    /// Shown at install + confirmation on first actual use.
150    Sensitive = 2,
151    /// Disabled by default; user must manually enable in Plugin Manager.
152    Restricted = 3,
153}
154
155#[cfg(test)]
156mod tests {
157    use super::{Permission, PermissionTier};
158
159    #[test]
160    fn host_permissions_have_expected_tiers() {
161        assert_eq!(Permission::HostAppStateRead.tier(), PermissionTier::Transparent);
162        assert_eq!(Permission::HostThemeRead.tier(), PermissionTier::Transparent);
163        assert_eq!(Permission::HostNavigation.tier(), PermissionTier::Standard);
164        assert_eq!(Permission::HostFileDialogs.tier(), PermissionTier::Standard);
165        assert_eq!(Permission::HostAIChatAccess.tier(), PermissionTier::Standard);
166    }
167}