> ## 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 (screenshots, logs, sensor data exports) to the Lua CDN and downloading files from it. The CDN is authenticated using the same API key as the device connection.

## 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!,
    apiKey: process.env.LUA_API_KEY!,
    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>
