TMA CloudTMA Cloud
Concepts

File System

File system architecture and organization in TMA Cloud.

File system architecture and organization in TMA Cloud.

File Structure

Hierarchical Organization

  • Root Level: User's root directory
  • Folders: Nested folder structure
  • Files: Stored within folders or root
  • Parents: An item's parent is always a folder in the same account. An upload, new folder or copy into a file, a folder in the trash or another account's folder is refused with Target folder not found. See Database Schema for the triggers that enforce this.

File Metadata

  • ID: Unique identifier (stable across operations)
  • Name: File or folder name
  • Type: 'file' or 'folder'
  • Size: File size in bytes
  • MIME Type: Actual MIME type detected from file content (not from extension)
  • Parent ID: Parent folder reference
  • Path: The storage key, not a user-visible path
  • User ID: The account the item belongs to
  • Last Access Time: When the item was last read (accessed_at), shown in the UI as "Last opened"
  • Shared At: When the item most recently became shared (shared_at); null while the item is not shared

File Names

A name is stored exactly as the user's system wrote it. Uploads, new folders and renames all follow one rule, close to Nextcloud's default, which also refuses only separators and control characters:

  • Up to 255 characters, and not empty, . or ...
  • No / or \, which are path separators, and no control characters (ASCII 0–31 and 127).

Everything else is allowed, including spaces, accents, emoji, the narrow no-break space that newer Windows time formats put before AM and PM, and the characters Windows reserves (: * ? " < > |). Names such as CON or ones ending in a dot are also accepted, since macOS, Linux, and Android allow them.

Where a name has to become a Windows file, it is translated rather than refused:

  • Folder ZIP downloads replace : * ? " < > | with _ and drop trailing dots and spaces in each entry, as browsers do for single downloads.
  • The Cloud Drive shows each reserved character, and a trailing dot or space, as the private-use character U+F000 plus its code, the mapping Cygwin, WSL and Syncthing use. Renames and saves turn it back, so the stored name never changes. WSL and Git Bash show the original characters.

Name Clashes

A new folder or a rename cannot take a name that another file or folder in the same folder already has. Letter case is ignored, as in Explorer and Finder, so Report.pdf and report.PDF clash, and a file and a folder cannot share a name. Items in the trash do not count. The server refuses the request with 409, and the New folder and Rename dialogs say so before sending.

Copies, moves and pastes keep both instead, giving the new item a numbered name such as Photos (1), and an upload asks whether to replace the existing file or keep both; see Upload Files and Manage Folders.

File Operations

Supported Operations

  • Upload: Add new files
  • Download: Retrieve files
  • Create Folder: Create new directories
  • Move: Move files/folders
  • Copy: Duplicate files/folders
  • Rename: Change file/folder names
  • Delete: Move to trash
  • Star: Mark as favorite
  • Share: Create share links

Large File Handling

  • Streaming: Files streamed without loading into memory
  • Upload: Files encrypted and streamed directly to the bucket
  • Download: Files streamed from storage to client; single-file downloads support HTTP Range (partial content) for seeking
  • ZIP Archives: Archive entries are read through a database cursor and streamed one at a time, keeping database pages and open storage streams bounded. Trees of up to 20,000 entries are read at the start so the database connection is released right away; larger trees are limited by DB_HELD_CURSOR_LIMIT. Each entry already carries its wrapped data key, so no per-file key lookup is made. The archive stops when the client disconnects

Performance

  • Streaming prevents memory exhaustion for large files (>1GB)
  • Rename operations update database metadata without moving stored objects
  • Copy operations are queued on the background worker, use server-side object-store copies (24 at once), and read folder trees once before batched inserts
  • File lists use stable cursor batches and load continuously as the user scrolls. Only visible grid/list rows plus a small overscan are rendered
  • Folder sizes and descendant counts are maintained by database triggers. Size sorting uses those stored totals and the matching page-order index instead of walking a folder tree during a listing. Nothing recomputes them on read; a weekly worker job recomputes them from the rows and reports any folder whose stored totals were wrong. See Background Workers

Path Management

  • Renaming or moving an item changes its parent_id and name, not its storage key, so no stored object is touched
  • Stored object keys are validated as flat keys without path separators or traversal sequences

Last Access Time

Every file and folder carries an accessed_at timestamp, surfaced in the UI as Last opened in Get Info and as a sort option in the file list. It answers "when was this last read", which modified cannot, since modified only moves when the contents change. The server also keeps who read it, the owner or one of their sub-users, so the dashboard's Recent files can say "Opened 2:18 AM by Sam Rivera". A new upload counts as opened by whoever uploaded it. Each person has their own update window, so a sub-user opening a file the owner just opened still counts.

What Counts as Reading

ActionUpdates
Downloading a fileThat file
Downloading a folder as ZIPThe folder and everything in it
Bulk downloadEvery item in the archive
Opening a document in OnlyOfficeThat file
Reading a file through a share linkThat file
Opening a folder in the file listThe folder only, not its contents
Uploading or replacing a fileThat file

Listing a folder marks the folder and leaves its children alone. This matches how Windows treats directory enumeration, and it keeps the value meaningful: if browsing past a file counted as opening it, every file in a folder you visit would look recently used. Cloud Drive's listings do not count either, since it lists every folder it keeps current on each resync whether or not anyone looks; files opened from it still count, through the download.

Searching, viewing Get Info, renaming, moving, starring and sharing do not count as reads.

Write Behavior

Recording a read on every request would turn each read into a database write. Two rules keep that cost bounded, both taken from how filesystems handle the same problem:

  • Update window, one hour by default. Once an item's timestamp is written, further reads of it are ignored until the window passes. NTFS guarantees its last-access time only to within an hour for the same reason; Linux's relatime uses a comparable rule.
  • Buffered writes. Updates accumulate in memory and are written in one batched statement every 10 seconds by default, so no read waits on a write. Linux's lazytime works the same way.

The effect is at most one write per item per window, batched. The number of statements is set by the flush interval rather than by how many users are reading, so it does not grow with traffic. Timestamps may lag by the flush interval plus the window, which is why the value is documented as approximate.

The first user can turn tracking off and change the window and the write interval under Settings → Administration → Recently opened. See Session Timeout and Recently Opened.

On the Cloud Drive

On Windows the drive reports this value as the NTFS LastAccessTime, so Explorer's Date accessed column reflects it. The other three NTFS timestamps come from the modification time. Times Windows writes back locally are never sent to the server, because the server decides what counts as a read. See Desktop App.

Storage

Physical Storage

  • S3: Files stored in S3-compatible object storage. Database stores object key.
  • The first user connects the bucket in Settings → Storage; file contents cannot be stored or read until it is saved.
  • Original filenames preserved in database

File Encryption

  • Files encrypted with AES-256-GCM in Google Tink's AES-GCM-HKDF-STREAMING format (the AES256_GCM_HKDF_1MB scheme)
  • Each newly encrypted object version uses a random data key (DEK). FILE_ENCRYPTION_KEY is the key-encryption key (KEK) that wraps the DEK; a server-side logical copy reuses the source ciphertext and wrapped DEK
  • Each object has a 40-byte header (a random salt and nonce prefix) followed by 1 MB segments, each sealed with its own authentication tag. The per-file encryption key is derived from the DEK with HKDF-SHA256
  • Segments make stored objects seekable: a download serves an HTTP Range request by fetching and decrypting only the overlapping segments, so a large file opens without reading all of it
  • Automatic decryption on download
  • Rotating FILE_ENCRYPTION_KEY only rewraps the stored DEKs and never re-encrypts the objects. See Key Rotation

Storage Limits

See Storage Management.

Trash System

Soft Delete

  • Files moved to trash (not deleted)
  • deleted_at timestamp set
  • Restorable within retention period
  • Share links stop serving trashed items until they are restored

Automatic Cleanup

  • Items in the trash for more than 15 days are deleted for good by the background worker
  • Delete forever and Empty Trash also run on the worker
  • PostgreSQL pg_trgm extension for fuzzy text matching
  • GIN index on file names for fast searches
  • Prefix matching for short queries
  • Indexed trigram matching for longer queries
  • User-scoped searches
  • % and _ in a query match literally, not as wildcards

Last updated on

On this page