<!--
hoody-files Subskill (sdk)
Auto-generated by Hoody Skills Generator
Generated: 2026-05-06T20:06:58.718Z
Model: mimo-v2.5-pro
Mode: sdk


Tokens: 41662

DO NOT EDIT MANUALLY - Changes will be overwritten on next generation
-->

# hoody-files

Universal file access across local storage and 60+ cloud providers. Every file path is a URL.

---

## Overview

### What This Service Does

hoody-files provides a unified file system abstraction over local storage and 60+ cloud storage providers. It exposes a RESTful API where every file path is a URL, enabling agents to read, write, search, archive, and manage files across any storage backend through a single consistent interface.

**Core capabilities:**
- **File CRUD** — Upload, download, list, delete, copy, move, rename files and directories
- **Search** — Glob pattern matching and ripgrep-powered content search across any backend
- **Archives** — Extract, preview, and create ZIP/TAR archives without full extraction
- **60+ Cloud Backends** — Connect S3, Google Drive, Dropbox, OneDrive, FTP, SFTP, WebDAV, and dozens more
- **FUSE Mounts** — Persistent filesystem mounts for connected backends
- **Image Processing** — On-the-fly thumbnails, format conversion, and resizing
- **Remote Access** — Ad-hoc file access via Git, S3, SSH, FTP, and WebDAV protocols
- **Journal** — Mutation tracking with queryable history

### When to Use hoody-files

| Scenario | Use hoody-files |
|----------|----------------|
| Agent needs to read/write files in a container | ✅ |
| Agent needs to access cloud storage (S3, GCS, Dropbox, etc.) | ✅ |
| Agent needs to search file contents across storage | ✅ |
| Agent needs to extract or create archives | ✅ |
| Agent needs to process images (resize, convert) | ✅ |
| Agent needs to manage persistent storage mounts | ✅ |
| Agent needs to provision containers or servers | ❌ Use hoody-api |

### Authentication Model

hoody-files runs as a Hoody Kit service behind the Hoody Proxy. Authentication is handled at the proxy layer — the service URL itself encodes project, container, and service identity. The Hoody SDK handles authentication transparently when you initialize the client with valid credentials.

```
import { HoodyClient } from '@hoody-ai/hoody-sdk'

// Token-based authentication (recommended for agents)
const client = new HoodyClient({
  baseURL: 'https://api.hoody.icu',
  token: process.env.HOODY_TOKEN
})

// Credential-based authentication
const client = await HoodyClient.authenticate(
  'https://api.hoody.icu',
  { username: process.env.HOODY_USER, password: process.env.HOODY_PASS }
)
```

### Base URL Construction

The service URL follows the Hoody Kit pattern:

```
https://{projectId}-{containerId}-files-{serviceId}.{node}.containers.hoody.icu
```

When using the SDK, this URL is resolved automatically. For direct HTTP access, construct it from your project metadata.

### Key Concepts

- **Paths as URLs** — Every file path maps directly to a URL segment. `/documents/report.pdf` is both a file path and a URL path.
- **Backends** — Named connections to storage providers. Each backend has a type (s3, drive, dropbox, etc.) and credentials.
- **Mounts** — Persistent FUSE filesystem mounts that make remote backends appear as local directories.
- **Root-level vs v1 API** — The service exposes two interface layers: root-level paths for direct file access and `/api/v1/` paths for structured operations. Both are documented below.

---

## Core Workflows

### File Operations

Basic file CRUD through root-level endpoints. These are the primary endpoints for direct file access.

#### List Directory Contents

Retrieve a directory listing or download a file. Returns HTML by default; pass `json` parameter for structured output.

```
// List directory as JSON
const listing = await client.files.listDirectory('/documents', 'true')
```

```
{
  "entries": [
    {
      "name": "report.pdf",
      "type": "file",
      "size": 1024000,
      "modified": "2025-01-15T10:30:00Z"
    },
    {
      "name": "images",
      "type": "directory",
      "size": 0,
      "modified": "2025-01-14T08:00:00Z"
    }
  ]
}
```

**Endpoint:** `GET /{path}`

#### Upload a File

Upload creates new files or overwrites existing ones. The request body is the file content.

```
// Upload a file
await client.files.upload({ path: '/documents/report.pdf' })
```

**Endpoint:** `PUT /{path}`

#### Delete a File or Directory

Permanently removes a file or directory. For directories, this is recursive.

```
// Delete a file
await client.files.deleteRecursive({ path: '/documents/old-report.pdf' })
```

**Endpoint:** `DELETE /{path}`

#### Get File Metadata (HEAD)

Retrieve metadata headers without downloading file content.

```
// Get metadata headers
await client.files.getMetadata({ path: '/documents/report.pdf' })
```

**Endpoint:** `HEAD /{path}`

#### Touch File

Create an empty file if it does not exist, or update the modification time if it does. Cannot be used on directories.

```
// Create empty file or update mtime
await client.files.touch({ path: '/documents/new-file.txt', touch: 'true' })
```

**Endpoint:** `PUT /{path}?touch`

#### Create Directory

Create a new directory using the WebDAV MKCOL method.

```
// Create directory
await client.files.directories.create({ path: '/documents/reports/2025' })
```

**Endpoint:** `MKCOL /{path}`

---

### Advanced File Operations

Structured file operations through the v1 API. These endpoints provide richer functionality including append, copy, move, permissions, and metadata.

#### Get Directory Listing or Download File (v1)

The v1 API version returns JSON by default and supports additional query parameters for filtering, sorting, and content retrieval.

```
// List directory via v1 API
const listing = await client.files.get('/documents', { sort: 'name', order: 'asc' })
```

```
{
  "entries": [
    {
      "name": "report.pdf",
      "type": "file",
      "size": 1024000,
      "modified": "2025-01-15T10:30:00Z",
      "hash": "abc123"
    }
  ]
}
```

**Endpoint:** `GET /api/v1/files/{path}`

#### Upload or Append File (v1)

Upload a file via the v1 API. Use the `append` parameter to append to an existing file instead of overwriting.

```
// Upload file via v1 API
await client.files.put({ path: '/documents/report.pdf' })

// Append to existing file
await client.files.put('/documents/log.txt', { append: 'true' })
```

**Endpoint:** `PUT /api/v1/files/{path}`

#### Append Binary Data

Append binary data to the end of an existing file. Creates the file if it does not exist. Auto-creates parent directories.

```
// Append data to file
await client.files.append({ path: '/documents/log.txt' })
```

**Endpoint:** `PUT /api/v1/files/append/{path}`

#### Copy File or Directory

Copy a file or directory to a new location. Supports recursive directory copy. Auto-creates parent directories at destination.

```
// Copy file to new location
await client.files.copy({ path: '/documents/report.pdf', copy_to: '/backups/report.pdf' })

// Copy with overwrite
await client.files.copy('/documents/report.pdf', '/backups/report.pdf', 'true')
```

**Endpoint:** `POST /api/v1/files/copy/{path}`

#### Move or Rename File

Move or rename a file/directory to a new location. Works across directories. Auto-creates parent directories at destination. Requires both upload and delete permissions.

```
// Move file to new location
await client.files.move({ path: '/documents/draft.pdf', move_to: '/documents/final/report.pdf' })
```

**Endpoint:** `POST /api/v1/files/move/{path}`

#### Change File Permissions

Change file or directory permissions using octal mode (Unix only).

```
// Set permissions to 755
await client.files.chmod({ path: '/scripts/run.sh', chmod: '755' })
```

**Endpoint:** `PATCH /api/v1/files/chmod/{path}`

#### Change File Ownership

Change file or directory ownership (Unix only). Group is optional.

```
// Change owner and group
await client.files.chown({ path: '/data/config.json', chown: 'www-data:www-data' })

// Change owner only
await client.files.chown({ path: '/data/config.json', chown: 'admin' })
```

**Endpoint:** `PATCH /api/v1/files/chown/{path}`

#### Get File Metadata (stat)

Get detailed metadata for a single file or directory without downloading content. Returns name, type, size, modification time, permissions, ownership, and symlink information.

```
// Get detailed file metadata
const stat = await client.files.stat({ path: '/documents/report.pdf' })
```

```
{
  "name": "report.pdf",
  "path": "/documents/report.pdf",
  "type": "file",
  "size": 1024000,
  "modified": "2025-01-15T10:30:00Z",
  "permissions": "0644",
  "owner": "user",
  "group": "staff",
  "is_symlink": false
}
```

**Endpoint:** `GET /api/v1/files/stat/{path}`

#### Resolve Canonical Path

Resolve a file or directory path to its canonical absolute form by following all symbolic links and resolving `.`/`..` segments.

```
// Resolve symbolic links and relative paths
const resolved = await client.files.realpath({ path: '/documents/../data/./config.json' })
```

```
{
  "path": "/data/config.json"
}
```

**Endpoint:** `GET /api/v1/files/realpath/{path}`

#### File Operations (POST)

Perform various file operations through a single endpoint: create directory, extract archive, download from URL, move, or copy.

```
// Create directory
await client.files.operate('/documents/new-folder', { mkdir: 'true' })

// Extract archive to destination
await client.files.operate('/data/archive.zip', {
  extract: 'true',
  dest: '/data/extracted'
})

// Download from remote URL
await client.files.operate('/downloads/', {
  download_from: 'https://example.com/file.zip'
})

// Move file
await client.files.operate('/documents/draft.pdf', {
  move_to: '/documents/final.pdf'
})

// Copy file
await client.files.operate('/documents/report.pdf', {
  copy_to: '/backups/report.pdf',
  overwrite: 'true'
})
```

**Endpoint:** `POST /api/v1/files/{path}`

#### Modify File Properties (PATCH v1)

Modify file properties or move/rename via the v1 API. Supports chmod, chown, rename, and cross-directory move.

```
// Change permissions via PATCH
await client.files.patchApi('/scripts/run.sh', {
  chmod: '755'
})

// Rename file
await client.files.patchApi('/documents/old-name.pdf', {
  data: { name: 'new-name.pdf' }
})

// Move to different directory
await client.files.patchApi('/documents/report.pdf', {
  data: { move_to: '/archive/2025/report.pdf' }
})
```

**Endpoint:** `PATCH /api/v1/files/{path}`

#### Delete File or Directory (v1)

Delete a file or directory from the server or a remote backend.

```
// Delete via v1 API
await client.files.delete({ path: '/documents/old-report.pdf' })

// Delete from specific backend
await client.files.delete('/remote/file.txt', { backend: 'my-s3-backend' })
```

**Endpoint:** `DELETE /api/v1/files/{path}`

---

### Search & Discovery

#### Directory Search

Search for files matching a query within a directory. Returns HTML by default; use `json` parameter for structured output.

```
// Search directory for matching files
const results = await client.files.search('/documents', 'report', 'true')
```

```
{
  "entries": [
    {
      "name": "report-2025.pdf",
      "type": "file",
      "size": 2048000,
      "modified": "2025-01-15T10:30:00Z"
    },
    {
      "name": "report-summary.txt",
      "type": "file",
      "size": 512,
      "modified": "2025-01-14T08:00:00Z"
    }
  ]
}
```

**Endpoint:** `GET /{directory}?q`

#### Glob Pattern Search

Find files and directories matching a glob pattern. Supports recursive patterns (`**/*.rs`), brace expansion (`{ts,tsx}`), character classes (`[a-z]`), and standard wildcards (`*`). Returns file metadata sorted by name.

```
// Find all TypeScript files recursively
const matches = await client.files.glob('/src', '**/*.ts', {
  max_results: 100,
  max_depth: 10,
  sort: 'name',
  order: 'asc'
})
```

```
{
  "matches": [
    {
      "path": "/src/index.ts",
      "name": "index.ts",
      "type": "file",
      "size": 2048,
      "modified": "2025-01-15T10:30:00Z"
    },
    {
      "path": "/src/utils/helpers.ts",
      "name": "helpers.ts",
      "type": "file",
      "size": 4096,
      "modified": "2025-01-14T08:00:00Z"
    }
  ],
  "total": 2
}
```

**Endpoint:** `GET /api/v1/files/glob/{path}`

#### Grep Content Search

Search file or directory contents using regex patterns. Powered by ripgrep engine with `.gitignore` support, binary file detection, and configurable limits. Returns matching lines with optional context.

```
// Search for pattern in file contents
const results = await client.files.grep('/src', 'function\\s+handle', {
  ignore_case: true,
  glob: '*.ts',
  context: 2,
  max_matches: 50
})
```

```
{
  "matches": [
    {
      "path": "/src/handler.ts",
      "line_number": 42,
      "line": "function handleRequest(req: Request) {",
      "context_before": ["import { Request } from './types';", ""],
      "context_after": ["  const body = await req.json();", "  return process(body);"]
    }
  ],
  "total": 1
}
```

**Endpoint:** `GET /api/v1/files/grep/{path}`

---

### Archive Operations

#### Extract Archive

Extract ZIP, TAR, or compressed TAR archives to a destination directory. Empty `extract` parameter extracts all entries; specify a path to selectively extract matching entries.

```
// Extract entire archive
const result = await client.files.archives.extract('/data/archive.zip', 'true', '/data/extracted')

// Extract specific entries matching pattern
const result = await client.files.archives.extract('/data/archive.zip', '*.txt', '/data/text-files')
```

```
{
  "status": "started",
  "extracted": 15,
  "destination": "/data/extracted"
}
```

**Endpoint:** `GET /{archive}?extract`

#### Extract Single File from Archive

Extract a single file or directory from inside an archive. Only the specified entry (and its children if a directory) is extracted, leaving other archive contents untouched.

```
// Extract single file from archive
const result = await client.files.archives.extractFile(
  '/data/archive.zip',
  'src/main.ts',
  '/data/extracted'
)
```

```
{
  "status": "completed",
  "extracted": 1,
  "destination": "/data/extracted"
}
```

**Endpoint:** `GET /{archive}?extract_file`

#### Preview Archive Contents

List contents of archives without extracting, or read a specific file from the archive. With empty `preview`: returns JSON listing of all entries. With `preview=<path>`: returns the specific file content.

```
// List all archive entries
const contents = await client.files.archives.preview({ archive: '/data/archive.zip' })

// Read specific file from archive
const fileContent = await client.files.archives.preview('/data/archive.zip', 'README.md', 'true')
```

```
{
  "entries": [
    {
      "name": "src/main.ts",
      "type": "file",
      "size": 2048,
      "modified": "2025-01-10T00:00:00Z"
    },
    {
      "name": "src/utils/",
      "type": "directory",
      "size": 0,
      "modified": "2025-01-10T00:00:00Z"
    },
    {
      "name": "README.md",
      "type": "file",
      "size": 512,
      "modified": "2025-01-10T00:00:00Z"
    }
  ],
  "total": 3
}
```

**Endpoint:** `GET /{archive}?preview`

#### View File from Archive

Read and return a single file from inside an archive without extracting the entire archive. Returns the raw file content with auto-detected MIME type.

```
// View file content from archive
const content = await client.files.archives.viewFile({ archive: '/data/archive.zip', preview: 'README.md' })
```

**Endpoint:** `GET /{archive}?view_file`

#### Download Directory as ZIP

Create and download an entire directory as a ZIP archive.

```
// Download directory as ZIP
await client.files.archives.downloadAsZip({ directory: '/documents/reports', zip: 'true' })
```

**Endpoint:** `GET /{directory}?zip`

#### Extraction History and Status

Track archive extraction operations.

```
// Get extraction history (successful and failed)
const history = await client.files.archives.getHistory({ extraction_history: 'true' })

// List currently running extractions (root-level)
const active = await client.files.archives.listActive({ extractions: 'true' })

// List currently running extractions (v1 API)
const activeGlobal = await client.files.archives.listGlobal()
```

```
{
  "extractions": [
    {
      "archive": "/data/archive.zip",
      "destination": "/data/extracted",
      "status": "completed",
      "files_extracted": 15,
      "started": "2025-01-15T10:30:00Z",
      "completed": "2025-01-15T10:30:05Z"
    }
  ]
}
```

**Endpoints:**
- `GET /?extraction_history`
- `GET /?extractions`
- `GET /api/v1/extractions`

---

### Download Management

#### Download File from Remote URL

Download a file from a remote URL to the server. The download runs asynchronously.

```
// Download file from URL to server directory
const result = await client.files.downloads.fetch({ directory: '/downloads/', download: 'https://example.com/file.zip' })
```

```
{
  "status": "started",
  "url": "https://example.com/file.zip",
  "destination": "/downloads/file.zip"
}
```

**Endpoint:** `GET /{directory}?download`

#### Track Active Downloads

Monitor currently running download operations.

```
// List active downloads for a directory
const active = await client.files.downloads.listActive({ directory: '/downloads/', downloads: 'true' })

// List all active downloads globally (v1 API)
const allActive = await client.files.downloads.listGlobal()
```

```
{
  "downloads": [
    {
      "url": "https://example.com/large-file.zip",
      "destination": "/downloads/large-file.zip",
      "progress": 67,
      "bytes_downloaded": 67108864,
      "bytes_total": 100000000,
      "speed": 10485760
    }
  ]
}
```

**Endpoints:**
- `GET /{directory}?downloads`
- `GET /api/v1/downloads`

#### Download History

Retrieve history of past download operations.

```
// Get download history
const history = await client.files.downloads.getHistory({ download_history: 'true' })
```

```
{
  "downloads": [
    {
      "url": "https://example.com/file.zip",
      "destination": "/downloads/file.zip",
      "status": "completed",
      "size": 104857600,
      "completed": "2025-01-15T10:30:00Z"
    }
  ]
}
```

**Endpoint:** `GET /?download_history`

---

### Backend Management

Connect to 60+ cloud storage providers. Each backend is a named connection with credentials that can be used for file operations, mounts, and remote access.

#### List All Backends

```
// List all connected backends
const backends = await client.files.backends.list()
```

```
{
  "backends": [
    {
      "id": "my-s3-backend",
      "type": "s3",
      "name": "My S3 Storage",
      "status": "connected"
    },
    {
      "id": "gdrive-main",
      "type": "drive",
      "name": "Google Drive",
      "status": "connected"
    }
  ]
}
```

**Endpoint:** `GET /api/v1/backends`

#### Connect Backend — Key Provider Examples

Each backend type has its own endpoint. Below are examples for the most common providers with their required fields.

**S3 (AWS, MinIO, DigitalOcean Spaces, etc.):**

```
await client.files.backends.connectS3({
  name: 'my-s3',
  access_key_id: 'AKIAIOSFODNN7EXAMPLE',
  secret_access_key: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
  region: 'us-east-1',
  endpoint: 'https://s3.amazonaws.com'
})
```

**Endpoint:** `POST /api/v1/backends/s3`

**Google Drive:**

```
await client.files.backends.connectDrive({
  name: 'gdrive-main',
  client_id: 'your-client-id.apps.googleusercontent.com',
  client_secret: 'your-client-secret',
  token: '{"access_token":"...","refresh_token":"...","token_type":"Bearer"}'
})
```

**Endpoint:** `POST /api/v1/backends/drive`

**Dropbox:**

```
await client.files.backends.connectDropbox({
  name: 'dropbox-main',
  token: 'your-dropbox-oauth-token'
})
```

**Endpoint:** `POST /api/v1/backends/dropbox`

**OneDrive:**

```
await client.files.backends.connectOnedrive({
  name: 'onedrive-main',
  client_id: 'your-client-id',
  client_secret: 'your-client-secret',
  token: '{"access_token":"...","refresh_token":"..."}'
})
```

**Endpoint:** `POST /api/v1/backends/onedrive`

**FTP (host required):**

```
await client.files.backends.connectFtp({
  name: 'ftp-server',
  host: 'ftp.example.com',
  user: 'ftpuser',
  pass: 'ftppassword',
  port: 21
})
```

**Endpoint:** `POST /api/v1/backends/ftp`

**SFTP/SSH:**

```
await client.files.backends.connectSftp({
  name: 'sftp-server',
  host: 'sftp.example.com',
  user: 'sftpuser',
  pass: 'sftppassword',
  port: 22
})
```

**Endpoint:** `POST /api/v1/backends/sftp`

**WebDAV:**

```
await client.files.backends.connectWebdav({
  name: 'nextcloud',
  url: 'https://nextcloud.example.com/remote.php/dav/files/user/',
  user: 'user',
  pass: 'password'
})
```

**Endpoint:** `POST /api/v1/backends/webdav`

**Alias (remote or path alias, remote required):**

```
await client.files.backends.connectAlias({
  name: 'my-alias',
  remote: 'my-s3:path/to/dir'
})
```

**Endpoint:** `POST /api/v1/backends/alias`

**Crypt (encryption wrapper, password and remote required):**

```
await client.files.backends.connectCrypt({
  name: 'encrypted-s3',
  remote: 'my-s3:encrypted-data',
  password: 'your-encryption-passphrase'
})
```

**Endpoint:** `POST /api/v1/backends/crypt`

**Union (merge multiple upstreams, upstreams required):**

```
await client.files.backends.connectUnion({
  name: 'combined',
  upstreams: 'my-s3: gdrive-main: dropbox-main:'
})
```

**Endpoint:** `POST /api/v1/backends/union`

**Cache (caching wrapper, remote required):**

```
await client.files.backends.connectCache({
  name: 'cached-s3',
  remote: 'my-s3:'
})
```

**Endpoint:** `POST /api/v1/backends/cache`

**Chunker (split large files, remote required):**

```
await client.files.backends.connectChunker({
  name: 'chunked-s3',
  remote: 'my-s3:chunks'
})
```

**Endpoint:** `POST /api/v1/backends/chunker`

**Compress (compression wrapper, remote required):**

```
await client.files.backends.connectCompress({
  name: 'compressed-s3',
  remote: 'my-s3:compressed'
})
```

**Endpoint:** `POST /api/v1/backends/compress`

**Backblaze B2 (account and key required):**

```
await client.files.backends.connectB2({
  name: 'b2-storage',
  account: 'your-account-id',
  key: 'your-application-key'
})
```

**Endpoint:** `POST /api/v1/backends/b2`

**Cloudinary (api_key, api_secret, cloud_name required):**

```
await client.files.backends.connectCloudinary({
  name: 'cloudinary-assets',
  api_key: 'your-api-key',
  api_secret: 'your-api-secret',
  cloud_name: 'your-cloud-name'
})
```

**Endpoint:** `POST /api/v1/backends/cloudinary`

**File Fabric (url required):**

```
await client.files.backends.connectFilefabric({
  name: 'enterprise-fabric',
  url: 'https://filefabric.example.com',
  user: 'user',
  pass: 'password'
})
```

**Endpoint:** `POST /api/v1/backends/filefabric`

**Local filesystem:**

```
await client.files.backends.connectLocal({
  name: 'local-data',
  root: '/data/storage'
})
```

**Endpoint:** `POST /api/v1/backends/local`

**Memory (in-memory storage):**

```
await client.files.backends.connectMemory({
  name: 'temp-storage'
})
```

**Endpoint:** `POST /api/v1/backends/memory`

**Hasher (checksums wrapper):**

```
await client.files.backends.connectHasher({
  name: 'hashed-s3',
  remote: 'my-s3:'
})
```

**Endpoint:** `POST /api/v1/backends/hasher`

**Combine (merge remotes):**

```
await client.files.backends.connectCombine({
  name: 'combined-storage',
  upstreams: 'my-s3: gdrive-main:'
})
```

**Endpoint:** `POST /api/v1/backends/combine`

#### All 62 Backend Types

Every backend type follows the same pattern: `POST /api/v1/backends/{type}`. The table below lists all supported types.

| Backend Type | Path | Category |
|---|---|---|
| alias | `/api/v1/backends/alias` | Utility |
| azureblob | `/api/v1/backends/azureblob` | Cloud Storage |
| azurefiles | `/api/v1/backends/azurefiles` | Cloud Storage |
| b2 | `/api/v1/backends/b2` | Cloud Storage |
| box | `/api/v1/backends/box` | Cloud Drive |
| cache | `/api/v1/backends/cache` | Utility |
| chunker | `/api/v1/backends/chunker` | Utility |
| cloudinary | `/api/v1/backends/cloudinary` | Media |
| combine | `/api/v1/backends/combine` | Utility |
| compress | `/api/v1/backends/compress` | Utility |
| crypt | `/api/v1/backends/crypt` | Utility |
| drive | `/api/v1/backends/drive` | Cloud Drive |
| dropbox | `/api/v1/backends/dropbox` | Cloud Drive |
| fichier | `/api/v1/backends/fichier` | Cloud Drive |
| filefabric | `/api/v1/backends/filefabric` | Enterprise |
| filescom | `/api/v1/backends/filescom` | Cloud Drive |
| ftp | `/api/v1/backends/ftp` | Protocol |
| gofile | `/api/v1/backends/gofile` | Cloud Drive |
| google-cloud-storage | `/api/v1/backends/google-cloud-storage` | Cloud Storage |
| google-photos | `/api/v1/backends/google-photos` | Media |
| hasher | `/api/v1/backends/hasher` | Utility |
| hdfs | `/api/v1/backends/hdfs` | Big Data |
| hidrive | `/api/v1/backends/hidrive` | Cloud Drive |
| http | `/api/v1/backends/http` | Protocol |
| iclouddrive | `/api/v1/backends/iclouddrive` | Cloud Drive |
| imagekit | `/api/v1/backends/imagekit` | Media |
| internetarchive | `/api/v1/backends/internetarchive` | Archive |
| jottacloud | `/api/v1/backends/jottacloud` | Cloud Drive |
| koofr | `/api/v1/backends/koofr` | Cloud Drive |
| linkbox | `/api/v1/backends/linkbox` | Cloud Drive |
| local | `/api/v1/backends/local` | Local |
| mailru | `/api/v1/backends/mailru` | Cloud Drive |
| mega | `/api/v1/backends/mega` | Cloud Drive |
| memory | `/api/v1/backends/memory` | Utility |
| netstorage | `/api/v1/backends/netstorage` | Cloud Storage |
| onedrive | `/api/v1/backends/onedrive` | Cloud Drive |
| opendrive | `/api/v1/backends/opendrive` | Cloud Drive |
| oracleobjectstorage | `/api/v1/backends/oracleobjectstorage` | Cloud Storage |
| pcloud | `/api/v1/backends/pcloud` | Cloud Drive |
| pikpak | `/api/v1/backends/pikpak` | Cloud Drive |
| pixeldrain | `/api/v1/backends/pixeldrain` | Cloud Drive |
| premiumizeme | `/api/v1/backends/premiumizeme` | Cloud Drive |
| protondrive | `/api/v1/backends/protondrive` | Cloud Drive |
| putio | `/api/v1/backends/putio` | Cloud Drive |
| qingstor | `/api/v1/backends/qingstor` | Cloud Storage |
| quatrix | `/api/v1/backends/quatrix` | Enterprise |
| s3 | `/api/v1/backends/s3` | Cloud Storage |
| seafile | `/api/v1/backends/seafile` | Cloud Drive |
| sftp | `/api/v1/backends/sftp` | Protocol |
| sharefile | `/api/v1/backends/sharefile` | Enterprise |
| sia | `/api/v1/backends/sia` | Decentralized |
| smb | `/api/v1/backends/smb` | Protocol |
| storj | `/api/v1/backends/storj` | Decentralized |
| sugarsync | `/api/v1/backends/sugarsync` | Cloud Drive |
| swift | `/api/v1/backends/swift` | Cloud Storage |
| tardigrade | `/api/v1/backends/tardigrade` | Decentralized |
| ulozto | `/api/v1/backends/ulozto` | Cloud Drive |
| union | `/api/v1/backends/union` | Utility |
| uptobox | `/api/v1/backends/uptobox` | Cloud Drive |
| webdav | `/api/v1/backends/webdav` | Protocol |
| yandex | `/api/v1/backends/yandex` | Cloud Drive |
| zoho | `/api/v1/backends/zoho` | Cloud Drive |

#### Get Backend Details

Retrieve detailed configuration and status for a specific backend.

```
const backend = await client.files.backends.getDetails({ id: 'my-s3-backend' })
```

```
{
  "id": "my-s3-backend",
  "type": "s3",
  "name": "My S3 Storage",
  "status": "connected",
  "config": {
    "region": "us-east-1",
    "endpoint": "https://s3.amazonaws.com"
  }
}
```

**Endpoint:** `GET /api/v1/backends/{id}`

#### Update Backend Credentials

Rotate credentials (passwords, tokens, OAuth refresh tokens, S3 keys, passphrases) for an existing backend connection. Identity fields (host, user, port, type) cannot be changed — disconnect and reconnect to change those.

```
await client.files.backends.update('my-s3-backend', {
  access_key_id: 'NEW_ACCESS_KEY',
  secret_access_key: 'NEW_SECRET_KEY'
})
```

**Endpoint:** `PUT /api/v1/backends/{id}`

#### Test Backend Connection

Verify that a backend connection is working correctly.

```
const testResult = await client.files.backends.testConnection({ id: 'my-s3-backend' })
```

```
{
  "status": "ok",
  "message": "Connection successful"
}
```

**Endpoint:** `GET /api/v1/backends/{id}/test`

#### Disconnect Backend

Remove a backend connection. Does not delete files on the remote storage.

```
await client.files.backends.disconnect({ id: 'old-backend' })
```

**Endpoint:** `DELETE /api/v1/backends/{id}`

---

### Mount Management

Create persistent FUSE filesystem mounts for connected backends, allowing direct file system access to remote storage. All mounts are automatically persisted and restored on server restart.

#### List All Mounts

```
// List all mounts
const mounts = await client.files.mounts.list()

// Filter by label
const filtered = await client.files.mounts.list('production')
```

```
{
  "mounts": [
    {
      "id": "mount-abc123",
      "backend": "my-s3-backend",
      "path": "/mnt/s3",
      "status": "mounted",
      "label": "production"
    }
  ]
}
```

**Endpoint:** `GET /api/v1/mounts`

#### Create Persistent Mount

```
const mount = await client.files.mounts.create({
  backend: 'my-s3-backend',
  path: '/mnt/s3',
  label: 'production',
  vfs_config: {
    cache_size: 1024,
    buffer_size: 64
  }
})
```

```
{
  "id": "mount-abc123",
  "backend": "my-s3-backend",
  "path": "/mnt/s3",
  "status": "mounted",
  "label": "production"
}
```

**Endpoint:** `POST /api/v1/mounts`

#### Get Mount Details

```
const details = await client.files.mounts.getDetails({ id: 'mount-abc123' })
```

```
{
  "id": "mount-abc123",
  "backend": "my-s3-backend",
  "path": "/mnt/s3",
  "status": "mounted",
  "label": "production",
  "vfs_config": {
    "cache_size": 1024,
    "buffer_size": 64
  },
  "created": "2025-01-15T10:00:00Z"
}
```

**Endpoint:** `GET /api/v1/mounts/{id}`

#### Update Mount Configuration

Update the VFS configuration for an existing mount. Allows changing cache settings, buffer sizes, and other VFS parameters.

```
await client.files.mounts.update('mount-abc123', {
  vfs_config: {
    cache_size: 2048,
    buffer_size: 128
  }
})
```

**Endpoint:** `PATCH /api/v1/mounts/{id}`

#### Unmount Filesystem

Remove a mount and disconnect the FUSE filesystem.

```
await client.files.mounts.unmount({ id: 'mount-abc123' })
```

**Endpoint:** `DELETE /api/v1/mounts/{id}`

---

### Journal System

The journal tracks all file mutations (writes, deletes, renames) with queryable history. Useful for audit trails, sync operations, and change detection.

#### Query Journal Entries

Query file mutation journal entries with optional filters. Supports cursor-based pagination via `after_id`.

```
// Query all recent mutations
const entries = await client.files.journal.query({
  limit: 50
})

// Query mutations for a specific path
const pathEntries = await client.files.journal.query({
  path: '/documents/',
  op: 'write',
  limit: 25
})

// Paginate through results
const nextPage = await client.files.journal.query({
  limit: 50,
  after_id: 1234
})
```

```
{
  "entries": [
    {
      "id": 1234,
      "path": "/documents/report.pdf",
      "op": "write",
      "timestamp": "2025-01-15T10:30:00Z",
      "size": 1024000
    },
    {
      "id": 1235,
      "path": "/documents/old-report.pdf",
      "op": "delete",
      "timestamp": "2025-01-15T10:31:00Z"
    }
  ],
  "total": 2,
  "has_more": false
}
```

**Endpoint:** `GET /api/v1/journal`

#### Flush Journal to Disk

Forces all pending journal entries to be written and fsynced to disk. Returns 200 with `flushed=true` if all entries were durably persisted, or 503 with `flushed=false` if flush failed.

```
const result = await client.files.journal.flush()
```

```
{
  "flushed": true,
  "entries_flushed": 15
}
```

**Endpoint:** `POST /api/v1/journal/flush`

#### Get Journal Statistics

Returns storage statistics for the journal system including entry counts, blob storage usage, writer health, and pruning info.

```
const stats = await client.files.journal.getStats()
```

```
{
  "total_entries": 15000,
  "blob_storage_bytes": 5242880,
  "writer_healthy": true,
  "oldest_entry": "2025-01-01T00:00:00Z",
  "newest_entry": "2025-01-15T10:30:00Z",
  "pruning_enabled": true,
  "pruning_max_age_days": 30
}
```

**Endpoint:** `GET /api/v1/journal/stats`

---

## Protocol & Processing

### Image Processing

On-the-fly image processing with format conversion, resizing, and effects. Supports JPEG, PNG, WebP, GIF, BMP input/output. Works for both local files and all 60+ remote cloud storage backends.

```
// Generate thumbnail
const thumbnail = await client.files.images.process(
  '/photos/vacation/beach.jpg',
  'true',
  {
    format: 'webp',
    width: 200,
    height: 200,
    resize: 'fill',
    quality: 80
  }
)

// Convert format with blur effect
const processed = await client.files.images.process(
  '/photos/landscape.png',
  'true',
  {
    format: 'jpeg',
    quality: 90,
    blur: 2.5
  }
)

// Grayscale conversion
const bw = await client.files.images.process(
  '/photos/color.jpg',
  'true',
  {
    grayscale: 'true',
    format: 'png'
  }
)
```

**Endpoint:** `GET /{image}?thumbnail`

**Supported parameters:**
- `format` — Output format: jpeg, png, webp, gif, bmp
- `width` / `height` — Target dimensions in pixels
- `resize` — Resize mode: fill, fit, limit
- `quality` / `q` — Output quality (1-100)
- `blur` — Gaussian blur radius
- `grayscale` — Convert to grayscale
- `bg` — Background color for transparent images

---

### Remote Access

Access files from remote servers without creating persistent backend connections. These endpoints provide ad-hoc access using protocol-specific parameters.

#### Git Repository Access

Access files from GitHub, GitLab, Bitbucket, or custom Git servers.

```
// Fetch file from Git repository
const content = await client.files.git.fetch(
  '/tmp/repo-file.ts',
  'git',
  'https://github.com/user/repo.git',
  {
    ref: 'main',
    pass: 'optional-token'
  }
)
```

**Endpoint:** `GET /{path}?type=git`

#### S3 Storage Access

Access AWS S3 or S3-compatible storage (MinIO, DigitalOcean Spaces, etc.) directly.

```
// Access file from S3
const content = await client.files.s3.access(
  '/tmp/s3-file.txt',
  's3',
  's3.amazonaws.com',
  {
    s3_bucket: 'my-bucket',
    s3_region: 'us-east-1',
    user: 'AKIAIOSFODNN7EXAMPLE',
    pass: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'
  }
)
```

**Endpoint:** `GET /{path}?type=s3`

#### SSH/SFTP Access

Connect to remote SSH servers for file access and upload.

```
// Read file from SSH server
const content = await client.files.ssh.access(
  '/tmp/remote-file.txt',
  'ssh',
  'ssh.example.com',
  {
    user: 'sshuser',
    pass: 'sshpassword'
  }
)

// Upload file to SSH server
await client.files.ssh.upload({ path: '/tmp/local-file.txt' })
```

**Endpoints:**
- `GET /{path}?type=ssh`
- `PUT /{path}?type=ssh`

#### FTP Access

Connect to FTP servers for file access.

```
// Access file from FTP server
const content = await client.files.ftp.access(
  '/tmp/ftp-file.txt',
  'ftp',
  'ftp.example.com',
  {
    user: 'ftpuser',
    pass: 'ftppassword',
    ftp_secure: false,
    ftp_passive: true
  }
)
```

**Endpoint:** `GET /{path}?type=ftp`

#### WebDAV Access

Connect to WebDAV servers (Nextcloud, ownCloud, etc.) for file access.

```
// Access file from WebDAV server
const content = await client.files.webdav.access(
  '/tmp/webdav-file.txt',
  'webdav',
  'nextcloud.example.com',
  {
    user: 'webdavuser',
    pass: 'webdavpassword',
    webdav_path: '/remote.php/dav/files/user/'
  }
)
```

**Endpoint:** `GET /{path}?type=webdav`

---

### WebDAV Protocol

hoody-files implements the WebDAV protocol for compatibility with WebDAV clients. These methods provide standard WebDAV operations.

#### Capability Discovery

Returns supported HTTP methods and WebDAV capabilities in the Allow header. Used by WebDAV clients for capability discovery.

```
const options = await client.files.webdav.getOptions({ path: '/documents/' })
```

**Endpoint:** `OPTIONS /{path}`

#### Copy Resource

Copy a file or directory to a new destination using WebDAV semantics.

```
await client.files.webdav.copyResource('/documents/report.pdf', '/backups/report.pdf', 'infinity')
```

**Endpoint:** `COPY /{path}`

#### Move or Rename Resource

Move or rename a file/directory using WebDAV semantics.

```
await client.files.webdav.moveResource('/documents/draft.pdf', '/documents/final.pdf')
```

**Endpoint:** `MOVE /{path}`

#### Lock Resource

Lock a file for exclusive editing (WebDAV compatibility).

```
await client.files.webdav.lockResource('/documents/report.pdf', 'infinity')
```

**Endpoint:** `LOCK /{path}`

#### Unlock Resource

Release a previously acquired lock.

```
await client.files.webdav.unlockResource('/documents/report.pdf', 'opaquelocktoken:abc123')
```

**Endpoint:** `UNLOCK /{path}`

#### Get WebDAV Properties

Retrieve WebDAV properties for a resource (PROPFIND).

```
await client.files.webdav.propfindResource('/documents/', '1')
```

**Endpoint:** `PROPFIND /{path}`

#### Update WebDAV Properties

Update WebDAV properties on a resource (PROPPATCH).

```
await client.files.webdav.proppatchResource({ path: '/documents/report.pdf' })
```

**Endpoint:** `PROPPATCH /{path}`

---

### Authentication & Session

#### Check Authentication Status

```
const authStatus = await client.files.authentication.checkAuth({ path: '/documents/' })
```

**Endpoint:** `CHECKAUTH /{path}`

#### Logout / Clear Authentication

```
await client.files.authentication.logout({ path: '/documents/' })
```

**Endpoint:** `LOGOUT /{path}`

---

### Health & System

#### Service Health Check

Standard service health endpoint. Returns service identity, build and start timestamps, resource usage, and caller metadata.

```
const health = await client.files.health.check()
```

```
{
  "status": "healthy",
  "service": "hoody-files",
  "version": "1.0.0",
  "build": "2025-01-10T00:00:00Z",
  "started": "2025-01-14T10:00:00Z",
  "uptime_seconds": 86400,
  "memory_used_mb": 256,
  "memory_total_mb": 512
}
```

**Endpoint:** `GET /api/v1/files/health`

#### API Version

Returns the current API version and server information.

```
const version = await client.files.system.getApiVersion()
```

```
{
  "version": "1.0.0",
  "server": "hoody-files",
  "api_version": "v1"
}
```

**Endpoint:** `GET /api/v1/version`

---

## Advanced Operations

### Multi-Backend File Sync Workflow

Synchronize files between two cloud storage providers using the union and copy operations.

```
// Step 1: Connect source backend (Google Drive)
await client.files.backends.connectDrive({
  name: 'gdrive-source',
  client_id: 'source-client-id',
  client_secret: 'source-secret',
  token: '{"access_token":"...","refresh_token":"..."}'
})

// Step 2: Connect destination backend (S3)
await client.files.backends.connectS3({
  name: 's3-dest',
  access_key_id: 'AKIAIOSFODNN7EXAMPLE',
  secret_access_key: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
  region: 'us-east-1'
})

// Step 3: Verify both connections
const sourceTest = await client.files.backends.testConnection({ id: 'gdrive-source' })
const destTest = await client.files.backends.testConnection({ id: 's3-dest' })

// Step 4: List files on source
const sourceFiles = await client.files.get('/gdrive-source:documents/', {
  backend: 'gdrive-source'
})

// Step 5: Copy files to destination
await client.files.operate('/gdrive-source:documents/report.pdf', {
  copy_to: '/s3-dest:backups/report.pdf',
  overwrite: 'true'
})

// Step 6: Verify copy on destination
const destStat = await client.files.stat({ path: '/s3-dest:backups/report.pdf' })
```

### Archive Extraction Pipeline

Download an archive from a URL, extract it, search the contents, and process results.

```
// Step 1: Download archive from remote URL
await client.files.downloads.fetch({ directory: '/data/', download: 'https://example.com/project-archive.tar.gz' })

// Step 2: Wait for download to complete
let downloads = await client.files.downloads.listActive({ directory: '/data/', downloads: 'true' })
while (downloads.downloads && downloads.downloads.length > 0) {
  await new Promise(resolve => setTimeout(resolve, 1000))
  downloads = await client.files.downloads.listActive({ directory: '/data/', downloads: 'true' })
}

// Step 3: Preview archive contents before extraction
const preview = await client.files.archives.preview({ archive: '/data/project-archive.tar.gz' })

// Step 4: Extract archive
await client.files.archives.extract('/data/project-archive.tar.gz', 'true', '/data/extracted/')

// Step 5: Wait for extraction to complete
let extractions = await client.files.archives.listActive({ extractions: 'true' })
while (extractions.extractions && extractions.extractions.length > 0) {
  await new Promise(resolve => setTimeout(resolve, 1000))
  extractions = await client.files.archives.listActive({ extractions: 'true' })
}

// Step 6: Search extracted contents
const matches = await client.files.grep('/data/extracted/', 'TODO|FIXME', {
  ignore_case: true,
  glob: '*.{ts,js,py}',
  max_matches: 100
})

// Step 7: Get extraction history for audit
const history = await client.files.archives.getHistory({ extraction_history: 'true' })
```

### Batch File Operations with Glob and Process

Find files matching a pattern, get metadata for each, and perform batch operations.

```
// Step 1: Find all large files
const largeFiles = await client.files.glob('/data/', '**/*', {
  max_results: 500,
  sort: 'size',
  order: 'desc'
})

// Step 2: Get detailed stats for files of interest
for (const file of largeFiles.matches.slice(0, 10)) {
  const details = await client.files.stat(file.path)

  // Step 3: Archive old files
  if (new Date(details.modified) < new Date('2024-01-01')) {
    await client.files.move(file.path, `/archive/old/${file.name}`)
  }
}

// Step 4: Search for sensitive content
const sensitive = await client.files.grep('/data/', 'password|secret|api_key', {
  ignore_case: true,
  max_matches: 50
})

// Step 5: Verify operations via journal
const mutations = await client.files.journal.query({
  op: 'rename',
  limit: 50
})
```

### Persistent Mount + Direct Access Workflow

Create a persistent mount for a backend, access files through the mount point, then clean up.

```
// Step 1: Connect backend
await client.files.backends.connectS3({
  name: 'project-storage',
  access_key_id: 'AKIAIOSFODNN7EXAMPLE',
  secret_access_key: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
  region: 'us-east-1'
})

// Step 2: Create persistent mount
const mount = await client.files.mounts.create({
  backend: 'project-storage',
  path: '/mnt/project',
  label: 'project-data',
  vfs_config: {
    cache_size: 2048,
    buffer_size: 128
  }
})

// Step 3: Access files through mount point
const listing = await client.files.listDirectory('/mnt/project/', 'true')

// Step 4: Upload file through mount
await client.files.upload({ path: '/mnt/project/data/new-file.txt' })

// Step 5: Update mount configuration if needed
await client.files.mounts.update(mount.id, {
  vfs_config: {
    cache_size: 4096
  }
})

// Step 6: Verify mount status
const mountDetails = await client.files.mounts.getDetails(mount.id)

// Step 7: Cleanup when done
await client.files.mounts.unmount(mount.id)
await client.files.backends.disconnect({ id: 'project-storage' })
```

### Error Recovery Patterns

Handle common failure scenarios with retry and fallback logic.

```
// Pattern 1: Retry with exponential backoff
async function withRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn()
    } catch (error) {
      if (attempt === maxRetries - 1) throw error
      await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000))
    }
  }
  throw new Error('Max retries exceeded')
}

// Usage: Upload with retry
await withRetry(() => client.files.upload({ path: '/documents/important.pdf' }))

// Pattern 2: Verify operation succeeded
async function uploadAndVerify(path: string) {
  await client.files.upload(path)
  const stat = await client.files.stat(path)
  if (!stat || stat.type !== 'file') {
    throw new Error(`Upload verification failed for ${path}`)
  }
  return stat
}

// Pattern 3: Backend connection recovery
async function ensureBackend(backendId: string) {
  try {
    const test = await client.files.backends.testConnection(backendId)
    if (test.status !== 'ok') {
      throw new Error('Connection test failed')
    }
  } catch (error) {
    // Backend may need credential refresh
    const backend = await client.files.backends.getDetails(backendId)
    console.error(`Backend ${backendId} connection failed:`, error)
    throw error
  }
}

// Pattern 4: Safe delete with verification
async function safeDelete(path: string) {
  const stat = await client.files.stat(path)
  if (!stat) return

  await client.files.deleteRecursive(path)

  // Verify deletion
  try {
    await client.files.stat(path)
    throw new Error(`File ${path} still exists after deletion`)
  } catch (error) {
    // Expected: file not found after deletion
  }
}
```

### Image Processing Pipeline

Process images in batch with format conversion and optimization.

```
// Step 1: Find all images
const images = await client.files.glob('/photos/', '**/*.{jpg,png,gif}', {
  max_results: 200
})

// Step 2: Generate thumbnails for each
for (const image of images.matches) {
  // Generate WebP thumbnail
  await client.files.images.process(image.path, 'true', {
    format: 'webp',
    width: 300,
    height: 300,
    resize: 'fill',
    quality: 80
  })
}

// Step 3: Convert all PNGs to WebP for optimization
const pngs = await client.files.glob('/photos/', '**/*.png', {
  max_results: 100
})

for (const png of pngs.matches) {
  await client.files.images.process(png.path, 'true', {
    format: 'webp',
    quality: 90
  })
}
```

---

## Quick Reference

### Endpoint Groups

| Group | Base Path | Count | Description |
|---|---|---|---|
| File Operations | `/{path}` | 7 | Direct file CRUD, touch, metadata |
| File Operations (v1) | `/api/v1/files/{path}` | 15 | Structured file ops, search, permissions |
| Backends | `/api/v1/backends/` | 67 | 62 provider types + CRUD + test |
| Archives | `/{archive}?*` | 4 | Extract, preview, view from archives |
| Downloads | `/{directory}?download*` | 2 | Remote URL downloads |
| Mounts | `/api/v1/mounts/` | 5 | Persistent FUSE mounts |
| Journal | `/api/v1/journal/` | 3 | Mutation tracking |
| Image Processing | `/{image}?thumbnail` | 1 | On-the-fly image processing |
| Remote Access | `/{path}?type=*` | 5 | Git, S3, SSH, FTP, WebDAV |
| WebDAV Protocol | `/{path}` (various methods) | 9 | COPY, MOVE, LOCK, PROPFIND, etc. |
| Health & System | `/api/v1/files/health`, `/api/v1/version` | 2 | Service health and version |
| History & Status | `/?*` | 3 | Download/extraction history and status |

### Essential Parameters

**File Operations:**
- `path` — File or directory path (URL segment)
- `json` — Return JSON instead of HTML (`true`)
- `sort` / `order` — Sort field and direction
- `backend` — Target backend ID for remote operations

**Search:**
- `pattern` — Glob or regex pattern
- `max_results` — Limit result count
- `max_depth` — Limit recursion depth
- `ignore_case` — Case-insensitive search
- `context` — Lines of context around grep matches

**Archives:**
- `extract` — Extract archive (empty for all, or path pattern)
- `extract_file` — Extract single entry
- `preview` — List or read archive contents
- `dest` — Extraction destination directory

**Image Processing:**
- `format` — Output format (jpeg, png, webp, gif, bmp)
- `width` / `height` — Target dimensions
- `resize` — Resize mode (fill, fit, limit)
- `quality` / `q` — Output quality (1-100)
- `blur` — Blur radius
- `grayscale` — Convert to grayscale

**Mounts:**
- `backend` — Backend ID to mount
- `path` — Mount point path
- `label` — Filter/search label
- `vfs_config` — Cache and buffer settings

**Journal:**
- `path` — Filter by file path
- `op` — Filter by operation type (write, delete, rename)
- `limit` — Max entries to return
- `after_id` — Cursor for pagination

### Response Formats

**File stat/metadata:**
```
{
  "name": "file.txt",
  "path": "/dir/file.txt",
  "type": "file",
  "size": 1024,
  "modified": "2025-01-15T10:30:00Z",
  "permissions": "0644",
  "owner": "user",
  "group": "staff",
  "is_symlink": false
}
```

**Directory listing:**
```
{
  "entries": [
    {
      "name": "file.txt",
      "type": "file",
      "size": 1024,
      "modified": "2025-01-15T10:30:00Z"
    }
  ]
}
```

**Search results (glob/grep):**
```
{
  "matches": [
    {
      "path": "/dir/file.txt",
      "name": "file.txt",
      "type": "file",
      "size": 1024,
      "modified": "2025-01-15T10:30:00Z"
    }
  ],
  "total": 1
}
```

**Backend list:**
```
{
  "backends": [
    {
      "id": "backend-id",
      "type": "s3",
      "name": "My Storage",
      "status": "connected"
    }
  ]
}
```

**Mount list:**
```
{
  "mounts": [
    {
      "id": "mount-id",
      "backend": "backend-id",
      "path": "/mnt/storage",
      "status": "mounted",
      "label": "production"
    }
  ]
}
```

**Journal entries:**
```
{
  "entries": [
    {
      "id": 1,
      "path": "/dir/file.txt",
      "op": "write",
      "timestamp": "2025-01-15T10:30:00Z"
    }
  ],
  "total": 1,
  "has_more": false
}
```

**Health check:**
```
{
  "status": "healthy",
  "service": "hoody-files",
  "version": "1.0.0",
  "build": "2025-01-10T00:00:00Z",
  "started": "2025-01-14T10:00:00Z",
  "uptime_seconds": 86400
}
```

**Operation result:**
```
{
  "status": "completed",
  "message": "Operation successful"
}
```

### SDK Method Quick Reference

| Namespace | Method | Endpoint |
|---|---|---|
| `client.files` | `listDirectory(path)` | `GET /{path}` |
| `client.files` | `upload(path)` | `PUT /{path}` |
| `client.files` | `deleteRecursive(path)` | `DELETE /{path}` |
| `client.files` | `getMetadata(path)` | `HEAD /{path}` |
| `client.files` | `touch(path, touch)` | `PUT /{path}?touch` |
| `client.files` | `search(directory, q)` | `GET /{directory}?q` |
| `client.files` | `get(path)` | `GET /api/v1/files/{path}` |
| `client.files` | `put(path)` | `PUT /api/v1/files/{path}` |
| `client.files` | `operate(path)` | `POST /api/v1/files/{path}` |
| `client.files` | `patchApi(path)` | `PATCH /api/v1/files/{path}` |
| `client.files` | `delete(path)` | `DELETE /api/v1/files/{path}` |
| `client.files` | `append(path)` | `PUT /api/v1/files/append/{path}` |
| `client.files` | `chmod(path, chmod)` | `PATCH /api/v1/files/chmod/{path}` |
| `client.files` | `chown(path, chown)` | `PATCH /api/v1/files/chown/{path}` |
| `client.files` | `copy(path, copy_to)` | `POST /api/v1/files/copy/{path}` |
| `client.files` | `move(path, move_to)` | `POST /api/v1/files/move/{path}` |
| `client.files` | `stat(path)` | `GET /api/v1/files/stat/{path}` |
| `client.files` | `realpath(path)` | `GET /api/v1/files/realpath/{path}` |
| `client.files` | `glob(path, pattern)` | `GET /api/v1/files/glob/{path}` |
| `client.files` | `grep(path, pattern)` | `GET /api/v1/files/grep/{path}` |
| `client.files.backends` | `list()` | `GET /api/v1/backends` |
| `client.files.backends` | `getDetails(id)` | `GET /api/v1/backends/{id}` |
| `client.files.backends` | `update(id, data)` | `PUT /api/v1/backends/{id}` |
| `client.files.backends` | `disconnect(id)` | `DELETE /api/v1/backends/{id}` |
| `client.files.backends` | `testConnection(id)` | `GET /api/v1/backends/{id}/test` |
| `client.files.backends` | `connect{Type}(data)` | `POST /api/v1/backends/{type}` |
| `client.files.mounts` | `list(label?)` | `GET /api/v1/mounts` |
| `client.files.mounts` | `create(data)` | `POST /api/v1/mounts` |
| `client.files.mounts` | `getDetails(id)` | `GET /api/v1/mounts/{id}` |
| `client.files.mounts` | `update(id, data)` | `PATCH /api/v1/mounts/{id}` |
| `client.files.mounts` | `unmount(id)` | `DELETE /api/v1/mounts/{id}` |
| `client.files.journal` | `query(params)` | `GET /api/v1/journal` |
| `client.files.journal` | `flush()` | `POST /api/v1/journal/flush` |
| `client.files.journal` | `getStats()` | `GET /api/v1/journal/stats` |
| `client.files.downloads` | `getHistory(download_history)` | `GET /?download_history` |
| `client.files.downloads` | `listGlobal()` | `GET /api/v1/downloads` |
| `client.files.downloads` | `fetch(directory, download)` | `GET /{directory}?download` |
| `client.files.downloads` | `listActive(directory, downloads)` | `GET /{directory}?downloads` |
| `client.files.archives` | `getHistory(extraction_history)` | `GET /?extraction_history` |
| `client.files.archives` | `listActive(extractions)` | `GET /?extractions` |
| `client.files.archives` | `listGlobal()` | `GET /api/v1/extractions` |
| `client.files.archives` | `extract(archive, extract)` | `GET /{archive}?extract` |
| `client.files.archives` | `extractFile(archive, extract)` | `GET /{archive}?extract_file` |
| `client.files.archives` | `preview(archive)` | `GET /{archive}?preview` |
| `client.files.archives` | `viewFile(archive, preview)` | `GET /{archive}?view_file` |
| `client.files.archives` | `downloadAsZip(directory, zip)` | `GET /{directory}?zip` |
| `client.files.images` | `process(image, thumbnail)` | `GET /{image}?thumbnail` |
| `client.files.health` | `check()` | `GET /api/v1/files/health` |
| `client.files.system` | `getApiVersion()` | `GET /api/v1/version` |
| `client.files.git` | `fetch(path, type, url)` | `GET /{path}?type=git` |
| `client.files.s3` | `access(path, type, server)` | `GET /{path}?type=s3` |
| `client.files.ssh` | `access(path, type, server)` | `GET /{path}?type=ssh` |
| `client.files.ssh` | `upload(path)` | `PUT /{path}?type=ssh` |
| `client.files.ftp` | `access(path, type, server)` | `GET /{path}?type=ftp` |
| `client.files.webdav` | `access(path, type, server)` | `GET /{path}?type=webdav` |
| `client.files.webdav` | `getOptions(path)` | `OPTIONS /{path}` |
| `client.files.webdav` | `copyResource(path, Destination)` | `COPY /{path}` |
| `client.files.webdav` | `moveResource(path, Destination)` | `MOVE /{path}` |
| `client.files.webdav` | `lockResource(path)` | `LOCK /{path}` |
| `client.files.webdav` | `unlockResource(path, Lock-Token)` | `UNLOCK /{path}` |
| `client.files.webdav` | `propfindResource(path)` | `PROPFIND /{path}` |
| `client.files.webdav` | `proppatchResource(path)` | `PROPPATCH /{path}` |
| `client.files.directories` | `create(path)` | `MKCOL /{path}` |
| `client.files.authentication` | `checkAuth(path)` | `CHECKAUTH /{path}` |
| `client.files.authentication` | `logout(path)` | `LOGOUT /{path}` |