Developer docs/Reference

Host services

Discover the APIs your Host actually provides, with parameter and result examples for everyday tasks.

Verified September 11, 2026

On this page

Discover your Host’s contracts#

Tables list contract parameter schemas. Generated methods without business parameters are called directly, for example host.runtime.getPaths(). Arbitrary JSON results are unknown; validate them against your application’s data shape.

Call host.services to inspect the running Host. Its response gives registered services and their actual parameter/result contracts, including differences between builds.

ts
import { host } from "@trove/plugin-sdk";

await host.ready();
const available = await host.services.list({ prefix: "host." });
const storageContract = await host.services.describe({
  service: "host.storage",
});
console.log(available, storageContract);
MethodParametersResult
list{ prefix?: string }{ services: [{ id, version, provider }] }
describe{ service: string, version?: string }{ descriptor, contract }
resolve{ service: string, version?: string }{ id, version, provider }
getStatus{ service: string, version?: string }{ id, version, state }

A version range mismatch rejects with service.version_mismatch. Status is Declared, Starting, Available, or Unavailable; registration alone does not prove a Backend is running.

Runtime and paths#

MethodParametersResult
echoAny JSON valueThe same JSON value
getHostInfo{}{ name, version, platform, arch, protocol }
getContext{}Current Curio identity and mode
getPaths{}{ data, cache, temp }, each an absolute path
openCurio{ id }{ ok: true }
closeSelf{}{ ok: true }
revealPath{ path }{ ok: true }
openUrl{ url }{ ok: true }

The final four operations require the desktop Host. Use the returned paths rather than hard-coding a user’s home or application data directory. data is for persistent files, cache for regenerable content, and temp for session work.

Store small JSON values#

host.storage isolates keys by the Host-bound Curio ID. A single serialized value is limited to 1 MiB and the serialized store to 16 MiB. It is JSON storage, not a keychain.

MethodParametersResult
set{ key: string, value: JSON }{ stored: true }
get{ key: string }{ value: JSON, found: boolean }
delete{ key: string }{ deleted: boolean }
list{ prefix?: string }{ keys: string[] }
clear{}{ cleared: true }
ts
import { host } from "@trove/plugin-sdk";

const key = `docs-demo-${crypto.randomUUID()}`;
try {
  await host.storage.set({ key, value: { theme: "system" } });
  const saved = await host.storage.get({ key });
  console.log(saved.found, saved.value);
} finally {
  await host.storage.delete({ key });
}

A missing key returns found: false, value: null. A stored JSON null returns found: true, value: null; check found before interpreting the value. Use the data directory for larger files.

Work with files#

host.fs.readText({ path }) returns { content: string }; writeText({ path, content }) returns { written: true }. Paths are filesystem paths, not browser URLs. Current text and binary transfers are limited to 4 MiB per file.

ts
import { host } from "@trove/plugin-sdk";

const { temp } = await host.runtime.getPaths();
const path = `${temp}/docs-${crypto.randomUUID()}.txt`;
try {
  await host.fs.writeText({ path, content: "Hello Trove" });
  console.log((await host.fs.readText({ path })).content);
} finally {
  await host.fs.remove({ path });
}
MethodParametersResult
stat{ path }{ type, size, modifiedUnixMs }
list{ path }{ entries: [{ name, path, type }] }; up to 20,000 entries
exists{ path }{ exists: boolean }
readBinary{ path }{ data: string }, Base64 encoded
writeBinary{ path, data }{ written: true }; data is Base64
createDirectory{ path }{ created: true }
copy / move{ from, to }{ copied: true } / { moved: true }
remove{ path, recursive?: boolean }{ removed: true }
createTempFile{}{ path: string }

Ask users to select their files through host.dialog when appropriate. Host filesystem calls do not imply the selected path is a Curio-only sandbox; respect the user’s intended file scope and macOS permissions.

Desktop integrations#

ts
import { host } from "@trove/plugin-sdk";

console.log((await host.clipboard.readText()).text);
await host.notification.show({ title: "Export complete", body: "Your file is ready." });

Clipboard supports readText({}) → { text }, writeText({ text }) → { written: true }, and clear({}) → { cleared: true }. Notification currently supports only show({ title, body }) → { shown: true, curioId }.

Use describe to retrieve the full contract before integrating the following services:

ServiceRegistered methods / topics
host.windowshow, hide, close, focus, getBounds, setBounds, center, setTitle, setAlwaysOnTop, setResizable, setDecorations, setShadow, setIgnoreCursorEvents, openChild; activated topic
host.dialogopenFile, openDirectory, saveFile, message, confirm
host.applicationlistInstalled, listRunning, launch, activate, terminate, open, getFrontmost, getIcons
host.shortcutregister, unregister, listSelf; triggered topic

System trust and child windows#

Trove owns system trust for accessibility, input monitoring, and screen recording. Curios display capability status and direct users to the Host permissions page when action is needed; the Host handles requests or opens system settings:

ts
import { host } from "@trove/plugin-sdk";

const { permissions } = await host.permission.list();
const accessibility = permissions.find(item => item.id === "accessibility");
if (accessibility?.status !== "allowed") {
  await host.permission.openSettings({ id: "accessibility" });
}

openSettings accepts accessibility, input, or screen and opens the Trove permissions page. Curios do not request Host permissions themselves. This is not a Curio access allowlist and does not restrict authors from implementing their own native capabilities.

Clipboard also exposes getChangeCount, readCapture, writeImage, getPastePermission, and paste({ pid }). Automatic paste requires Trove's accessibility authorization. The running Host reports foreground application and permission state; Curios should not guess them.

host.window.openChild opens an independent window belonging to the same Curio. Parameters include id, title, query, width, height, and optional minimum dimensions. Reusing an ID focuses its existing window. Calls to host.window from a child act on that calling window. Hiding the main window and retaining a background process are separate responsibilities.

Implemented and planned capabilities#

CapabilityCurrent implementation
Runtime, discovery, storage, filesystemImplemented
Windows, dialogs, text clipboard, notifications, applications, shortcutsImplemented in desktop Host; inspect its contract
Clipboard capture, image writing, automatic paste, permission status, Host permissions pageImplemented; inspect the current Host contract
host.clipboard.readFiles/writeFilesNot registered as separate methods; readCapture can return file information
host.notification.remove/removeAllForCurioDraft only
host.fs.watchDraft only
host.process, host.accessibility, host.input, host.screen, host.keychainPlanned; not registered in the current build

Do not infer method availability from a service name in an architecture draft. Handle service.not_found and service.method_not_found and check the installed Host version before enabling a feature.

Discover Curios by ID#

The Web SDK exposes host.curios.has/status/get/start(id); see the complete semantics. Wire registers service host.curios with methods has, status, describe and start. Each takes { "id": "dev.example.text-tools" }.

Has returns boolean; status/start return CurioStatus; describe returns { id, version, services: ServiceContract[] } containing public contracts only. Get is an SDK convenience that builds a reference using describe; Wire has no get method. Backends can call the same discovery methods over Wire, then request public capabilities by service and method. Requests retain caller identity and version checks.