Source Container Decides WHAT
Share Creator Controls:
- Which directory to share (source_path)
- Access mode (readonly or readwrite)
- Who can access (1-to-1 or project-wide)
- When it expires (optional)
- Enable/disable anytime
Share directories from one container to othersβautomatically works across servers. Perfect for multi-service applications, team collaboration, and data exchange without duplicating files.
Complete Storage Shares API:
Creating & Managing Shares:
Receiving & Mounting Shares:
Storage shares use a two-party system:
Source Container Decides WHAT
Share Creator Controls:
Target Container Decides IF
Share Receiver Controls:
Key principle: Source controls WHAT. Target controls IF.
Share specific directory with ONE container:
# Create 1-to-1 container share (readonly)hoody storage create --container $SOURCE_ID \ --source-path "/hoody/storage/shared-assets" \ --target-container-id $TARGET_ID \ --mode readonly \ --description "Static assets for frontend container"const share = await client.api.storageShares.create( SOURCE_CONTAINER_ID, { source_path: '/hoody/storage/shared-assets', target_container_id: TARGET_CONTAINER_ID, mode: 'readonly', description: 'Static assets for frontend container' });console.log(share.data.id); // Share ID for mountingcurl -X POST "https://api.hoody.icu/api/v1/containers/$SOURCE_ID/storage/shares" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "source_path": "/hoody/storage/shared-assets", "target_container_id": "'$TARGET_ID'", "mode": "readonly", "description": "Static assets for frontend container" }' Use when:
Share directory with ALL containers in a project:
# Create project-wide share β all containers in project can accesshoody storage create --container $SOURCE_ID \ --source-path "/hoody/storage/config" \ --target-project-id $PROJECT_ID \ --mode readonly \ --description "Shared configuration for all services"const share = await client.api.storageShares.create( SOURCE_CONTAINER_ID, { source_path: '/hoody/storage/config', target_project_id: PROJECT_ID, mode: 'readonly', description: 'Shared configuration for all services' });// Every container in the project automatically mounts this sharecurl -X POST "https://api.hoody.icu/api/v1/containers/$SOURCE_ID/storage/shares" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "source_path": "/hoody/storage/config", "target_project_id": "'$PROJECT_ID'", "mode": "readonly", "description": "Shared configuration for all services" }' Every container in project automatically mounts this share (by default).
Use when:
{ "mode": "readonly"}Target containers can:
Perfect for:
{ "mode": "readwrite"}Target containers can:
Changes visible to:
Perfect for:
/hoody/databases/)# In source containermkdir -p /hoody/storage/team-assets
# Add filescp logo.png /hoody/storage/team-assets/cp styles.css /hoody/storage/team-assets/ Response includes share_id.
Share now accessible in /hoody/shares/{share_alias}/.
# In target containerls /hoody/shares/shared-assets/# logo.png styles.css
# Files from source container, accessible in targetcat /hoody/shares/shared-assets/styles.cssWhen a target container mounts a share, files appear at:
/hoody/shares/{share-alias}/Example:
# Source creates share with alias "config"POST /storage/shares{ "source_path": "/hoody/storage/app-config", "alias": "config"}
# Target mounts sharePATCH /storage/incoming/{share_id}/mount{"mount": true}
# Files now accessible at:/hoody/shares/config/βββ app.yamlβββ database.jsonβββ secrets.envThe alias becomes the mount point name.
Backend shares upload directory with multiple frontends:
# Backend Container (source)POST /containers/{backend_id}/storage/shares{ "source_path": "/hoody/storage/user-uploads", "target_project_id": "{project_id}", "mode": "readwrite", "alias": "uploads"}
# Frontend Container 1 (accepts)PATCH /containers/{frontend_1}/storage/incoming/{share_id}/mount{"mount": true}
# Frontend Container 2 (accepts)PATCH /containers/{frontend_2}/storage/incoming/{share_id}/mount{"mount": true}
# Now all three containers see same /hoody/shares/uploads/# Upload from any frontend β visible to backend and other frontendsConfig container shares settings with all services (readonly):
# Config ContainerPOST /containers/{config_id}/storage/shares{ "source_path": "/hoody/storage/production-config", "target_project_id": "{project_id}", "mode": "readonly", "alias": "config"}
# All service containers mount it# Services read from /hoody/shares/config/# Only config container can update (others readonly)Developers share workspace between containers:
# Developer A's containerPOST /containers/{dev_a}/storage/shares{ "source_path": "/home/user/project", "target_container_id": "{dev_b_container}", "mode": "readwrite", "alias": "shared-project"}
# Developer B mounts in their container# Both edit files in real-time# Changes sync instantlyServices share logs with monitoring container (readonly):
# Service 1POST /storage/shares{ "source_path": "/hoody/storage/service1/logs", "target_container_id": "{monitor_container}", "mode": "readonly"}
# Service 2POST /storage/shares{ "source_path": "/hoody/storage/service2/logs", "target_container_id": "{monitor_container}", "mode": "readonly"}
# Monitor container sees all logs/hoody/shares/service1-logs//hoody/shares/service2-logs/Shares progress through these statuses:
| Status | Description | Next Steps |
|---|---|---|
active | Successfully mounted in target | In use |
failed | Mount failed (see status_message) | Check errors, fix, retry |
Check status:
GET /api/v1/containers/{id}/storage/shares/{share_id}
# Response includes: "status": "active"Source container can disable without deleting:
# Disable share temporarilyPATCH /api/v1/containers/{source_id}/storage/shares/{share_id}{ "enabled": false, "description": "Temporarily disabled for maintenance"}
# Files unmount from target containers# Share configuration preserved
# Re-enable laterPATCH /api/v1/containers/{source_id}/storage/shares/{share_id}{ "enabled": true}Use for: Maintenance windows, testing, gradual rollouts.
Set automatic expiration:
POST /api/v1/containers/{id}/storage/shares{ "source_path": "/hoody/storage/temp-files", "target_container_id": "{target}", "mode": "readonly", "expires_at": 1735689600 # Unix timestamp: 2025-01-01}
# Share auto-unmounts and notifies before expiryPerfect for: Temporary access, demo environments, time-limited shares.
# Shares you created from specific containerhoody storage list --container $SOURCE_ID
# All shares you created (across all containers)hoody storage list-all// Shares from specific containerconst shares = await client.api.storageShares.list(SOURCE_CONTAINER_ID);console.log(shares.data); // Array of shares you created
// All shares across all containersconst allShares = await client.api.storageShares.listGlobal();# Shares you created from specific containercurl "https://api.hoody.icu/api/v1/containers/$SOURCE_ID/storage/shares" \ -H "Authorization: Bearer $TOKEN"
# All shares you created (across all containers)curl "https://api.hoody.icu/api/v1/storage/shares" \ -H "Authorization: Bearer $TOKEN"# Incoming shares for specific containerhoody storage get-incoming-shares $TARGET_ID
# All incoming shares (all your containers)hoody storage list-all-incoming// Incoming shares for specific containerconst incoming = await client.api.storageShares.listIncoming(TARGET_CONTAINER_ID);console.log(incoming.data); // Shares offered to this container
// All incoming shares across all containersconst allIncoming = await client.api.storageShares.listIncomingGlobal();# Incoming shares for specific containercurl "https://api.hoody.icu/api/v1/containers/$TARGET_ID/storage/incoming" \ -H "Authorization: Bearer $TOKEN"
# All incoming shares (all your containers)curl "https://api.hoody.icu/api/v1/storage/incoming" \ -H "Authorization: Bearer $TOKEN"# Upgrade share from readonly to readwritehoody storage update $SOURCE_ID $SHARE_ID \ --mode readwrite --description "Now allows writes"await client.api.storageShares.update( SOURCE_CONTAINER_ID, SHARE_ID, { mode: 'readwrite', description: 'Now allows writes' });curl -X PATCH "https://api.hoody.icu/api/v1/containers/$SOURCE_ID/storage/shares/$SHARE_ID" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"mode": "readwrite", "description": "Now allows writes"}'Target containers must remount to get updated mode.
# Delete share β unmounts from all target containershoody storage delete $SHARE_IDawait client.api.storageShares.delete(SHARE_ID);// Unmounts from all target containers, configuration deleted permanentlycurl -X DELETE "https://api.hoody.icu/api/v1/storage/shares/$SHARE_ID" \ -H "Authorization: Bearer $TOKEN"Storage shares automatically work across different physical servers with full POSIX compliance:
βββββββββββββββββββββββββββββββββ Server US-West-1 ββ ββββββββββββββββββββββββ ββ β Source Container β ββ β /shared/data/ β ββ ββββββββββββββββββββββββ ββ β Hoody handles βββββββββββββββββββββββββββββββββ β cross-serverβββββββββββββββββββββββββββββββββ Server EU-Central-1 ββ ββββββββββββββββββββββββ ββ β Target Container β ββ β /hoody/shares/data/ β ββ ββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββWhat you get automatically:
Share remains mounted but inaccessible.
/hoody/shares/{alias}/Best practice: Donβt rely on shares from containers that stop frequently.
Yes! Create multiple shares from same source_path:
# Share /hoody/storage/assets with 3 containersPOST /storage/shares {"source_path": "/hoody/storage/assets", "target_container_id": "A"}POST /storage/shares {"source_path": "/hoody/storage/assets", "target_container_id": "B"}POST /storage/shares {"source_path": "/hoody/storage/assets", "target_container_id": "C"}
# Or share once to entire project (all containers see it)POST /storage/shares {"source_path": "/hoody/storage/assets", "target_project_id": "{project}"}Not recommended for SQLite databases. While you CAN share /hoody/databases/ directories, it bypasses the concurrent-write safety:
# β NOT Recommended: Sharing /hoody/databases via storage sharesPOST /storage/shares{ "source_path": "/hoody/databases", "mode": "readwrite"}
# Problem: Network-shared SQLite loses local-filesystem optimizations# Better: Each container uses /hoody/databases/ locally (same-server concurrent writes)# Better: Use hoody-sqlite HTTP API for cross-container database accessWhy not recommended:
/hoody/databases/ concurrent-write safety is optimized for local same-server accessBetter solutions:
/hoody/databases/ directly (concurrent-write-safe)Share remains available but unmounted. Target can accept later:
# Initially rejectPATCH /storage/incoming/{share_id}/mount{"mount": false}
# Accept later when neededPATCH /storage/incoming/{share_id}/mount{"mount": true}
# Files appear in /hoody/shares/{alias}/Yes. Share configuration and mount state survive:
Yes:
PATCH /api/v1/containers/{source_id}/storage/shares/{share_id}{ "alias": "new-alias"}Target containers must unmount and remount to use new alias. Old mount point /hoody/shares/old-alias/ becomes invalid.
Source container: Files count toward sourceβs storage.
Target containers: Mounted shares do NOT count toward targetβs storage quota (theyβre references, not copies).
Problem: Share status is "failed" with error message
Common causes:
Source path doesnβt exist:
# In source container, verify path existsls -la /hoody/storage/shared-path
# Create if missingmkdir -p /hoody/storage/shared-path
# Update share to retryPATCH /storage/shares/{id}{"enabled": true}Permission issues:
# Fix permissions in source containerchown -R root:root /hoody/storage/shared-pathchmod -R 755 /hoody/storage/shared-pathSource container stopped:
Problem: Share mounted but /hoody/shares/{alias}/ is empty
Solutions:
Verify share is active:
GET /api/v1/containers/{target_id}/storage/incoming# Check: "status": "active", "mount": trueCheck source container is running:
GET /api/v1/containers/{source_id}# Verify: "status": "running"Verify files exist in source:
# In source containerls /hoody/storage/shared-path# Should show filesUnmount and remount:
PATCH /storage/incoming/{share_id}/mount{"mount": false}
PATCH /storage/incoming/{share_id}/mount{"mount": true}Problem: Cannot access files in /hoody/shares/{alias}/
Cause: Source container restarted while target was accessing files
Solution:
# Unmount and remountPATCH /storage/incoming/{share_id}/mount{"mount": false}
PATCH /storage/incoming/{share_id}/mount{"mount": true}
# Or restart target container (auto-remounts)POST /api/v1/containers/{target_id}/restart# (Consolidated lifecycle route: POST /api/v1/containers/{id}/{operation}# where {operation} is one of: start | stop | force-stop | restart | pause | resume)Problem: PATCH /mount succeeds but files donβt appear
Check:
Share is enabled:
GET /storage/incoming/{share_id}# Verify: "enabled": trueShare not expired:
# Check expires_at (Unix timestamp)# If expired, ask source to extend or remove expirationTarget container has permission:
# β
Good: Specific subdirectory{"source_path": "/hoody/storage/assets"}
# β Risky: Entire Hoody Kit storage{"source_path": "/hoody/storage"}
# Sharing entire /hoody/storage exposes all service dataOne share for all containers:
# Instead of creating 10 identical 1-to-1 sharesPOST /storage/shares {"target_project_id": "{project}"}
# All containers in project can mount# Simpler management, one configurationMultiple writers can conflict:
# Container A writes /hoody/shares/data/file.txt# Container B writes /hoody/shares/data/file.txt (same file)
# Last write wins (potential data loss)Solution: Use application-level locking or coordinate writes (e.g., different directories per container).
Or use /hoody/databases/ for SQLite databases (automatic concurrent-write safety).
Share deletion unmounts from all targets:
# Snapshot source container firstPOST /api/v1/containers/{source_id}/snapshots{"alias": "before-share-deletion"}
# Then delete shareDELETE /api/v1/storage/shares/{share_id}# β
Clear documentation{ "description": "Read-only access to team logo, CSS, and JS assets for frontend containers. Source: /hoody/storage/static-web-assets"}
# β Vague{ "description": "shared files"}Future you will thank present you when managing dozens of shares.
Storage ecosystem:
Related features:
Understanding gained:
/hoody/databases/ for shared SQLite databasesShare directories between containers.
Readonly for safety. Readwrite for collaboration.
One share, multiple consumers. Data exchange without duplication.