Read Files
Stream file contents via HTTP:
GET /api/v1/files/{path}?backend={id}Works for text, binary, any file type.
Connect your container to Google Drive, Dropbox, S3, and 63 cloud providers through a single HTTP API. This capability is powered by hoody-kit’s hoody-files service—not a Foundation-level feature, but so essential we document it here.
The magic: hoody-files makes ANY storage backend both HTTP-accessible AND POSIX-mountable. Access Google Drive via HTTP API or mount it like a local directory—your choice.
Complete hoody-files documentation:
Overview:
Mounting Cloud Storage:
File Operations:
Backend Management:
hoody-files transforms cloud storage APIs into HTTP endpoints AND mountable filesystems.
The transformation:
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 └─────────────────┘ANY backend becomes:
The core idea:
# Instead of learning 63 different APIs:# - Google Drive API (OAuth, pagination, MIME types)# - Dropbox API (cursors, batching, webhooks)# - S3 API (buckets, keys, multipart uploads)# ... 60 more APIs
# Use ONE unified HTTP API:GET /api/v1/files/{path}?backend={backend_id}
# Works identically for Google Drive, S3, Dropbox, OneDrive, etc.One API to access 63 providers:
See the complete expandable list below for all supported providers.
Prerequisite — OAuth credentials. OAuth-backed providers (Google Drive, Dropbox, OneDrive, Box, …) need a
client_id/client_secretfrom the provider console and an initialtokenfrom an OAuth consent flow. Either runhoody files backends connect drive(interactive — opens a browser, completes the flow, and captures the token for you), or paste pre-minted values as shown below. Key/secret backends (S3, B2, etc.) need the provider’s access-key pair instead.
Mount a storage provider:
curl -X POST "http://localhost:5000/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...\"}" }'
# Response: {"id": "backend_drive_abc123"}curl -X POST "http://localhost:5000/api/v1/backends/dropbox" \ -H "Content-Type: application/json" \ -d '{ "client_id": "your-client-id", "client_secret": "your-secret", "token": "{\"access_token\":\"sl.B...\"}" }'
# Response: {"id": "backend_dropbox_xyz789"}curl -X POST "http://localhost:5000/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" }'
# Response: {"id": "backend_s3_def456"}Backend connection is persistent. Configure once, use forever.
Now all backends use the same HTTP API:
# Read file from any connected backendhoody files get Documents/report.pdf --backend backend_drive_abc123
# List directory on a backend (get lists directory paths too)hoody files get Documents/ --backend backend_s3_def456const containerClient = await client.withContainer({ id: CONTAINER_ID, project_id: PROJECT_ID, server: SERVER});
// Read from Google Drive — same API for all 63 providersconst driveFile = await containerClient.files.get( 'Documents/report.pdf', { backend: 'backend_drive_abc123' });
// Read from S3 — just change the backend parameterconst s3File = await containerClient.files.get( 'Documents/report.pdf', { backend: 'backend_s3_def456' });# Read from Google Drivecurl "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.icu/api/v1/files/Documents/report.pdf?backend=backend_drive_abc123"
# Read from Dropbox — same endpoint, different backendcurl "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.icu/api/v1/files/Documents/report.pdf?backend=backend_dropbox_xyz789"
# Read from S3 — same API, different backendscurl "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.icu/api/v1/files/Documents/report.pdf?backend=backend_s3_def456"The abstraction is complete: Whether it’s Google Drive, S3, or SFTP—the HTTP API is identical.
Major Cloud Storage (9)
Object Storage (12)
s3 backend, provider selects the flavor)File Protocols (5)
File Sharing Services (9)
Enterprise Services (7)
Specialty Services (7)
fetch-from-git file operation, not a backends/ mount)Utility/Virtual Backends (10)
Legacy/Specialized (4)
Total: 63 backend types
For detailed configuration parameters, see:
Traditional approach requires learning and implementing each provider’s unique 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: Completely different paradigm (buckets, keys, regions)const s3 = new AWS.S3();const file = await s3.getObject({Bucket: 'my-bucket', Key: 'folder/file.pdf'}).promise();
// You need 63 different implementationshoody-files abstracts everything to simple HTTP:
# Same API for all 63 providers - just change backend parametercurl "http://localhost:5000/api/v1/files/folder/file.pdf?backend={backend_id}"
# Works for Google Drive, Dropbox, S3, OneDrive, Box, SFTP, WebDAV, etc.Benefits:
What you can do with any connected backend:
Read Files
Stream file contents via HTTP:
GET /api/v1/files/{path}?backend={id}Works for text, binary, any file type.
Download Files
Download with SHA256 verification:
GET /api/v1/files/{path}?backend={id}&hashIntegrity checked automatically.
List Directories
Browse directories with JSON/HTML output:
GET /api/v1/files/{path}/?backend={id}&jsonSortable, filterable directory listings.
File Metadata
Get size, type, modified time:
HEAD /api/v1/files/{path}?backend={id}Lightweight metadata queries.
See: Hoody Files API for complete operations.
Connect Google Drive and access files:
# Connect Google Drivecurl -X POST "http://localhost:5000/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, "data": { "id": "backend_drive_abc123", "type": "drive" }}# List root directorycurl "http://localhost:5000/api/v1/files/?backend=backend_drive_abc123&json"
# Response: JSON object with an entries array{ "path": "/", "entries": [ {"name": "Documents", "is_dir": true, "size": 0}, {"name": "Photos", "is_dir": true, "size": 0}, {"name": "report.pdf", "is_dir": false, "size": 1048576} ], "count": 3}# Read file contentcurl "http://localhost:5000/api/v1/files/Documents/report.pdf?backend=backend_drive_abc123" \ > report.pdf
# File downloaded from Google Drive via HTTPTotal setup time: 1 minute. Now you have HTTP access to all your Google Drive files.
Access files from multiple cloud providers in one application:
# Mount all your storagePOST /backends/drive → backend_drive_abcPOST /backends/dropbox → backend_dropbox_xyzPOST /backends/s3 → backend_s3_def
# Now access any file from any backendGET /files/project-data.json?backend=backend_drive_abcGET /files/project-data.json?backend=backend_dropbox_xyzGET /files/project-data.json?backend=backend_s3_def
# Same API, different storage providersPerfect for: Backup verification, data migration, multi-cloud strategies.
Automate backups from container to cloud storage:
# Upload container backup to Google Drivetar czf backup.tar.gz /hoody/storage/myapp/
curl -X PUT "http://localhost:5000/api/v1/files/Backups/backup.tar.gz?backend=backend_drive_abc" \ --data-binary "@backup.tar.gz"
# Hoody Files handles OAuth, chunking, retriesRead data from cloud, process in container, write results back:
# Download dataset from S3curl "http://localhost:5000/api/v1/files/datasets/data.csv?backend=backend_s3_abc" > data.csv
# Process locallypython process.py data.csv > results.json
# Upload results to Dropboxcurl -X PUT "http://localhost:5000/api/v1/files/Results/results.json?backend=backend_dropbox_xyz" \ --data-binary "@results.json"
# Zero cloud SDK integration - pure HTTPMake cloud storage files accessible via HTTP:
# Mount S3 bucketPOST /backends/s3 {"bucket": "my-public-files"}
# Now serve files via Hoody ProxyGET https://{project}-{container}-files.../api/v1/files/images/logo.png?backend=backend_s3_abc
# Cloud storage becomes HTTP file serverConsumer-focused cloud services with OAuth authentication:
See: Cloud Storage API for mounting instructions.
Developer-focused object storage with S3 API:
See: Object Storage API for S3 configuration.
Connect to any server supporting standard protocols:
See: File Protocols API for protocol mounting.
Same endpoints work for every provider:
# List directory — works for ALL backendshoody files get folder/ --backend $BACKEND_ID
# Read file — works for ALL backendshoody files get folder/file.txt --backend $BACKEND_ID// List directory — works for ALL backends// (files.get returns the JSON listing for a directory path)const listing = await containerClient.files.get( 'folder/', { backend: BACKEND_ID });
// Read file — works for ALL backendsconst file = await containerClient.files.get( 'folder/file.txt', { backend: BACKEND_ID });# List directory — works for ALL backendscurl "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.icu/api/v1/files/folder/?backend=$BACKEND_ID&json"
# Read file — works for ALL backendscurl "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.icu/api/v1/files/folder/file.txt?backend=$BACKEND_ID"
# Get metadata — works for ALL backendscurl -I "https://$PROJECT-$CONTAINER-files-1.$SERVER.containers.hoody.icu/api/v1/files/folder/file.txt?backend=$BACKEND_ID"Switch backends = change one parameter:
// Change from S3 to Dropboxconst backend = 'backend_s3_abc'; // S3const backend = 'backend_dropbox_xyz'; // Dropbox
// Rest of code unchangedconst file = await fetch(`/api/v1/files/data.json?backend=${backend}`);Connect unlimited backends simultaneously:
# 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-filesBuild apps that work with any storage:
// 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.Transfer files between providers without downloading locally:
# Read from Google Drivecurl "http://localhost:5000/api/v1/files/backup.zip?backend=backend_drive_abc" \ > backup.zip
# Upload to S3curl -X PUT "http://localhost:5000/api/v1/files/backup.zip?backend=backend_s3_xyz" \ --data-binary "@backup.zip"
# Transfer happens in container (fast server bandwidth)Store backups across multiple providers for redundancy:
# Create backuptar czf critical-data.tar.gz /hoody/storage/production/
# Upload to 3 different cloud providersfor backend in backend_s3_primary backend_gcs_backup backend_backblaze_archive; do curl -X PUT "http://localhost:5000/api/v1/files/Backups/critical-data.tar.gz?backend=$backend" \ --data-binary "@critical-data.tar.gz"done
# Same data in 3 locations (AWS, Google, Backblaze)Read from cloud, process, upload results:
# 1. Download dataset from Dropboxcurl "http://localhost:5000/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 "http://localhost:5000/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 user interaction. For automated systems, use service accounts:
# Google Drive: Service Account (no user interaction){ "service_account_file": "/keys/service-account.json", "team_drive": "0ABC123xyz"}
# Box: JWT Authentication{ "box_config_file": "/keys/box-config.json", "box_sub_type": "enterprise"}Credentials don’t expire with user sessions—perfect for scheduled tasks.
Minimize security risk:
# Google Drive: Read-only scope{ "scope": "drive.readonly"}
# Prevents accidental modifications# Limits damage if credentials compromisedBackend connection persists across container restarts:
# Connect backend oncePOST /backends/drive → backend_drive_abc
# Use in all future requestsGET /files/data.json?backend=backend_drive_abc
# Connection remains active until explicitly disconnected# 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.
Add zero-knowledge encryption to any backend:
# Mount encrypted wrapperPOST /backends/crypt{ "remote": "backend_drive_abc:/Encrypted", "password": "your-encryption-password"}
# Now files are encrypted before uploadSee: Encryption Layer for zero-knowledge encryption.
No. hoody-files streams content:
Yes! hoody-files also provides:
Cloud mounting is optional—hoody-files works for local and cloud storage.
Requests fail with 401 Unauthorized. Fix:
# 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! Example: Personal and work Google Drive accounts:
POST /backends/drive {"token": "PERSONAL_TOKEN"} → backend_drive_personalPOST /backends/drive {"token": "WORK_TOKEN"} → backend_drive_work
# Access both simultaneouslyGET /files/data.json?backend=backend_drive_personalGET /files/data.json?backend=backend_drive_workYes, via HTTP PUT. Send the file body with PUT /api/v1/files/{path}?backend={id} (or at the root /{path}). See Hoody Files API for upload endpoints.
Streaming: Near-native performance over the provider’s native transport. Some backends (notably Google Drive) disable HTTP/2 by default pending an upstream fix, and fall back to HTTP/1.1 — expect slightly more per-request overhead there; others keep HTTP/2 enabled.
Batch operations: Slightly slower (HTTP overhead per request)
For high-throughput: Use cloud provider’s native SDK inside container. Use hoody-files for convenience and abstraction, not maximum throughput.
Problem: POST /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 /files/?backend={id} returns empty or incomplete
Check:
Path is correct:
# Root directoryGET /files/?backend={id}
# Specific folderGET /files/Documents/?backend={id}Backend has data:
Permissions:
Problem: 429 Too Many Requests from cloud provider
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 !== 429) return response; await sleep(Math.pow(2, i) * 1000); // 1s, 2s, 4s }}Use caching:
# Mount cache layerPOST /backends/cache{"remote": "backend_drive_abc:", "chunk_size": "10M"}
# Repeated requests served from cacheReduce request frequency:
Storage ecosystem:
Deep dive into hoody-files:
Understanding gained:
63 cloud providers. One HTTP API. One POSIX interface. Any backend → HTTP-accessible AND locally mountable.
The abstraction layer that makes cloud storage feel local and local storage feel like APIs.