> ## Documentation Index
> Fetch the complete documentation index at: https://docs.heylua.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# CDN Uploads

> Upload and download files between your device and the Lua CDN

## Overview

Every `DeviceClient` instance includes a `cdn` property for uploading files such as screenshots, logs, and sensor data exports. The CDN uses the same credential as the device connection. A new device credential must include the `assets.upload` operation before this client can upload files.

## Methods

### upload

Upload a file to the CDN. Returns a `CdnUploadResult` with the file ID and public URL.

```typescript theme={null}
const result = await device.cdn.upload(data, filename, contentType);
```

<ParamField body="data" type="Buffer | Blob" required>
  File content as a Node.js Buffer or Blob.
</ParamField>

<ParamField body="filename" type="string" required>
  Filename including extension (e.g., `screenshot.png`, `sensor-log.csv`).
</ParamField>

<ParamField body="contentType" type="string">
  MIME type (e.g., `image/png`, `text/csv`). Defaults to `application/octet-stream`.
</ParamField>

**Returns:** `CdnUploadResult`

```typescript theme={null}
interface CdnUploadResult {
  fileId: string;      // Unique file identifier
  mediaType: string;   // Detected MIME type
  extension: string;   // File extension
  url: string;         // Public URL (https://cdn.heylua.ai/{fileId})
}
```

### download

Download a file from the CDN by its file ID.

```typescript theme={null}
const buffer = await device.cdn.download(fileId);
```

<ParamField body="fileId" type="string" required>
  The file ID returned from a previous upload.
</ParamField>

**Returns:** `Buffer` containing the file content.

### getUrl

Get the public URL for a file without downloading it.

```typescript theme={null}
const url = device.cdn.getUrl(fileId);
// https://cdn.heylua.ai/{fileId}
```

<ParamField body="fileId" type="string" required>
  The file ID returned from a previous upload.
</ParamField>

**Returns:** `string` -- the full CDN URL.

## Use Cases

<CardGroup cols={2}>
  <Card title="Screenshots" icon="camera">
    Kiosk devices capturing screen state for debugging or audit.
  </Card>

  <Card title="Sensor Logs" icon="file-csv">
    Periodic CSV or JSON exports of accumulated sensor readings.
  </Card>

  <Card title="Photos" icon="image">
    Camera-equipped devices uploading inspection photos.
  </Card>

  <Card title="Firmware Reports" icon="file-lines">
    Diagnostic reports uploaded for agent analysis.
  </Card>
</CardGroup>

## Example: Screenshot Upload

A kiosk device that takes a screenshot when the agent requests it and uploads it to the CDN:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { DeviceClient } from '@lua-ai-global/device-client';
  import { execSync } from 'child_process';
  import fs from 'fs';

  const device = new DeviceClient({
    agentId: process.env.LUA_AGENT_ID!,
    deviceCredential: process.env.LUA_DEVICE_CREDENTIAL!,
    deviceName: 'lobby-kiosk',
    commands: [
      {
        name: 'take_screenshot',
        description: 'Capture a screenshot of the kiosk display and upload it',
      },
      {
        name: 'upload_logs',
        description: 'Upload the last hour of application logs',
      },
    ],
  });

  device.onCommand('take_screenshot', async () => {
    // Capture screenshot (Linux with scrot, macOS with screencapture)
    const tmpPath = '/tmp/kiosk-screenshot.png';
    execSync(`screencapture -x ${tmpPath}`);

    const screenshot = fs.readFileSync(tmpPath);
    const result = await device.cdn.upload(screenshot, 'kiosk-screenshot.png', 'image/png');

    return {
      message: 'Screenshot captured and uploaded',
      url: result.url,
      fileId: result.fileId,
      size: screenshot.length,
    };
  });

  device.onCommand('upload_logs', async () => {
    const logPath = '/var/log/kiosk/app.log';
    const logContent = fs.readFileSync(logPath);

    const result = await device.cdn.upload(
      logContent,
      `kiosk-logs-${new Date().toISOString().slice(0, 10)}.log`,
      'text/plain',
    );

    return {
      message: 'Logs uploaded',
      url: result.url,
      fileId: result.fileId,
      lines: logContent.toString().split('\n').length,
    };
  });

  async function main() {
    await device.connect();
    console.log('Kiosk device online');
  }

  main().catch(console.error);
  ```

  ```python Python theme={null}
  import asyncio
  import os
  import subprocess
  from datetime import date
  from lua_device import DeviceClient, DeviceCommandDefinition

  client = DeviceClient(
      agent_id=os.environ["LUA_AGENT_ID"],
      api_key=os.environ["LUA_API_KEY"],
      device_name="lobby-kiosk",
      commands=[
          DeviceCommandDefinition(
              name="take_screenshot",
              description="Capture a screenshot of the kiosk display and upload it",
          ),
          DeviceCommandDefinition(
              name="upload_logs",
              description="Upload the last hour of application logs",
          ),
      ],
  )

  @client.on_command("take_screenshot")
  async def handle_take_screenshot(payload):
      # Capture screenshot (Linux with scrot, macOS with screencapture)
      tmp_path = "/tmp/kiosk-screenshot.png"
      subprocess.run(["screencapture", "-x", tmp_path], check=True)

      with open(tmp_path, "rb") as f:
          screenshot = f.read()

      result = await client.cdn.upload(screenshot, "kiosk-screenshot.png", "image/png")

      return {
          "message": "Screenshot captured and uploaded",
          "url": result.url,
          "fileId": result.file_id,
          "size": len(screenshot),
      }

  @client.on_command("upload_logs")
  async def handle_upload_logs(payload):
      log_path = "/var/log/kiosk/app.log"
      with open(log_path, "rb") as f:
          log_content = f.read()

      result = await client.cdn.upload(
          log_content,
          f"kiosk-logs-{date.today().isoformat()}.log",
          "text/plain",
      )

      return {
          "message": "Logs uploaded",
          "url": result.url,
          "fileId": result.file_id,
          "lines": log_content.decode().count("\n"),
      }

  async def main():
      await client.connect()
      print("Kiosk device online")

  asyncio.run(main())
  ```
</CodeGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Node.js Client" icon="node-js" href="/devices/node-client">
    Full client reference including CDN property
  </Card>

  <Card title="Retail Kiosk Example" icon="store" href="/devices/examples/retail-kiosk">
    Complete kiosk example with CDN uploads
  </Card>

  <Card title="API Reference" icon="book" href="/api/luadeviceclient">
    CDN class method signatures
  </Card>
</CardGroup>
