Hoody’s advanced filesystem backends compose and extend the capabilities of standard remotes. Use these endpoints to create specialized storage layers — combining upstreams, caching cloud providers, splitting large files, aliasing paths, computing checksums, and adding in-memory or local disk storage. Each backend can then be mounted as a persistent FUSE filesystem for direct file access.
Mounts
Section titled “Mounts”Mounts expose connected backends as live FUSE filesystems. Use these endpoints to list existing mounts, retrieve details, create new mounts, update VFS configuration, or remove mounts.
GET /api/v1/mounts
Section titled “GET /api/v1/mounts”Get a list of all active filesystem mounts. Supports filtering by label via the label query parameter.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
label | query | string | No | Filter mounts by label. Only mounts with this exact label will be returned. |
Example
Section titled “Example”curl -X GET "https://api.hoody.com/api/v1/mounts?label=Photos" \ -H "Authorization: Bearer YOUR_TOKEN"const { mounts } = await client.files.mounts.list({ label: "Photos" });Responses
Section titled “Responses”{ "count": 2, "mounts": [ { "id": "mount_550e8400e29b41d4a716446655440000", "backend_id": "bnd_550e8400e29b41d4a716446655440001", "label": "Photos", "mount_path": "/hoody/mounts/mount_550e8400e29b41d4a716446655440000", "status": "active", "created_at": 1735689600 }, { "id": "mount_550e8400e29b41d4a716446655440002", "backend_id": "bnd_550e8400e29b41d4a716446655440003", "label": "Work Documents", "mount_path": "/hoody/mounts/mount_550e8400e29b41d4a716446655440002", "status": "active", "created_at": 1735776000 } ]}GET /api/v1/mounts/{id}
Section titled “GET /api/v1/mounts/{id}”Get detailed information about a specific mount, including its VFS cache configuration.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Mount ID. |
Example
Section titled “Example”curl -X GET "https://api.hoody.com/api/v1/mounts/mount_550e8400e29b41d4a716446655440000" \ -H "Authorization: Bearer YOUR_TOKEN"const mount = await client.files.mounts.getDetails({ id: "mount_550e8400e29b41d4a716446655440000"});Responses
Section titled “Responses”{ "id": "mount_550e8400e29b41d4a716446655440000", "backend_id": "bnd_550e8400e29b41d4a716446655440001", "label": "Photos", "mount_path": "/hoody/mounts/mount_550e8400e29b41d4a716446655440000", "status": "active", "created_at": 1735689600, "vfs_config": { "cache_max_age": 3600, "cache_max_size": 10737418240, "cache_mode": "writes", "dir_cache_time": 300 }}POST /api/v1/mounts
Section titled “POST /api/v1/mounts”Create a persistent FUSE filesystem mount for a connected backend, allowing direct file system access to remote storage.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
backend_id | string | Yes | ID of an existing backend connection. |
label | string | No | Human-readable label for the mount (e.g. “My NAS”, “Work S3”). Used by the UI to identify the mount and to filter via GET /api/v1/mounts?label=.... |
mount_path | string | No | Path for the mount. If omitted, defaults to /hoody/mounts/mount_{uuid}. Relative paths resolve under the server’s mount directory. |
vfs_config | object | No | VFS configuration for performance tuning. See the fields below. |
vfs_config fields:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
cache_max_age | integer | string | No | 3600 | Maximum time files are cached. Accepts seconds or duration strings like "1h", "30m". |
cache_max_size | integer | string | No | 10737418240 | Maximum cache size in bytes (default 10GB). Accepts bytes or human-readable strings like "10G", "128M". |
cache_mode | string | No | "writes" | Cache mode. One of "off", "minimal", "writes", "full". |
dir_cache_time | integer | string | No | 300 | How long directory listings are cached. Accepts seconds or duration strings like "5m", "1h". |
Example
Section titled “Example”curl -X POST "https://api.hoody.com/api/v1/mounts" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "backend_id": "bnd_550e8400e29b41d4a716446655440001", "label": "Photos", "vfs_config": { "cache_mode": "full", "cache_max_size": "20G" } }'const mount = await client.files.mounts.create({ data: { backend_id: "bnd_550e8400e29b41d4a716446655440001", label: "Photos", vfs_config: { cache_mode: "full", cache_max_size: "20G" } }});Responses
Section titled “Responses”{ "success": true, "message": "Mount created successfully", "data": { "id": "mount_550e8400e29b41d4a716446655440000", "backend_id": "bnd_550e8400e29b41d4a716446655440001", "label": "Photos", "mount_path": "/hoody/mounts/mount_550e8400e29b41d4a716446655440000", "status": "active" }}PATCH /api/v1/mounts/{id}
Section titled “PATCH /api/v1/mounts/{id}”Update the VFS configuration for an existing mount. Allows changing cache settings, buffer sizes, and other VFS parameters.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Mount ID. |
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
vfs_config | object | Yes | VFS configuration parameters. Accepts the same field set as in the create endpoint. |
Example
Section titled “Example”curl -X PATCH "https://api.hoody.com/api/v1/mounts/mount_550e8400e29b41d4a716446655440000" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "vfs_config": { "cache_mode": "full", "cache_max_size": "50G" } }'await client.files.mounts.update({ id: "mount_550e8400e29b41d4a716446655440000", data: { vfs_config: { cache_mode: "full", cache_max_size: "50G" } }});Responses
Section titled “Responses”{ "success": true, "message": "Mount configuration updated"}{ "success": false, "error": "Invalid cache_mode: must be one of off, minimal, writes, full"}{ "success": false, "error": "Mount not found"}DELETE /api/v1/mounts/{id}
Section titled “DELETE /api/v1/mounts/{id}”Remove a mount and disconnect the FUSE filesystem.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Mount ID. |
Example
Section titled “Example”curl -X DELETE "https://api.hoody.com/api/v1/mounts/mount_550e8400e29b41d4a716446655440000" \ -H "Authorization: Bearer YOUR_TOKEN"await client.files.mounts.unmount({ id: "mount_550e8400e29b41d4a716446655440000"});Responses
Section titled “Responses”{ "success": true, "message": "Mount removed successfully"}{ "success": false, "error": "Mount not found: mount_550e8400e29b41d4a716446655440000"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
MOUNT_NOT_FOUND | Mount not found | No mount exists with the specified ID. | Verify the mount ID is correct or list all mounts. |
Advanced Backends
Section titled “Advanced Backends”Advanced backends wrap existing remotes to add caching, chunking, checksumming, composition, and other transformations. After connecting a backend, mount it through the endpoints above to access its files through the filesystem.
POST /api/v1/backends/alias
Section titled “POST /api/v1/backends/alias”Create an alias for an existing remote or local path. Useful for exposing a deeply nested remote path under a friendly name.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”| Field | Type | Required | Default | Description |
|---|---|---|---|---|
remote | string | Yes | "" | Remote or path to alias. Can be "myremote:path/to/dir", "myremote:bucket", "myremote:" or "/local/path". |
description | string | No | "" | Human-readable description of the remote. |
Example
Section titled “Example”curl -X POST "https://api.hoody.com/api/v1/backends/alias" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "remote": "s3-prod:backups/2025", "description": "2025 backups alias" }'const backend = await client.files.backends.connectAlias({ data: { remote: "s3-prod:backups/2025", description: "2025 backups alias" }});Responses
Section titled “Responses”{ "success": true, "message": "Backend connected successfully", "data": { "id": "bnd_550e8400e29b41d4a716446655440010", "type": "alias", "backend_type": "alias", "mount_paths": [] }}{ "success": false, "error": "Invalid remote specification: missing ':' separator"}POST /api/v1/backends/cache
Section titled “POST /api/v1/backends/cache”Wrap a remote with a local chunk and metadata cache to reduce latency and bandwidth on repeated reads and to support Plex streaming optimizations.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”| Field | Type | Required | Default | Description |
|---|---|---|---|---|
remote | string | Yes | "" | Remote to cache. Should contain : and a path, e.g. "myremote:path/to/dir", "myremote:bucket", or "myremote:" (not recommended). |
description | string | No | "" | Human-readable description of the remote. |
chunk_path | string | No | "/home/user/.cache/hoody-vfs/cache-backend" | Directory to cache chunk files. The remote name is appended to the final path. Defaults to the value of db_path if not set. |
chunk_size | string | No | "5242880" | Size of a chunk. One of "1M", "5M", "10M". Lower values suit slower connections. Changing this invalidates existing chunks. |
chunk_total_size | string | No | "10737418240" | Total disk space the chunks can occupy. One of "500M", "1G", "10G". Oldest chunks are deleted when exceeded. |
chunk_clean_interval | integer | No | 60 | How often (in seconds) the cache performs cleanups of the chunk storage. |
chunk_no_memory | boolean | No | false | Disable the in-memory cache used for streaming chunks. |
db_path | string | No | "/home/user/.cache/hoody-vfs/cache-backend" | Directory to store the file structure metadata DB. The remote name is used as the DB file name. |
db_purge | boolean | No | false | Clear all cached data for this remote on start. |
db_wait_time | integer | No | 1 | Seconds to wait for the DB to be available before failing. 0 waits forever. |
info_age | integer | No | 21600 | How long (in seconds) to cache directory listings, file size, and times. Possible values: "1h", "24h", "48h". |
workers | integer | No | 4 | Number of parallel workers that download chunks. Higher values need more CPU and increase cloud API request rates. |
read_retries | integer | No | 10 | How many times to retry a read from the cache storage before giving up. |
rps | integer | No | -1 | Hard limit on requests per second to the source filesystem. -1 disables the limit. Directory listings are not throttled. |
writes | boolean | No | false | Cache file data on writes through the filesystem so that reads right after uploads are served from cache. |
tmp_upload_path | string | No | "" | Directory used as temporary storage for new files before they are uploaded to the cloud provider. Empty disables the feature. |
tmp_wait_time | integer | No | 15 | Seconds a file must wait in tmp_upload_path before being uploaded. |
plex_url | string | No | "" | URL of the Plex server (enables Plex integration). |
plex_username | string | No | "" | Plex username. |
plex_password | string | No | "" | Plex password. |
plex_token | string | No | "" | Plex auth token. Auto-set normally. |
plex_insecure | string | No | "" | Skip certificate verification when connecting to the Plex server. |
Example
Section titled “Example”curl -X POST "https://api.hoody.com/api/v1/backends/cache" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "remote": "s3-prod:media", "description": "Cached media library", "chunk_size": "10M", "chunk_total_size": "10G", "workers": 8, "writes": true }'const backend = await client.files.backends.connectCache({ data: { remote: "s3-prod:media", description: "Cached media library", chunk_size: "10M", chunk_total_size: "10G", workers: 8, writes: true }});Responses
Section titled “Responses”{ "success": true, "message": "Backend connected successfully", "data": { "id": "bnd_550e8400e29b41d4a716446655440011", "type": "cache", "backend_type": "cache", "mount_paths": [] }}{ "success": false, "error": "Failed to open cache DB: permission denied"}POST /api/v1/backends/chunker
Section titled “POST /api/v1/backends/chunker”Transparently split large files on a remote into smaller chunks, useful for backends that have per-file size limits.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”| Field | Type | Required | Default | Description |
|---|---|---|---|---|
remote | string | Yes | "" | Remote to chunk/unchunk. Should contain : and a path, e.g. "myremote:path/to/dir", "myremote:bucket", or "myremote:" (not recommended). |
description | string | No | "" | Human-readable description of the remote. |
chunk_size | string | No | "2147483648" | Files larger than this size are split into chunks. |
name_format | string | No | "*.hoody-vfs_chunk.###" | Format of chunk file names. The * placeholder is the base file name and one or more # characters are replaced with the chunk number. |
hash_type | string | No | "md5" | How chunker handles hash sums. One of "none", "md5", "sha1", "md5all", "sha1all", "md5quick", "sha1quick". All modes except "none" require metadata. |
meta_format | string | No | "simplejson" | Format of the metadata object. One of "none", "simplejson". |
start_from | integer | No | 1 | Minimum valid chunk number. Usually 0 or 1. |
transactions | string | No | "rename" | How chunker handles temporary files during transactions. One of "rename", "norename", "auto". |
fail_hard | boolean | No | false | How chunker should handle files with missing or invalid chunks. One of "true", "false". |
Example
Section titled “Example”curl -X POST "https://api.hoody.com/api/v1/backends/chunker" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "remote": "s3-prod:archives", "description": "Chunked cold archives", "chunk_size": "1073741824", "hash_type": "sha1" }'const backend = await client.files.backends.connectChunker({ data: { remote: "s3-prod:archives", description: "Chunked cold archives", chunk_size: "1073741824", hash_type: "sha1" }});Responses
Section titled “Responses”{ "success": true, "message": "Backend connected successfully", "data": { "id": "bnd_550e8400e29b41d4a716446655440012", "type": "chunker", "backend_type": "chunker", "mount_paths": [] }}{ "success": false, "error": "name_format must contain exactly one '*' and at least one '#'"}POST /api/v1/backends/combine
Section titled “POST /api/v1/backends/combine”Combine several remotes into a single directory tree. Each upstream is mounted under a root directory inside the combined filesystem.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”| Field | Type | Required | Default | Description |
|---|---|---|---|---|
upstreams | string | Yes | — | Upstreams for combining, in the form dir=remote:path dir2=remote2:path. Embedded spaces are supported by quoting entries, e.g. "dir=remote:path with space". |
description | string | No | "" | Human-readable description of the remote. |
Example
Section titled “Example”curl -X POST "https://api.hoody.com/api/v1/backends/combine" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "upstreams": "photos=gdrive:photos videos=gdrive:videos", "description": "Combined Google Drive" }'const backend = await client.files.backends.connectCombine({ data: { upstreams: "photos=gdrive:photos videos=gdrive:videos", description: "Combined Google Drive" }});Responses
Section titled “Responses”{ "success": true, "message": "Backend connected successfully", "data": { "id": "bnd_550e8400e29b41d4a716446655440013", "type": "combine", "backend_type": "combine", "mount_paths": [] }}{ "success": false, "error": "upstreams must contain at least one entry in 'dir=remote:path' form"}POST /api/v1/backends/hasher
Section titled “POST /api/v1/backends/hasher”Compute and cache checksums for files on another remote, exposing md5, sha1, or other supported hashes transparently.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”| Field | Type | Required | Default | Description |
|---|---|---|---|---|
remote | string | Yes | "" | Remote to cache checksums for, e.g. myRemote:path. |
description | string | No | "" | Human-readable description of the remote. |
hashes | string | No | ["md5","sha1"] | Comma-separated list of supported checksum types. |
max_age | integer | No | 0 | Maximum time (in seconds) to keep checksums in cache. 0 disables caching, "off" caches forever. |
auto_size | string | No | "0" | Auto-update checksum for files smaller than this size. Disabled by default. |
Example
Section titled “Example”curl -X POST "https://api.hoody.com/api/v1/backends/hasher" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "remote": "s3-prod:data", "description": "Hashed data archive", "hashes": "md5,sha1,blake3", "max_age": 86400 }'const backend = await client.files.backends.connectHasher({ data: { remote: "s3-prod:data", description: "Hashed data archive", hashes: "md5,sha1,blake3", max_age: 86400 }});Responses
Section titled “Responses”{ "success": true, "message": "Backend connected successfully", "data": { "id": "bnd_550e8400e29b41d4a716446655440014", "type": "hasher", "backend_type": "hasher", "mount_paths": [] }}{ "success": false, "error": "Unsupported hash type: blake3"}POST /api/v1/backends/local
Section titled “POST /api/v1/backends/local”Expose a directory on the server’s local disk as a Hoody backend.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”| Field | Type | Required | Default | Description |
|---|---|---|---|---|
description | string | No | "" | Human-readable description of the remote. |
encoding | string | No | "33554434" | Backend encoding. See the encoding section in the overview for details. |
case_insensitive | boolean | No | false | Force the filesystem to report itself as case insensitive, overriding the OS default. |
case_sensitive | boolean | No | false | Force the filesystem to report itself as case sensitive, overriding the OS default. |
links | boolean | No | false | Translate symlinks to/from regular files with a .hoody-vfslink extension. |
copy_links | boolean | No | false | Follow symlinks and copy the pointed-to item. |
skip_links | boolean | No | false | Don’t warn about skipped symlinks or junction points. |
zero_size_links | boolean | No | false | Assume the stat size of links is zero (and read them instead). Deprecated. |
unicode_normalization | boolean | No | false | Apply Unicode NFC normalization to paths and file names. |
one_file_system | boolean | No | false | Don’t cross filesystem boundaries (Unix/macOS only). |
nounc | boolean | No | false | Disable UNC (long path names) conversion on Windows. Must be "true" to enable. |
no_preallocate | boolean | No | false | Disable preallocation of disk space for transferred files. |
no_sparse | boolean | No | false | Disable sparse files for multi-thread downloads (Windows). |
no_check_updated | boolean | No | false | Don’t check whether files change during upload. Useful for filesystems with broken mtime semantics. |
no_set_modtime | boolean | No | false | Disable setting modification time after upload. |
no_clone | boolean | No | false | Disable reflink cloning for server-side copies (macOS APFS). |
time_type | string | No | "0" | Which time to return for entries. One of "mtime", "atime", "btime", "ctime". |
Example
Section titled “Example”curl -X POST "https://api.hoody.com/api/v1/backends/local" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "description": "Local archive drive", "no_preallocate": true, "one_file_system": true }'const backend = await client.files.backends.connectLocal({ data: { description: "Local archive drive", no_preallocate: true, one_file_system: true }});Responses
Section titled “Responses”{ "success": true, "message": "Backend connected successfully", "data": { "id": "bnd_550e8400e29b41d4a716446655440015", "type": "local", "backend_type": "local", "mount_paths": [] }}{ "success": false, "error": "Invalid time_type: must be one of mtime, atime, btime, ctime"}POST /api/v1/backends/memory
Section titled “POST /api/v1/backends/memory”Expose an in-memory object store as a Hoody backend. Data is lost when the server restarts.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”| Field | Type | Required | Default | Description |
|---|---|---|---|---|
description | string | No | "" | Human-readable description of the remote. |
Example
Section titled “Example”curl -X POST "https://api.hoody.com/api/v1/backends/memory" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "description": "Ephemeral scratch space" }'const backend = await client.files.backends.connectMemory({ data: { description: "Ephemeral scratch space" }});Responses
Section titled “Responses”{ "success": true, "message": "Backend connected successfully", "data": { "id": "bnd_550e8400e29b41d4a716446655440016", "type": "memory", "backend_type": "memory", "mount_paths": [] }}{ "success": false, "error": "Failed to initialize memory backend"}POST /api/v1/backends/union
Section titled “POST /api/v1/backends/union”Merge the contents of several upstream filesystems into a single union view, with configurable policies for searches, creates, and actions.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”| Field | Type | Required | Default | Description |
|---|---|---|---|---|
upstreams | string | Yes | "" | Space-separated list of upstreams, e.g. "upstreama:test/dir upstreamb:". Quotes support embedded spaces, e.g. "upstreama:test/space:ro dir". |
description | string | No | "" | Human-readable description of the remote. |
search_policy | string | No | "ff" | Policy used to choose an upstream on SEARCH operations. |
create_policy | string | No | "epmfs" | Policy used to choose an upstream on CREATE operations. |
action_policy | string | No | "epall" | Policy used to choose an upstream on ACTION operations. |
cache_time | integer | No | 120 | How long (in seconds) to cache usage and free-space information. Only useful with path-preserving policies. |
min_free_space | string | No | "1073741824" | Minimum free space required for a remote to be considered by lfs/eplfs policies. |
Example
Section titled “Example”curl -X POST "https://api.hoody.com/api/v1/backends/union" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "upstreams": "drive-a:photos drive-b:videos drive-c:documents", "description": "Combined drive view", "create_policy": "epmfs" }'const backend = await client.files.backends.connectUnion({ data: { upstreams: "drive-a:photos drive-b:videos drive-c:documents", description: "Combined drive view", create_policy: "epmfs" }});Responses
Section titled “Responses”{ "success": true, "message": "Backend connected successfully", "data": { "id": "bnd_550e8400e29b41d4a716446655440017", "type": "union", "backend_type": "union", "mount_paths": [] }}{ "success": false, "error": "upstreams must contain at least one remote"}