Capability Manifest
Overview
Section titled “Overview”A Capability Manifest declares the full set of operations a Tier 1 application exposes to the compositor. Unlike Tier 2 apps, where capabilities are derived by inspecting the accessibility tree, Tier 1 apps define their capabilities explicitly, with typed parameters, typed return values, and versioned schemas.
The manifest is shipped alongside the app binary and signed with the developer’s Ed25519 key. The compositor verifies the signature at registration time. On verification failure, the app falls back to Tier 2 (AT-SPI2-derived capabilities).
Capability JSON Schema
Section titled “Capability JSON Schema”A single capability declaration within a manifest. Each capability describes one operation the app can perform.
{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Capability", "type": "object", "required": ["id", "name", "description", "category", "parameters", "returns"], "properties": { "id": { "type": "string", "pattern": "^[a-z][a-z0-9]*(\\.[a-z][a-z0-9]*)+$", "description": "Dot-separated capability identifier. E.g., 'mail.search', 'editor.save'." }, "name": { "type": "string", "description": "Human-readable capability name." }, "description": { "type": "string", "description": "What this capability does." }, "category": { "type": "string", "enum": [ "APP_MANAGEMENT", "SURFACE_MANAGEMENT", "ELEMENT_INTERACTION", "TEXT_INPUT", "TEXT_QUERY", "NAVIGATION", "STATE_QUERY", "SYSTEM", "MEDIA_PLAYBACK", "COMMUNICATION", "FILE_OPERATION", "HARDWARE" ], "description": "Intent category from the taxonomy." }, "parameters": { "type": "object", "description": "JSON Schema describing accepted parameters. Use required/optional/default/type." }, "returns": { "type": "object", "description": "JSON Schema describing the return value." }, "side_effects": { "type": "string", "enum": ["none", "read", "write", "destructive", "network", "system"], "description": "What this capability does to the system. Used for permission checks." }, "confirmation_required": { "type": "boolean", "default": false, "description": "If true, requires user confirmation before execution." }, "undo_window_ms": { "type": ["integer", "null"], "description": "If set, the action can be undone within this time window." }, "version": { "type": "string", "pattern": "^\\d+\\.\\d+$", "default": "1.0", "description": "Capability version. Used for compatibility." }, "auth_level": { "type": "string", "enum": ["public", "user", "privileged", "system"], "default": "user", "description": "Minimum trust level required." } }}Category Taxonomy
Section titled “Category Taxonomy”The category field places each capability in the intent taxonomy. This is used for routing and permission scoping.
| Category | Purpose |
|---|---|
APP_MANAGEMENT |
Launch, quit, install, configure applications |
SURFACE_MANAGEMENT |
Move, resize, tile, minimize, close windows |
ELEMENT_INTERACTION |
Click buttons, toggle checkboxes, select items |
TEXT_INPUT |
Type text, insert at cursor, replace selection |
TEXT_QUERY |
Read content, search, extract text |
NAVIGATION |
Open URLs, switch tabs, navigate within apps |
STATE_QUERY |
Get counts, check status, list items |
SYSTEM |
System-level operations (audio, display, power) |
MEDIA_PLAYBACK |
Play, pause, skip, seek media |
COMMUNICATION |
Send messages, make calls |
FILE_OPERATION |
Move, copy, delete files |
HARDWARE |
Interact with devices (camera, microphone) |
Side Effects
Section titled “Side Effects”The side_effects field determines the permission check behavior. Operations with no side effects can be auto-approved. Destructive and network operations require confirmation or elevated trust.
Auth Levels
Section titled “Auth Levels”| Level | Meaning |
|---|---|
public |
No trust required. Read-only state queries. |
user |
Standard user trust. Most capability invocations. |
privileged |
Elevated trust. System configuration changes. |
system |
System identity only. Reserved for the compositor and System Intelligence. |
CapabilityManifest JSON Schema
Section titled “CapabilityManifest JSON Schema”The container that holds all capability declarations for one application, plus optional event definitions and the Ed25519 signature.
{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "CapabilityManifest", "type": "object", "required": ["app_id", "version", "manifest_version", "capabilities"], "properties": { "app_id": { "type": "string", "description": "Application identifier matching desktop_id." }, "version": { "type": "string", "description": "Application version." }, "manifest_version": { "type": "string", "pattern": "^\\d+\\.\\d+$", "default": "1.0", "description": "Manifest schema version. Used for compatibility." }, "capabilities": { "type": "array", "items": { "$ref": "Capability" }, "description": "All capabilities this app exposes." }, "events": { "type": "array", "items": { "type": "object", "required": ["event_type", "description"], "properties": { "event_type": { "type": "string" }, "description": { "type": "string" }, "payload_schema": { "type": "object" } } }, "description": "Custom events this app emits." }, "signature": { "type": ["string", "null"], "description": "Ed25519 signature over the manifest content (excluding this field). Compositor verifies against the app's public key." } }}CapabilityHandler Trait
Section titled “CapabilityHandler Trait”Tier 1 applications implement this trait to handle capability invocations. The compositor calls invoke when System Intelligence dispatches an intent to a Tier 1 capability.
#[async_trait]pub trait CapabilityHandler: Send + Sync { /// Returns the capability manifest for this handler. fn manifest(&self) -> &CapabilityManifest;
/// Execute a capability invocation. async fn invoke( &self, capability_id: &str, params: serde_json::Value, context: &InvocationContext, ) -> Result<CapabilityResult, CapabilityError>;
/// Optional: validate parameters before execution. fn validate_params( &self, capability_id: &str, params: &serde_json::Value, ) -> Result<(), ValidationError> { let cap = self.manifest() .capabilities .iter() .find(|c| c.id == capability_id) .ok_or(ValidationError::UnknownCapability(capability_id.to_string()))?;
jsonschema::validate(&cap.parameters_schema, params) .map_err(ValidationError::SchemaViolation) }}
#[derive(Debug, Clone, Serialize, Deserialize)]pub struct InvocationContext { pub session_id: Option<SessionId>, pub source: InvocationSource, pub timestamp: DateTime<Utc>, pub app_id: AppId,}
#[derive(Debug, Clone, Serialize, Deserialize)]pub enum InvocationSource { SystemIntelligence,}
#[derive(Debug, Clone, Serialize, Deserialize)]pub struct CapabilityResult { pub success: bool, pub data: Option<serde_json::Value>, pub state_after: Option<serde_json::Value>, pub undo_token: Option<String>,}
#[derive(Debug, Clone, Serialize, Deserialize)]pub enum CapabilityError { NotFound(String), InvalidParams(String), ExecutionFailed { reason: String, recoverable: bool }, PermissionDenied { reason: String }, Timeout { capability_id: String, elapsed_ms: u64 }, AppUnresponsive { app_id: AppId },}The default validate_params implementation performs JSON Schema validation using the capability’s parameters_schema. Applications can override this for custom validation logic (cross-field checks, business rules).
Ed25519 Signing
Section titled “Ed25519 Signing”Manifests are signed with Ed25519 to ensure authenticity and integrity. The signing and verification flow works as follows.
Key Locations
Section titled “Key Locations”| Key | Path | Permissions |
|---|---|---|
| Developer private key | ~/.config/portal-{app_id}/ed25519.key |
0600 (owner read/write only) |
| Trusted public key | /etc/portal/keys/trusted/{app_id}.pub |
0644 (world-readable) |
Private keys are 32 raw bytes. Public keys are derived from the private key at key generation time.
Signature Format
Section titled “Signature Format”The signature field in the manifest uses the format "ed25519:<base64>". The base64 portion is the Ed25519 signature over the canonicalized manifest content.
Canonicalization
Section titled “Canonicalization”Before signing or verifying, the manifest is canonicalized:
- Remove the
signaturefield entirely from the JSON object. - Sort all remaining keys lexicographically.
- Use compact JSON separators (no whitespace after commas or colons).
Verification Flow
Section titled “Verification Flow”- The compositor receives the manifest at registration time.
- It extracts the
signaturefield value and removes it from the payload. - It canonicalizes the remaining JSON.
- It looks up the trusted public key at
/etc/portal/keys/trusted/{app_id}.pub. - It verifies the Ed25519 signature against the canonicalized payload.
- On success, the manifest is accepted and capabilities are registered.
- On failure, the app falls back to Tier 2 (AT-SPI2-derived capabilities).
Key rotation is supported. When a developer generates a new key pair, the updated public key must be placed in the trusted keys directory before the new manifest is deployed.
Performance
Section titled “Performance”The native manifest load and verify operation must complete in under 50ms, with a maximum acceptable latency of 100ms. This covers JSON parsing, canonicalization, and the Ed25519 signature check.
Architectural Requirement
Section titled “Architectural Requirement”Tier 1 PCP capabilities operate on the application’s data model in Rust. The renderer (webview, terminal canvas, or any other display surface) is a pure consumer of state.
The following APIs are forbidden in Tier 1 capability handlers:
evaluate_script()/execute_script()(JavaScript injection into a webview)document.execCommand()(browser formatting commands)- Any UI automation API that operates on the rendered surface rather than the underlying data
These restrictions apply only to PCP capability handlers. The UI layer itself (toolbar buttons, keyboard shortcuts) may use these APIs for direct user interaction.
Capability IDs use the format {domain}.{app_id}.{action} where app_id may contain dots (reverse-DNS names supported). The minimum is three dot-separated parts. Example: editor.org.xfce.mousepad.read (domain=editor, app=org.xfce.mousepad, action=read).