File Repository

What It Is

The File Repository is a tenant-wide operational view over every file Jetstack holds. It does not introduce a new storage location and it does not re-upload files. It gives administrators and power users one place to see, organize, lock, rename, move, and audit files regardless of where those files were originally attached.

Two kinds of files flow into this view:

  • Standalone files that live as Media File records
  • Attached files that live as file-valued properties on any typed object

In the File Repository, both kinds look the same to the operator. The repository abstracts away the difference so that file-level operations can be performed without having to know which presenter or type originally produced the file.

Why It Matters

File-valued data in Jetstack tends to spread across many types, properties, and records. Without a central surface the tenant cannot easily answer questions like:

  • "how much storage does the file repository consume?"
  • "is this important contract file still referenced by a live record?"
  • "who owns this uploaded asset?"
  • "can I bulk-move these files into a legal-review folder?"
  • "can I prevent this file from being accidentally reorganized?"

The File Repository answers those questions in one place. It treats files as a governed asset surface rather than as a side-effect of whatever form originally accepted the upload.

Conceptual Model

There are four layers to the repository experience:

  1. Source data. Every file still lives where it was created — either on a Media File record or on a file-valued property of some typed object.
  2. Assignment. A lightweight record binding a file to a folder and optional lock state inside the repository's private root.
  3. Index. A denormalized snapshot used to render the listing in one query. The index is authoritative for display; the source data is authoritative for content.
  4. Redirect endpoint. A stable link per indexed row that resolves on demand to the current storage URL of the underlying file.

These four layers work together so that the listing page stays fast, the displayed state stays accurate, and the underlying files stay free to be moved or replaced by the parts of the platform that own them.

Files From Two Sources, One Surface

The repository merges two file sources into a single browseable list.

Standalone media files

These are full records in their own right. They have their own id, their own lifecycle, their own owner, their own publicity level, and they appear in the repository as a file that is not attached to another domain object.

Property-attached files

Any typed object can have file-valued properties. When such a property holds a file, the repository shows it as though the file were a standalone entry, but the listing still tells the user:

  • which type owns the object
  • which property holds the file
  • which object it is attached to

From the repository's point of view, the underlying object is the "carrier" of the file. The file is displayed as the asset, and the carrier is shown as context.

The Index

To serve the listing page with one query, the repository keeps a denormalized index. Each indexed row records:

  • the source scope (type id, object id, property id — with a sentinel value for standalone files)
  • the file name and file source path
  • the cached size and modified timestamp read from the file payload
  • the resolved type name, property name, and object label for display
  • the current folder placement and lock state
  • the current owner user
  • a stable uuid used to build the redirect link

The index exists to separate how we display files from where they actually live. The source data is still the authority; the index is a fast denormalized mirror.

Why An Index

A tenant can easily hold thousands of files across many types. Without an index, each listed row would require hydrating an object, resolving a property value, and producing a URL. That makes the listing slow and noisy.

With an index, the listing is a single SELECT plus a small bounded amount of work for folder path resolution and owner name resolution.

How The Index Stays Current

The repository keeps the index in sync automatically during normal tenant usage:

  • Creating a typed object or a Media File re-syncs the affected scopes.
  • Updating a typed object or a Media File re-syncs the affected scopes.
  • Deleting a typed object or a Media File clears the affected scopes.
  • Moving files between folders updates the placement without re-reading the file payload.
  • Locking and unlocking files updates the lock flag without re-reading the file payload.

These hooks fire for every save path that goes through the platform's resource managers, which covers ordinary edits, API writes, and automation-driven writes.

When A Full Rebuild Is Useful

A full rebuild scans all Media File records and every file-valued property on every type, then replaces any drifted rows in the index and removes rows whose source is gone.

Use a rebuild when:

  • raw SQL or an imported dataset bypassed the normal save path
  • a tenant was restored from a backup
  • the index looks inconsistent after a schema or data migration
  • a new file-valued property was added and existing rows should be indexed immediately

A rebuild is safe to run repeatedly. It is idempotent, and it does not touch file contents.

Owners And Attribution

Every indexed row carries the id of the current owner user of the source record. The owner's display name is resolved at render time, not denormalized into the index, so renaming a user is reflected immediately in the repository listing without a rebuild.

This matters when:

  • governance reviews need to know who is responsible for an asset
  • a file should be re-assigned to a different owner as part of a process
  • the tenant tracks storage usage per user

Folders Inside The Repository

The repository has its own private root and its own folder tree. That tree is independent from any other root the tenant uses. Its job is to let operators organize files:

  • by process stage (for example: draft, approved, archived)
  • by topic (for example: legal, marketing, internal)
  • by handling rule (for example: retention, restricted)

Folder placement is part of the assignment record. That means a file can be moved through the repository tree without touching the source object. The carrier object is not renamed, not re-saved, and not modified in any way — only the file's organizational placement changes.

Folder State Tracking

Folder placement is tracked on the assignment row, not on the underlying file. This keeps the repository's navigational state fully separate from the business data model.

When a file is:

  • newly detected in a scan → it has no folder assignment yet, so it shows at the root level
  • moved → only its assignment row changes
  • removed at source → both its index row and its assignment row are cleared

Locking A File

A file can be locked inside the repository. Locking is a tenant-admin control that declares "this file should not be moved or rearranged through the repository UI."

Locking does not:

  • freeze the file contents
  • prevent the underlying object from being edited
  • remove the file from ordinary search or access control

It only says: the repository should treat this file as pinned for organizational purposes. That is useful when:

  • a sensitive document must stay in a specific folder
  • a file is referenced externally by URL and administrators want to signal "leave this alone"
  • an automation-generated file should not be drifted into unrelated folders by manual edits

Redirect Endpoint

Every row in the listing exposes a stable link based on its uuid. That link does not download the file directly. Instead, it asks the repository to resolve the file right now — look up the indexed scope, re-derive the current storage URL, enforce access control, and redirect the caller to that URL.

This design matters for three reasons:

  1. Links stay stable across moves and renames because the link is tied to the indexed scope's uuid, not to the current physical path.
  2. Access control stays correct because the redirect recomputes permissions at the moment of the request.
  3. Broken or pending files are communicated cleanly. If the source has been removed, the destination has not been materialized yet, or the caller has no permission, the endpoint returns a clean 404 instead of silently giving a dead link.

Soft Delete Handling

A Media File or typed object can be soft-deleted outside the repository. The repository handles this in two ways:

  • The listing query defensively filters soft-deleted standalone files, so those rows disappear from the listing immediately, even before a rebuild removes them from the index.
  • Any save or delete path through the resource manager clears the affected scope from the index directly.

This is the practical guarantee: the listing does not show a file whose source is gone, even during the small window between the soft-delete and the next index housekeeping pass.

How This Fits Into Tenant Storage Reporting

The repository exposes total bytes across all indexed files. This is shown on the application management screen as the File Repository Size, distinct from the Database Size.

Those two numbers are intentionally separate because:

  • database size reflects the relational data footprint
  • file repository size reflects file payload storage usage

Together they give operators a complete picture of how much the tenant is consuming.

Upload Workflow

Uploading a file into the repository:

  1. The user opens the upload dialog.
  2. Files are pushed through the tenant's configured upload pipeline, producing upload promises.
  3. When the user confirms, the repository finalizes each promise into a standalone Media File.
  4. The new file is placed into the currently selected folder in the repository tree.
  5. The index receives the new row automatically through the normal save hook.

The user does not need to pre-create a carrier object. Files uploaded through the repository live as standalone Media Files and are ready to be organized.

Listing And Filtering

The listing supports:

  • free-text search by file name
  • filter by owning type
  • filter by owning property
  • scoping by folder
  • sorting by name, size, modified timestamp, or type
  • sidebar and table column display of owner, location, and type

Search is system-wide when active. Folder scope is automatically dropped while a search query is in effect so the user can find assets across the whole repository without having to clear their current folder first.

Bulk Operations

The repository supports bulk moves and bulk deletes on standalone files. Bulk operations always respect locking. A locked file is never moved or deleted by a bulk action — the operator has to unlock it first, deliberately.

This is intentional. Bulk handling is fast but must not be reversibly wrong, so locking acts as the operator's safety net.

Governance Recommendations

Use the repository as the daily organizational surface for files, even when those files originated elsewhere. Concretely:

  • put important long-lived assets into managed folders
  • lock files that must not be moved, with a short note in the surrounding documentation explaining why
  • review owner attribution periodically and re-assign ownership when people change roles
  • use size reporting to keep storage growth visible
  • prefer the redirect link over raw file URLs when distributing file references internally, because the redirect keeps working when the source is moved or renamed

Treat the repository as the administrative layer on top of whichever carrier object originally held the file.

Example

Suppose a tenant operator needs to consolidate and govern contract attachments across many different record types.

A workable approach:

  1. Build a folder hierarchy inside the repository for contracts (for example: drafts, in-review, signed, archived).
  2. Use search and type filters to find all file attachments carried on contract-shaped records.
  3. Move those files into the appropriate contracts folders.
  4. Lock the signed and archived files so that they stay where they were placed.
  5. Track owner and size reporting over time to verify that contracts are being produced, reviewed, and archived at a sensible rate.

In this scenario the underlying contract records are never modified. Only the repository's assignment rows change. That is the intended separation: business data stays on the carrier object, organizational state stays on the repository.

Anti-Patterns

Avoid these patterns.

  • Using repository folders to encode business meaning that really belongs to a property. Properties are indexable and filterable in queries; folders are not.
  • Pinning every file with a lock. Locks are meaningful only when they are exceptions.
  • Relying on rebuild as a routine operation. Rebuild is a recovery tool; normal usage should not need it.
  • Treating the index as a source of truth for content. The source record is always authoritative; the index can be rebuilt from the source at any time.

Best Practices

  • Treat the repository as an operational and administrative surface, not as a data model.
  • Keep folders shallow and meaningful. The repository is browsed by operators, not consumed as a taxonomy.
  • Prefer the repository's redirect link when referencing a file from other documentation or communication channels.
  • Run a rebuild after bulk imports or schema migrations to ensure consistency.
  • Monitor repository size alongside database size when evaluating tenant growth.

Programmatic Access

Scripts running inside automations, canvases, the Cloud Console, and the Code Inspector can work with standalone repository files through $api->fileRepository. That sub-manager covers listing, uploading files from a local path, renaming, moving, deleting, resolving the current storage URL by uuid, rebuilding the index, and reporting total size.

Files attached to typed objects are managed implicitly through the owning object's normal save path, so the programmatic surface intentionally focuses on standalone files only.

See Internal API Reference for the full method signatures and usage notes.