How to Integrate Clipboard, Credentials, and File Drop Capabilities in the Native SDK

The Native SDK exposes three permission-gated bridges—native-sdk.clipboard.*, native-sdk.credentials.*, and native-sdk:drop:files—that you enable via app.zon manifest entries and consume through JavaScript bridge calls or Zig event handlers.

The vercel-labs/native repository provides a cross-platform runtime for building desktop applications with web technologies and Zig. According to the vercel-labs/native source code, you integrate clipboard, credentials, and file drop capabilities in the Native SDK by declaring permissions and capabilities in app.zon and invoking dedicated bridge commands from JavaScript. The runtime enforces strict gating so that host-level operations cannot execute without explicit developer consent.

Declare Required Permissions and Capabilities in app.zon

Every Native SDK application defines its host-level access in the app.zon manifest. Clipboard and credentials require entries in the permissions array, while file drops require the capabilities array. At startup, the runtime parses this manifest in src/tooling/manifest.zig and only enables the bridges the host explicitly grants. If a permission is missing, subsequent bridge calls are rejected with error.PermissionDenied.

// app.zon
permissions = [ "clipboard", "credentials" ];
capabilities = [ "file_drops" ];

Native SDK Clipboard Integration

Write and Read Text via the JavaScript Bridge

After declaring the "clipboard" permission, use the injected window.zero.invoke helper to transfer text between your application and the system clipboard. The bridge exposes native-sdk.clipboard.writeText, write, readText, and read commands.

// Write plain text
await window.zero.invoke("native-sdk.clipboard.writeText", { text: "Hello Native" });

// Read plain text
const text = await window.zero.invoke("native-sdk.clipboard.readText", {});

Clipboard writes are fire-and-forget from the host perspective, but the SDK records the operation in the fake executor for testing. You can see assertions for this behavior in tests/ts-core/host_e2e_tests.zig.

Core Clipboard Routing in builtin_bridge.zig

Command strings such as "native-sdk.clipboard.writeText" are dispatched through src/runtime/builtin_bridge.zig, which verifies the command string, extracts the payload, and forwards it to the platform service. The Event union in src/runtime/api.zig defines the clipboard-related effects that the runtime processes.

Native SDK Credentials Integration

Store and Retrieve Secrets from JavaScript

The credentials bridge exposes set, get, and delete commands for keychain-style secret storage. These commands use the native-sdk.credentials.* prefix.

// Define a credential key
const credKey = { service: "my.app", account: "user@example.com" };

// Store a secret
await window.zero.invoke("native-sdk.credentials.set", { ...credKey, secret: "s3cr3t" });

// Retrieve a secret
const secret = await window.zero.invoke("native-sdk.credentials.get", credKey);

// Delete a secret
await window.zero.invoke("native-sdk.credentials.delete", credKey);

Validation and Platform Storage Implementation

Before secrets reach the OS, src/runtime/validation.zig runs validateCredential and related helpers to sanitize input. The platform-agnostic interface lives in src/platform/types.zig through PlatformServices.setCredential, getCredential, and deleteCredential. Per-OS implementations reside in:

  • src/platform/windows/root.zig
  • src/platform/macos/root.zig
  • src/platform/linux/root.zig

Permission-specific test coverage is available in tests/ts-core/bridge_permission_tests.zig.

Native SDK File Drop Integration

Listen for Browser-Level Drop Events

File drops do not use a request-response bridge. Instead, the host pushes a native-sdk:drop:files custom event to the browser context when the user drops files onto the application window.

window.addEventListener("native-sdk:drop:files", (e) => {
    // e.detail is an array of File objects supplied by the host
    console.log("User dropped files:", e.detail);
});

Consume File Drops in Zig via onEvent

You can also consume drops inside your Zig application by matching against the Event union in src/runtime/api.zig. The runtime surfaces two variants: files_dropped for raw platform events and canvas_widget_file_drop for high-level widget events. Both travel through src/runtime/flow.zig before reaching your handler.

pub fn onEvent(app: *App, runtime: *Runtime, event: runtime.Event) !void {
    switch (event) {
        .files_dropped => |drop| {
            // `drop` contains file URLs supplied by the host.
            // Use `runtime.platform.readFile` or other APIs to process them.
        },
        .canvas_widget_file_drop => |widgetDrop| {
            // Contains widget ID and the associated file URLs.
        },
        else => {},
    }
}

Unit tests for widget-level file drops are located in src/runtime/canvas_widget_event_tests.zig.

Security Model and Permission Gating

The SDK enforces a strict capability boundary at two layers. When the app starts, src/runtime/flow.zig performs is_clipboard and is_credentials checks against the parsed manifest. At runtime, the dispatcher in src/runtime/builtin_bridge.zig validates every command string before it reaches platform code. This ensures that reading clipboard text, accessing the credential store, or receiving file drops cannot occur unless the corresponding permission or capability is declared in app.zon.

Summary

  • app.zon manifest declaration is mandatory: list "clipboard" and "credentials" under permissions, and "file_drops" under capabilities.
  • Clipboard operations use native-sdk.clipboard.writeText and native-sdk.clipboard.readText, routed through src/runtime/builtin_bridge.zig.
  • Credential storage uses native-sdk.credentials.set, .get, and .delete, validated in src/runtime/validation.zig and stored via OS-specific implementations under src/platform/*/root.zig.
  • File drops are push events; listen for native-sdk:drop:files in JavaScript or handle files_dropped / canvas_widget_file_drop in Zig through src/runtime/api.zig.
  • Permission gating happens in src/runtime/flow.zig; unauthorized bridge calls return error.PermissionDenied.

Frequently Asked Questions

What manifest fields are required to integrate clipboard, credentials, and file drop capabilities in the Native SDK?

You must add "clipboard" and "credentials" to the permissions array, and "file_drops" to the capabilities array inside app.zon. The runtime parses these fields at launch, and missing entries cause bridge commands or events to be rejected immediately.

How does the Native SDK prevent unauthorized access to clipboard and credentials?

The dispatcher in src/runtime/builtin_bridge.zig verifies every command string against the manifest, and src/runtime/flow.zig performs is_clipboard and is_credentials checks. If the permission is not declared, the SDK returns error.PermissionDenied before invoking any platform service.

Where are credential secrets actually stored when using the Native SDK?

The PlatformServices interface in src/platform/types.zig defines setCredential, getCredential, and deleteCredential. The actual storage uses OS-native APIs: Windows credentials are handled in src/platform/windows/root.zig, macOS in src/platform/macos/root.zig, and Linux in src/platform/linux/root.zig.

Can file drop events be handled on both the JavaScript and Zig sides?

Yes. The host emits a browser-level native-sdk:drop:files event that JavaScript can capture with window.addEventListener. Simultaneously, the Zig runtime surfaces the same drop as either files_dropped or canvas_widget_file_drop inside the Event union defined in src/runtime/api.zig, allowing either side to process the files.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →