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

> Integrate clipboard, credentials, and file drop in Native SDK using permission-gated bridges. Learn how to enable these features via app.zon manifest and consume them with JavaScript or Zig.

- Repository: [Vercel Labs/native](https://github.com/vercel-labs/native)
- Tags: how-to-guide
- Published: 2026-07-18

---

**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`**.

```zon
// 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.

```javascript
// 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.

```javascript
// 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.

```javascript
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.

```zig
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.