Read files
Read file contents over HTTP:
GET /api/v1/files/{path}?backend={id}This works for text, binary, and any other file type.
hoody-files connects 49 remote storage backends, including Google Drive, Dropbox, and S3, through a single HTTP API. It is a hoody-kit service rather than a Foundation feature, but cloud mounting is documented here with the rest of container storage.
Every admitted backend is a network service. The container’s own filesystem is not one of them: hoody-files serves local files natively over the same API, and the backend allowlist deliberately excludes rclone’s filesystem and wrapping types so that a connected backend can never be re-pointed at the container’s disk.
Every storage backend hoody-files supports is both HTTP-accessible and POSIX-mountable. You can read Google Drive through the HTTP API, or mount it and treat it as a local directory.
Complete hoody-files documentation:
Overview:
Mounting cloud storage:
File operations:
Backend management:
hoody-files turns a cloud storage API into HTTP endpoints and into a mountable filesystem.
Google Drive API (OAuth, MIME types, pagination complexity) ↓ hoody-files abstraction ↓ ┌─────────────────┐ │ HTTP API │ ← curl, fetch, any HTTP client │ POSIX Mount │ ← ls, cp, cat, any filesystem tool └─────────────────┘Every backend is reachable two ways:
ls, cp, cat, and other standard tools work on it.The file-access interface is the same for every backend.
# Instead of configuring each backend separately:# - Google Drive API (OAuth, pagination, MIME types)# - Dropbox API (cursors, batching, webhooks)# - S3 API (buckets, keys, multipart uploads)# ... and the rest
# One unified HTTP API:GET /api/v1/files/{path}?backend={backend_id}
# Works identically for Google Drive, S3, Dropbox, OneDrive, etc.The full backend list is further down this page.
Prerequisite: OAuth credentials. OAuth-backed providers (Google Drive, Dropbox, OneDrive, Box, and so on) accept a
client_id/client_secretfrom the provider console plus atokenJSON blob from an OAuth consent flow. The client id and secret can be left out for providers that offer a shared client, but they do not replace authorization: you still supply atoken(or another supported credential). Google Drive’s shared client is being retired during 2026, so use your own client there; for other providers, your own client is also recommended for full quota. The CLI is not interactive; pass the values as flags. For your own OAuth client, runhoody files backends drive create -c <container-id> --client-id <id> --client-secret <secret>. To supply a pre-obtained provider token, add--tokento that same command:hoody files backends drive create -c <container-id> --token '{"access_token":"ya29..."}'. Keep it after the subcommand, where it names the provider’s token; a--tokentyped before the subcommand is the Hoody API bearer instead, and sending a provider token there hands it to the wrong host. The CLI prints only a success message and does not return the backend id, so runhoody files backends list -c <container-id> -o jsonafterwards to read it. Key/secret backends (S3, B2, etc.) need the provider’s access-key pair instead.
Connect a storage provider. The connection persists, so you configure a provider once and reuse it:
curl -X POST "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/backends/drive" \ -H "Content-Type: application/json" \ -d '{ "client_id": "your-app.apps.googleusercontent.com", "client_secret": "your-secret", "token": "{\"access_token\":\"ya29...\"}" }'
# 201 Response: {"success": true, "message": "drive backend connected successfully", "data": {"id": "9b4d3e2f5a6c7890", "type": "drive", "vfs_backend_type": "drive", "config": {"client_id": "***", "client_secret": "***", "token": "***"}, "mount_paths": []}}curl -X POST "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/backends/dropbox" \ -H "Content-Type: application/json" \ -d '{ "client_id": "your-client-id", "client_secret": "your-secret", "token": "{\"access_token\":\"sl.B...\"}" }'
# 201 Response: {"success": true, "message": "dropbox backend connected successfully", "data": {"id": "0c5e4f3a6b7d8901", "type": "dropbox", "vfs_backend_type": "dropbox", "config": {"client_id": "***", "client_secret": "***", "token": "***"}, "mount_paths": []}}curl -X POST "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/backends/s3" \ -H "Content-Type: application/json" \ -d '{ "provider": "AWS", "access_key_id": "AKIAIOSFODNN7EXAMPLE", "secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "region": "us-east-1", "endpoint": "s3.us-east-1.amazonaws.com" }'
# 201 Response: {"success": true, "message": "s3 backend connected successfully", "data": {"id": "1a2b3c4d5e6f7080", "type": "s3", "vfs_backend_type": "s3", "config": {"provider": "AWS", "region": "us-east-1", "endpoint": "s3.us-east-1.amazonaws.com", "access_key_id": "***", "secret_access_key": "***"}, "mount_paths": []}}Every connected backend answers the same requests, whether it is Google Drive, S3, or SFTP:
# Read file from any connected backendhoody files get "Documents/report.pdf" --backend 9b4d3e2f5a6c7890 -c <container-id>
# List directory on a backend (get lists directory paths too)hoody files get "my-bucket/Documents/" --backend 1a2b3c4d5e6f7080 -c <container-id>// Read from Google Drive: the same call for every backendconst driveFile = await client.files.get('Documents/report.pdf', { backend: '9b4d3e2f5a6c7890'});
// Read from S3: the backend changes, and the path starts with the bucket nameconst s3File = await client.files.get('my-bucket/Documents/report.pdf', { backend: '1a2b3c4d5e6f7080'});# Read from Google Drivecurl "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/files/Documents/report.pdf?backend=9b4d3e2f5a6c7890"
# Read from Dropbox: same endpoint, different backendcurl "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/files/Documents/report.pdf?backend=0c5e4f3a6b7d8901"
# Read from S3: same API, different backend (S3 paths start with the bucket name)curl "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/files/my-bucket/Documents/report.pdf?backend=1a2b3c4d5e6f7080"One request, one link
cURL runs inside your container and can wrap any HTTP request into a single GET URL. The call stops being something you need a client for and becomes something you can paste into a browser, send in a chat, bookmark, schedule with cron, or drop into a no-code tool.
Nothing is installed on the machine that opens it. The link does carry whatever credentials the call needs, so treat it as you would treat those credentials.
Slashes, colons and braces pass through as they are. The one character you must
encode is an & inside a value, which happens when the wrapped URL
carries its own query string. Left raw it ends the value early, and the rest is
read as cURL's own parameters, so you get a 200 on a request you did
not make.
How the wrapping works Chaining calls into one link Turning a link into a shortcut
Reads the same file from three connected backends. The backend id changes, and the S3 path starts with the bucket name.
# Google Drive
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://PROJECT_ID-CONTAINER_ID-files-1.SERVER.containers.hoody.com/api/v1/files/Documents/report.pdf?backend=9b4d3e2f5a6c7890&method=GET&response=transparent
# Dropbox
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://PROJECT_ID-CONTAINER_ID-files-1.SERVER.containers.hoody.com/api/v1/files/Documents/report.pdf?backend=0c5e4f3a6b7d8901&method=GET&response=transparent
# S3
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://PROJECT_ID-CONTAINER_ID-files-1.SERVER.containers.hoody.com/api/v1/files/my-bucket/Documents/report.pdf?backend=1a2b3c4d5e6f7080&method=GET&response=transparent Major cloud storage (9)
Object storage (10)
s3 backend, provider selects the flavor)File protocols (5)
File sharing services (8)
Enterprise services (7)
Specialty services (6)
Legacy and specialized (4)
Total: 49 backend types
Storj is not available: connecting it returns 400. A saved Storj backend is kept but not connected, and its mounts come back with status error; both can be deleted.
Git repositories are not supported as remote backends, and ?type=git requests are rejected.
rclone ships filesystem, caching, and wrapping types that hoody-files refuses to instantiate: local, alias, cache, chunker, combine, compress, crypt, hasher, memory, and union. Every one of them either reads and writes the container filesystem directly or re-points at another remote, which takes the backend outside the scope hoody-files is meant to serve. A request naming one is rejected before any connection is made or reused.
That is a limit on rclone backends, not on local file access. The container’s own filesystem is served natively by hoody-files over the same HTTP API, without a backend at all: see Container Storage.
Connection parameters are filtered too. A backend’s config cannot carry keys that make rclone read a local credential source or pivot to ambient host credentials, so credential-file options such as service_account_file, box_config_file, key_file, config_file and shared_credentials_file are rejected, and so are ambient-auth flags such as env_auth when enabled (false is accepted). Pass credentials by value instead. Keys that are not plain option names (such as global.*), rclone’s config_* setup keys, Kerberos settings (kerberos_ccache, service_principal_name, and use_kerberos when true), Azure connection_string and WebDAV unix_socket are refused as well, and every value must be a string, number, boolean or null.
For detailed configuration parameters, see:
Without an abstraction layer, an application implements each provider’s own API:
// Google Drive: OAuth, MIME types, fileId abstractionconst drive = google.drive({version: 'v3', auth});const file = await drive.files.get({fileId: 'abc123', alt: 'media'});
// Dropbox: different auth, different structureconst dbx = new Dropbox({accessToken: TOKEN});const file = await dbx.filesDownload({path: '/folder/file.pdf'});
// S3: buckets, keys, and regions instead of pathsconst s3 = new AWS.S3();const file = await s3.getObject({Bucket: 'my-bucket', Key: 'folder/file.pdf'}).promise();
// Without the unified layer, provider and backend integrations require separate implementationshoody-files collapses those into one request shape:
# Same API for every backend - just change backend parametercurl "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/files/folder/file.pdf?backend={backend_id}"
# Works for Google Drive, Dropbox, S3, OneDrive, Box, SFTP, WebDAV, etc.You learn one file API for every backend. Application code names a backend id rather than a provider SDK, so moving from S3 to Dropbox changes the backend id and the path layout (S3 paths start with the bucket name). hoody-files translates each HTTP call into the provider’s own protocol and returns errors as an HTTP status with a message. Which status you see depends on where the call failed (for example 502 for a connection failure, 404 when the path is not found).
Connected backends share the same API, but the operations available depend on the provider and its credentials. HTTP/HTTPS backends are read-only; uploads, deletes, and directory creation are refused.
Read files
Read file contents over HTTP:
GET /api/v1/files/{path}?backend={id}This works for text, binary, and any other file type.
Verify a file hash
Get a file’s SHA256 hash as JSON ({"hash": "<hex digest>", "algorithm": "sha256"}):
GET /api/v1/files/{path}?backend={id}&hashCompare the hash field against your local digest to check integrity.
List directories
Browse directories as JSON:
GET /api/v1/files/{path}/?backend={id}Remote listings come back unsorted and unfiltered; sort and filter the returned entries in your application.
Read file metadata
Remote ?backend= requests do not support &stat, and sending it downloads the file. List the parent directory instead and read each entry’s size, directory flag, and modified time:
GET /api/v1/files/{parent-path}/?backend={id}See the Hoody Files API for the complete set of operations.
The three requests below connect the backend, list the root directory, and read a file. After that, every file in the account is reachable over HTTP.
# Connect Google Drivecurl -X POST "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/backends/drive" \ -H "Content-Type: application/json" \ -d '{ "client_id": "your-app.apps.googleusercontent.com", "client_secret": "your-client-secret", "token": "{\"access_token\":\"ya29.a0...\"}" }'
# Response includes backend ID{ "success": true, "message": "drive backend connected successfully", "data": { "id": "9b4d3e2f5a6c7890", "type": "drive", "vfs_backend_type": "drive", "config": {"client_id": "***", "client_secret": "***", "token": "***"}, "mount_paths": [] }}# List root directorycurl "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/files/?backend=9b4d3e2f5a6c7890"
# Response: JSON with the directory's entries (modified is Unix seconds){ "path": "", "entries": [ {"name": "Documents", "is_dir": true, "size": 0, "modified": 1716390000}, {"name": "Photos", "is_dir": true, "size": 0, "modified": 1716380000}, {"name": "report.pdf", "is_dir": false, "size": 1048576, "modified": 1716300000} ], "count": 3}# Read file contentcurl "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/files/Documents/report.pdf?backend=9b4d3e2f5a6c7890" \ > report.pdf
# File downloaded from Google Drive via HTTPOne application can read from several providers at once. This pattern covers backup verification, data migration, and multi-cloud setups:
# Connect all your storagePOST /api/v1/backends/drive → backend_drive_abcPOST /api/v1/backends/dropbox → backend_dropbox_xyzPOST /api/v1/backends/s3 → backend_s3_def
# Now access any file from any backendGET /api/v1/files/project-data.json?backend=backend_drive_abcGET /api/v1/files/project-data.json?backend=backend_dropbox_xyzGET /api/v1/files/my-bucket/project-data.json?backend=backend_s3_def
# Same API, different storage providersA container can push its own backups to a cloud provider:
# Upload container backup to Google Drivetar czf backup.tar.gz /hoody/storage/myapp/
curl -X PUT "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/files/Backups/backup.tar.gz?backend=backend_drive_abc" \ --data-binary "@backup.tar.gz"
# Hoody Files handles OAuth, chunking, retriesRead from the cloud, process in the container, write the results back:
# Download dataset from S3curl "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/files/my-bucket/datasets/data.csv?backend=backend_s3_abc" > data.csv
# Process locallypython process.py data.csv > results.json
# Upload results to Dropboxcurl -X PUT "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/files/Results/results.json?backend=backend_dropbox_xyz" \ --data-binary "@results.json"
# No cloud SDK involved; the transfers are plain HTTPCloud storage files can be exposed through a container URL:
# Connect S3 (the bucket is addressed in the path, e.g. my-public-files/images/logo.png)POST /api/v1/backends/s3 {"provider": "AWS", "access_key_id": "AKIA...", "secret_access_key": "...", "region": "us-east-1", "endpoint": "s3.us-east-1.amazonaws.com"}
# Now serve files via Hoody ProxyGET https://{project}-{container}-files.../api/v1/files/my-public-files/images/logo.png?backend=backend_s3_abc
# S3 objects are now served over HTTPConsumer cloud services that authenticate with OAuth:
See the Cloud Storage API for mounting instructions.
Object storage backends include:
See the Object Storage API for provider-specific configuration. AWS and S3-compatible stores use the s3 backend with a provider value; Azure Blob, Google Cloud Storage, and Backblaze B2 have dedicated backends of their own.
Servers reachable over standard file protocols:
See the File Protocols API for protocol mounting.
The endpoint and query parameters are the same for every provider. Remote paths follow the provider’s own layout; for example, S3 paths begin with the bucket name.
# List a directory (identical for every backend)hoody files get "folder/" --backend $BACKEND_ID -c <container-id>
# Read a file (identical for every backend)hoody files get "folder/file.txt" --backend $BACKEND_ID -c <container-id>// List a directory (identical for every backend)// (files.get returns the JSON listing for a directory path)const listing = await client.files.get('folder/', { backend: BACKEND_ID});
// Read a file (identical for every backend)const file = await client.files.get('folder/file.txt', { backend: BACKEND_ID});# List a directory (identical for every backend)curl "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/files/folder/?backend=$BACKEND_ID"
# Read a file (identical for every backend)curl "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/files/folder/file.txt?backend=$BACKEND_ID"One request, one link
cURL runs inside your container and can wrap any HTTP request into a single GET URL. The call stops being something you need a client for and becomes something you can paste into a browser, send in a chat, bookmark, schedule with cron, or drop into a no-code tool.
Nothing is installed on the machine that opens it. The link does carry whatever credentials the call needs, so treat it as you would treat those credentials.
Slashes, colons and braces pass through as they are. The one character you must
encode is an & inside a value, which happens when the wrapped URL
carries its own query string. Left raw it ends the value early, and the rest is
read as cURL's own parameters, so you get a 200 on a request you did
not make.
How the wrapping works Chaining calls into one link Turning a link into a shortcut
Lists a folder and reads a file — the same two links work for any connected backend.
# List a directory
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://PROJECT_ID-CONTAINER_ID-files-1.SERVER.containers.hoody.com/api/v1/files/folder/?backend=BACKEND_ID&method=GET&response=transparent
# Read a file
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://PROJECT_ID-CONTAINER_ID-files-1.SERVER.containers.hoody.com/api/v1/files/folder/file.txt?backend=BACKEND_ID&method=GET&response=transparent Switching provider changes the backend id and the provider-specific path:
// Change from S3 to Dropboxlet backend = 'backend_s3_abc';let path = 'my-bucket/data.json'; // S3 paths start with the bucket name
backend = 'backend_dropbox_xyz';path = 'data.json';
const file = await fetch(`/api/v1/files/${path}?backend=${backend}`);A container can hold several backends at the same time. Active shared connections are capped at 100 in total and 10 per remote host by default (for cloud backends without a host setting, the per-host cap applies per backend type). Connecting past the cap is refused:
# Personal Google Drivebackend_drive_personal
# Work Google Drivebackend_drive_work
# AWS S3 productionbackend_s3_prod
# AWS S3 backupsbackend_s3_backup
# Dropbox archivebackend_dropbox_archive
# All accessible from one container via hoody-filesThe backend can be a runtime choice rather than a build-time one:
// User chooses storage providerconst backend = userPreference; // 'backend_drive_abc' or 'backend_s3_xyz'
// App code doesn't care which providerasync function saveFile(path, content, backend) { return fetch(`/api/v1/files/${path}?backend=${backend}`, { method: 'PUT', body: content });}
// Works with Google Drive, S3, Dropbox, OneDrive, etc.Copy a file between two providers. Run these commands inside the container: the file is downloaded to a temporary copy there and uploaded from it to the destination provider. The source file stays in place.
# Read from Google Drivecurl "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/files/backup.zip?backend=backend_drive_abc" \ > backup.zip
# Upload to S3curl -X PUT "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/files/my-bucket/backup.zip?backend=backend_s3_xyz" \ --data-binary "@backup.zip"
# Both transfers use the container's bandwidth, not your own machine'srm backup.zipWrite the same backup to several providers for redundancy:
# Create backuptar czf critical-data.tar.gz /hoody/storage/production/
# Upload to 3 different cloud providers# Each destination has its own bucket (S3, GCS, B2 paths start with the bucket name)for destination in \ "backend_s3_primary:my-s3-bucket/Backups/critical-data.tar.gz" \ "backend_gcs_backup:my-gcs-bucket/Backups/critical-data.tar.gz" \ "backend_backblaze_archive:my-b2-bucket/Backups/critical-data.tar.gz"; do backend=${destination%%:*} path=${destination#*:} curl --fail-with-body -X PUT "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/files/$path?backend=$backend" \ --data-binary "@critical-data.tar.gz"done
# Same data in 3 locations (AWS, Google, Backblaze)# A successful PUT means the file was accepted. For a cached backend the upload to# the provider can finish afterwards, and a failure there is not reported in the response.The same three stages written out step by step:
# 1. Download dataset from Dropboxcurl "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/files/datasets/raw-data.csv?backend=backend_dropbox_abc" \ > raw-data.csv
# 2. Process in containerpython analyze.py raw-data.csv > analysis-results.json
# 3. Upload results to Google Drivecurl -X PUT "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.com/api/v1/files/Results/analysis-results.json?backend=backend_drive_xyz" \ --data-binary "@analysis-results.json"
# Cloud → Container → Cloud (all via HTTP)Consumer OAuth requires a person to complete the consent flow. Service account credentials do not expire with a user session, so scheduled tasks keep working.
Pass the credential by value, not by path. Config keys that point rclone at a file on disk are rejected, service_account_file and box_config_file among them, because a backend must never be able to read the container’s filesystem for credentials. Enabled env_auth and the other flags that pivot to ambient host credentials are rejected too.
# Google Drive: service account, with the key material sent inline{ "service_account_credentials": "{\"type\":\"service_account\", ...}", "team_drive": "0ABC123xyz"}Cloud Storage API lists the parameter each provider accepts for an inline credential.
Authorize the credential for read-only access when you obtain its OAuth token, then connect with that token. For Google Drive, request only the drive.readonly scope in the consent flow:
# Google Drive: a token issued for drive.readonly only{ "token": "{\"access_token\":\"ya29...\",\"token_type\":\"Bearer\"}"}
# Prevents accidental modifications# Limits damage if credentials compromisedSetting scope on the connect request does not remove permissions from a token that was already issued with more access.
A backend connection survives container restarts:
# Connect backend oncePOST /api/v1/backends/drive → backend_drive_abc
# Use in all future requestsGET /api/v1/files/data.json?backend=backend_drive_abc
# The saved backend configuration stays until you delete it.# Idle connections can be released and are recreated on the next request.# Verify connection worksGET /api/v1/files/?backend=backend_drive_abc
# Should return directory listing# If error: OAuth expired, credentials wrong, or network issueSee Managing Backends for testing and troubleshooting.
rclone’s crypt wrapper is not an admitted backend type, and neither are the other wrapping types, so there is no POST /api/v1/backends/crypt. Encrypt the file in the container and upload the ciphertext, or use a provider that encrypts on its own side, such as Proton Drive.
No. A download through /api/v1/files/{path}?backend={id} streams the file as it arrives from the provider, so memory use stays small whatever the file size. A single byte range (Range: bytes=...) answers 206 Partial Content with just those bytes. A request whose ranges leave more than one that fits inside the file gets the whole file (200). So does one sent with If-Range, because a cloud file’s ETag is weak and can’t prove the file is unchanged. ?hash reads the whole file before it answers, and ?base64 holds the file in memory and accepts files up to 50 MiB.
Yes. hoody-files also provides:
Cloud mounting is optional. hoody-files works the same way for local storage.
Requests through the backend usually fail with a backend connection error (502) or a not-found error (404) rather than a 401, so check the error message. If the token includes refresh credentials, access tokens can refresh automatically. Otherwise, replace the token with PUT /api/v1/backends/{id} once no mount is using the backend, or disconnect and reconnect it (disconnecting also removes the backend’s mounts):
# Disconnect expired backendDELETE /api/v1/backends/backend_drive_abc
# Reconnect with fresh OAuth tokenPOST /api/v1/backends/drive{"client_id": "...", "client_secret": "...", "token": "{\"access_token\":\"NEW_TOKEN\"}"}Yes. Personal and work Google Drive accounts can both be connected:
POST /api/v1/backends/drive {"token": "{\"access_token\":\"PERSONAL_ACCESS_TOKEN\",\"token_type\":\"Bearer\"}"} → backend_drive_personalPOST /api/v1/backends/drive {"token": "{\"access_token\":\"WORK_ACCESS_TOKEN\",\"token_type\":\"Bearer\"}"} → backend_drive_work
# Access both simultaneouslyGET /api/v1/files/data.json?backend=backend_drive_personalGET /api/v1/files/data.json?backend=backend_drive_workYes, with HTTP PUT. Send the file body to PUT /api/v1/files/{path}?backend={id}. The root path /{path} does not take ?backend=; it addresses the local filesystem or a one-off remote given by server and type parameters. See Hoody Files API for upload endpoints.
Transfers: run over the provider’s own transport, so the transfer itself is close to native speed. A remote read starts responding as soon as the first bytes arrive from the provider. Some backends (notably Google Drive) use HTTP/1.1 rather than HTTP/2 by default, which adds a little per-request overhead. Other backends keep HTTP/2 enabled.
Batch operations: slightly slower, because each request carries HTTP overhead.
High throughput: run the cloud provider’s own SDK inside the container. hoody-files trades maximum throughput for a single interface.
Problem: POST /api/v1/backends/{provider} returns error
Solutions:
OAuth token issues:
Credential errors:
Network connectivity:
# Test from containercurl https://www.googleapis.com # Google Drivecurl https://api.dropboxapi.com # Dropbox
# Should return response (not timeout)Problem: GET /api/v1/files/?backend={id} returns empty or incomplete
Check:
Path is correct:
# Root directoryGET /api/v1/files/?backend={id}
# Specific folderGET /api/v1/files/Documents/?backend={id}Backend has data:
Permissions:
Problem: the cloud provider is throttling requests. This does not always arrive as HTTP 429, so check the error message as well as the status.
Solutions:
Implement exponential backoff:
async function fetchWithRetry(url, retries = 3) { for (let i = 0; i < retries; i++) { const response = await fetch(url); if (response.status < 500 && response.status !== 429) return response; await sleep(Math.pow(2, i) * 1000); // 1s, 2s, 4s }}Cache in your own code. rclone’s cache backend is not admitted, so there is no POST /api/v1/backends/cache. Hold directory listings and hot files in the container and re-read them from there.
Reduce request frequency:
Storage pages:
hoody-files reference: