Files And Network
Model file fields in Inspire and declare outbound network policy for your application runtime.
Two features that define the application boundary: file determines how uploaded bytes become part of your data model; network determines which external systems your server-side code can reach.
This page also covers the raw integration path: routes(...) for inbound webhooks, req.rawBody for signature verification, network {} for outbound allowlists, and @secret(...) for provider keys. Use integrations {} when you need managed per-user delegated OAuth.
File Fields
The Core Model
A file is a field on an entity, not a bucket path.
entity Attachment {
name: string,
file: file,
ownerId: __User.id,
@grant read, write, delete where resource.ownerId == principal.id
}Authorization, lifecycle, namespace scoping, and cleanup all derive from the owning entity. The object store is an implementation detail.
Declaration Patterns
Use a file field when one entity owns one uploaded file.
entity UserProfile {
displayName: string,
avatar: file?,
userId: __User.id,
@grant read to *
@grant write where resource.userId == principal.id
}One File Per Field
A single file field holds one file. Arrays of file are not supported; the compiler rejects them. Use a separate entity for collections:
entity TaskAttachment {
taskId: Task.id @onDelete(cascade),
file: file,
uploadedBy: __User.id,
label: string?,
@grant read to * via TeamMembership
where TeamMembership.teamId == resource.task.teamId
@grant insert where resource.task.createdBy == principal.id
@grant delete where resource.uploadedBy == principal.id
}A collection of files typically needs its own identity, ordering, labels, audit data, and per-file deletion. Model it as an entity.
What The Runtime Stores
File metadata is stored as JSONB on the owning row. Pending uploads are tracked in a system table.
Metadata includes: storage key, file size, MIME type, original filename, and upload timestamp. Application code works with the typed file field, not raw JSONB.
The Upload Lifecycle
A three-step handshake. File bytes do not travel through your application server.
- Request upload -- the platform issues a principal-bound upload token and presigned URL. Upload permission is checked before upload begins.
- Direct upload -- the client uploads bytes directly to the backing store using the presigned URL.
- Confirm upload -- the platform verifies the token, rechecks principal authorization, validates metadata, and writes file JSONB onto the entity row. Until confirmation, the entity has not adopted the file.
Download Flow
- The principal reads an entity row they can access
- The platform rechecks file-read access when generating the download URL
- The client receives a presigned download URL
Authorization
File Authorization Is Entity Authorization
No second authorization system for file storage.
- Read the entity field → obtain file metadata and download URL
- Update the entity field → replace the file
- Delete the entity → associated file follows
All policy patterns apply: ownership, role-based, via junction, public, field-level grants.
Upload And Replace
Uploading or replacing a file follows insert/update authorization on the owning entity. Confirmation re-enters the normal policy path.
Read
Downloading follows read authorization on the entity and field. Composes with field-level grants.
Delete
No independent file-delete permission. Files are deleted when:
- A nullable file field is cleared
- A file field is replaced
- The owning entity is deleted
- A cascading delete removes the owning row
Lifecycle And Cleanup
Replacement
When a file field is replaced, the runtime writes new metadata and cleans up the old object.
Entity Deletion
Deleting the owning row deletes the associated file object.
Cascade Deletion
File cleanup follows FK cascade deletion.
Orphan Handling
Pending uploads are tracked in __file_pending. Uploads that complete but are never confirmed are treated as controlled in-flight state, not permanent orphans.
Namespaces
File fields on namespaced entities follow the same namespace rules as all other fields. Tenant-scoped entities have tenant-scoped files. Cross-tenant file access requires the same elevated path as cross-tenant data access.
Quotas And Operational Limits
Uploads consume storage, downloads consume bandwidth, and pending uploads need cleanup discipline. Files are part of the application resource model.
Current Runtime Shape
- JSONB file metadata on the owning row
- 3-step upload handshake
- Principal-bound pending uploads in
__file_pending - Presigned upload and download URLs
- MIME validation
- Entity-policy rechecks on confirm and download
- Mock provider in dev, S3 presigner in configured environments
Recommended Modeling Pattern
- Use
filewhen one row owns one uploaded file - Use a separate attachment entity for collections
- Keep all authorization on the owning entity
- Treat upload confirmation as the moment the entity adopts the file
- Let entity deletion and cascade deletion drive file cleanup
Network Policy
network controls which external services server-side code can reach.
network {
allow: [
"api.stripe.com",
"hooks.slack.com",
],
dynamic: false,
}Use network to keep outbound access explicit at the schema level.
Related guide: File Storage