File Upload Storage
Use native Convex Storage for public media and private business documents.
NowStack stores uploaded files in native Convex Storage. Storage is attached to the Convex deployment, so there is no separate bucket, S3 endpoint, access key, or public storage domain to configure.
How It Works
The active implementation lives in convex/files/. It separates public media from private business documents instead of exposing every upload through one public URL flow.
For small or server-generated content, a Convex action validates the payload and stores a Blob with ctx.storage.store. Browser uploads use a short-lived URL created with ctx.storage.generateUploadUrl; after the upload, the backend validates the returned storage ID and associates it with an authorized domain record.
Store the storage ID and required metadata in Convex records rather than persisting a generated URL. Public media may resolve a current URL from Convex Storage. Private HR, payroll, expense, contract, and worksite files must go through the authenticated gateway in convex/files/, which rechecks organization, object, field, and actor authorization before serving bytes.
Configuration
Convex Storage requires no storage-specific environment variables. Configure and run the intended Convex deployment, then use its native storage APIs from Convex functions.
Do not add AWS SDK packages, S3-compatible credentials, or a public bucket fallback. Application-specific limits, accepted content types, ownership, retention, and access checks remain explicit backend rules.
Upload Flow
- Authenticate the actor and resolve the active organization and target object.
- Reserve the upload and enforce file type, size, rate, and lifecycle constraints.
- Store the file server-side or issue a short-lived Convex upload URL.
- Validate the stored metadata and bind the storage ID to the domain record.
- Resolve public media through Convex Storage, or issue an authorized, short-lived private read grant.
- Delete orphaned or replaced storage objects through the managed lifecycle.
Verify Uploads
Use the relevant public-media or private-document flow in the app, then confirm:
- The upload is rejected when actor, organization, target, size, or content type is invalid.
- The resulting storage ID is associated only after backend validation.
- Public media resolves from Convex Storage without a third-party domain.
- Private files are unavailable without a current authorized read grant and use private, no-store responses.
- Replaced, failed, or expired objects follow the cleanup and retention rules for their domain.
If storage calls fail, verify the active Convex deployment and inspect the application’s storage authorization, validation, and lifecycle errors. There are no storage-provider credentials to repair.