gighiveThis document outlines adding TUS (Tus Resumable Upload) protocol support to the GigHive iOS client for bypassing Cloudflare’s 100MB upload limit.
Implement TUS support for Cloudflare compatibility, using a dedicated TUS endpoint (recommended: tusd behind Apache) and keeping the existing upload path as a fallback.
MultipartInputStream already solves memory usage for large files./api/uploads.php cannot speak TUS).tusd service and temp storage/cleanup).If an environment’s origin is only reachable via Cloudflare Tunnel, a DNS-only upload subdomain does not bypass Cloudflare limits unless the origin is also publicly reachable. In these environments, TUS (or direct-to-object-storage uploads) is the practical approach.
InputStream that streams files without loading into memory ✅URLSessionTaskDelegate ✅project.yml configuration ✅The GigHive origin in some environments is only reachable via Cloudflare Tunnel (for example, routing lab.gighive.app to a private IP). In that setup:
The existing /api/uploads.php endpoint expects a single multipart/form-data request (PHP $_FILES). TUS uses multiple requests (typically POST then multiple PATCH requests with application/offset+octet-stream). Therefore:
/api/uploads.php./tus via tusd) and keep /api/uploads.php available temporarily as a legacy fallback for testing/rollback.Current Behavior (Before TUS Cutover):
See: UPLOAD_OPTIONS.md for detailed analysis
(Int64, Int64) -> Void/api/uploads.php)/audio//video locations and write DB metadatatusd Sidecar Containertusdtusd is the reference TUS server implementation and is available as a Docker image. Running it as a sidecar keeps the upload logic out of the PHP endpoint and provides well-tested resumable upload semantics.
https://<env>.gighive.app/files (through Cloudflare, same hostname)/files (Apache), which reverse-proxies to tusd’s internal /files/ handlertusd stores partial uploads in a mounted directory/files//files/If you see 200 OK with a body like “Welcome to tusd” when creating an upload, you are likely hitting tusd’s welcome endpoint instead of its TUS handler (/files/).
The Ansible playbooks (including post_build_checks) assume the TUS-related variables are defined for the inventory/group being deployed (for example under ansible/inventories/group_vars/<group>/<group>.yml). Key variables include:
tusd_porttusd_base_path (expected: /files)tus_public_path (expected: /files)gighive_scheme, gighive_host, gighive_base_urlgighive_validate_certsgighive_hostname_for_host_header (optional, but needed when accessing by IP and Apache vhost routing depends on Host)Current ModSecurity configuration enforces multipart/form-data for /api/uploads.php and /api/media-files. TUS requests are not multipart. To avoid conflicts:
/api/uploads.php for TUS traffic./files) and ensure ModSecurity rules do not block PATCH or the TUS content type on that path.GET /files/ should return 405 with Allow: POST.POST /files/ should return 201 Created with a Location header.OPTIONS /files/ without auth should return 401/403.POST /files/ should return 201 Created with a Location header.ansible/roles/docker/files/apache/webroot/db/upload_form.phpansible/roles/docker/files/apache/webroot/db/upload_form_admin.phpuploadWithTUS method to UploadClient.swift (~30 lines) - COMPLETEDtusd service to docker-compose.yml.j2 (tusproject/tusd) with a persistent volume for upload storage/tus to tusd (e.g. http://tusd:1080/files/)/tus with Basic Auth (same users as uploads)PATCH and TUS content-types on /tus (keep multipart enforcement on /api/uploads.php)tusd hooks to record a server-side mapping from upload_id to the completed upload’s storage location (Option A)/audio//video locationsupload_id/audio or /video destination and DB metadata is written// Hard cutover: use TUS protocol for all files
return try await uploadWithTUS(payload: payload, progress: progress)
| Method | HTTP Requests | Use Case | Cloudflare Compatible |
|---|---|---|---|
uploadWithTUS() |
Multiple PATCH | All uploads | ✅ Yes |
uploadWithMultipartInputStream() |
1 POST | Legacy fallback only | ✅ Yes |
Old upload() method |
1 POST (loads to memory) | Deprecated | ⚠️ |
/api/uploads.php fallbackRelated Documentation:
UPLOAD_OPTIONS.md - Cloudflare 100MB limit analysis