Table of Contents
Open Table of Contents
Overview
Most collaboration tools are built around documents, chat messages, or project management boards.
LOFTY takes a different approach.
I developed LOFTY as a collaborative desktop workspace where teams can create shared virtual desktops, organize files and folders, and work together in an environment that feels more like using a traditional computer than using a conventional SaaS dashboard.
The idea is to provide teams with a persistent digital workspace where files, folders, and people exist together inside a shared desktop.
Rather than opening separate applications for file storage, team management, and project organization, LOFTY brings these concepts together into a single workspace.
The application is designed around the concept of a desktop that can be shared with other users.
A user can create a desktop, add team members, organize files and folders, assign permissions, and transfer ownership when necessary.
The project also provided an opportunity to build a complete full-stack SaaS application and deal with real-world concerns such as authentication, permissions, cloud storage, email delivery, deployment, and database design.
The Idea Behind LOFTY
A Shared Digital Workspace
The central idea behind LOFTY is simple: give a group of people a shared desktop.
Instead of thinking about a workspace as a collection of pages or database records, LOFTY represents it as a desktop environment.
Each desktop can contain:
- Files
- Folders
- Team members
- Permissions
- Ownership information
Users can create multiple desktops and collaborate with different groups of people.
For example, a developer could create one desktop for a personal project, another for a freelance client, and another for a larger team.
Each desktop provides its own isolated workspace and membership structure.
Why a Desktop?
The desktop metaphor provides an intuitive way of organizing information.
Traditional file systems already give users a familiar mental model:
- Desktops contain folders
- Folders contain files
- Files belong to projects
- People can share access to resources
LOFTY builds on this existing model while adding the collaborative features expected from modern SaaS applications.
The goal is to make collaboration feel familiar rather than forcing users to learn an entirely new organizational system.
Core Features
Shared Desktops
The main resource in LOFTY is the desktop.
Users can create a desktop and give it a name that represents the project or team it belongs to.
Each desktop has its own:
- Unique ID
- Name
- Owner
- Members
- Files
- Folders
- Permissions
Desktop membership is stored independently, allowing different users to collaborate on the same desktop without sharing access to every other desktop in their account.
This creates a simple boundary between different projects and teams.
File and Folder Management
LOFTY provides a familiar file-system structure for organizing content.
Users can create folders and upload files into them, allowing projects to be organized hierarchically.
The system keeps track of information such as:
- File names
- File sizes
- MIME types
- Folder relationships
- Creation dates
- Uploading users
- File storage keys
Folders can contain other folders, creating a nested structure similar to a traditional operating system.
This structure also makes it possible to build additional features in the future, such as file previews, search, sharing links, and more advanced organization tools.
Team Collaboration
A desktop can be shared with other registered LOFTY users.
Desktop owners can add members by email and choose the level of access they should receive.
When a member is added, LOFTY sends an invitation email containing information about the desktop and their assigned role.
This creates a simple workflow:
- A user creates a desktop.
- The owner enters another user’s email address.
- A role is selected.
- The user is added to the desktop.
- LOFTY sends an invitation email.
- The recipient can open the shared desktop.
The membership remains stored in the database even if email delivery temporarily fails, preventing an email service problem from accidentally removing a user’s access.
Roles and Permissions
LOFTY uses role-based permissions to control what members can do.
The current roles are:
- Owner
- Editor
- Viewer
Owners have full control over the desktop and its membership.
Editors can collaborate with the desktop without having ownership-level control.
Viewers have restricted access intended for users who need to access information without modifying the workspace.
Keeping permissions at the desktop membership level makes it possible to apply the same access rules consistently across files, folders, and team-management operations.
Desktop Ownership
Ownership is treated as a separate concept from membership.
This became particularly important when implementing account deletion.
A user cannot simply delete their account while they still own desktops, because doing so would leave those workspaces without an owner.
Instead, LOFTY requires the user to deal with their owned desktops first.
They can either:
- Transfer ownership to another member
- Delete the desktop
Once the user no longer owns any desktops, their account can safely be deleted.
When ownership is transferred, the previous owner becomes an editor and the selected member becomes the new owner.
This provides a clean ownership lifecycle without leaving orphaned workspaces behind.
Account Management
LOFTY includes standard account management functionality alongside the collaborative features.
Users can:
- Register an account
- Log in
- Log out
- Verify their email address
- Reset their password
- Update their profile
- Manage desktop membership
- Delete their account
Account deletion also takes relationships with collaborative data into consideration.
For example, deleting a user should not unnecessarily destroy files or folders that other members still depend on.
This required careful consideration of database relationships and foreign-key behaviour.
Technical Architecture
Tech Stack
LOFTY is built using a TypeScript-based full-stack architecture.
The main technologies include:
- TypeScript — Used throughout the application for type safety
- React — Provides the interactive frontend
- Vite — Handles frontend development and production builds
- Fastify — Provides the backend API
- PostgreSQL — Stores application and collaboration data
- Drizzle ORM — Provides type-safe database queries and schema management
- Zod — Validates API requests and user input
- Cloudflare R2 — Provides object storage for uploaded files
- Resend — Handles transactional email
- Cloudflare — Hosts the frontend and DNS
- Railway — Hosts the backend API
The application is structured as a monorepo using pnpm and Turborepo.
Monorepo Structure
The project uses a workspace-based monorepo to keep the frontend, backend, and shared packages together.
A simplified structure looks like:
apps/
web/
api/
packages/
db/
types/
validation/
typescript-config/
The web application contains the user interface while the API handles authentication, desktop management, membership, files, folders, and other server-side functionality.
Shared packages contain reusable database definitions, TypeScript types, validation schemas, and configuration.
Using a monorepo also makes it easier to maintain consistent types between the frontend and backend.
Backend and API
The backend is built with Fastify and exposes a versioned REST API.
The API is divided into separate route modules based on application functionality.
These include areas such as:
- Authentication
- OAuth
- Profiles
- Desktops
- Folders
- Files
- Members
- Realtime connections
For example, desktop functionality is exposed through routes under:
/v1/desktops
while authentication is handled under:
/v1/auth
Requests are validated using Zod before reaching the underlying application logic.
This keeps invalid input away from database operations and makes API contracts explicit.
The backend also uses Fastify plugins to provide shared functionality such as authentication and request-level user information.
Database
Relational Data Model
PostgreSQL is used as the primary application database.
The database models the relationships between users, desktops, members, files, and folders.
At a high level, the relationships look like:
User
├── Desktops
├── Desktop Memberships
└── Sessions
Desktop
├── Members
├── Folders
└── Files
Folder
└── Files
A separate membership table allows many users to belong to many desktops.
This is preferable to storing a list of user IDs directly on a desktop because membership also needs additional information such as the user’s role and join date.
Database Constraints
Database constraints are used to protect the integrity of collaborative data.
For example, a desktop membership should not be duplicated for the same user and desktop.
Foreign-key behaviour is also carefully considered when deleting users or desktops.
Deleting a desktop should remove its associated collaborative resources where appropriate, while deleting a user should not necessarily destroy files that are still being used by other members.
These rules are enforced at the database level where possible rather than relying entirely on application code.
File Storage
Cloudflare R2
LOFTY separates file metadata from the actual file contents.
PostgreSQL stores information about a file, such as its name and location, while Cloudflare R2 stores the binary data.
This provides a structure similar to:
PostgreSQL
↓
File metadata
↓
R2 object key
↓
Cloudflare R2
↓
Actual file
This approach avoids putting large binary objects directly into PostgreSQL.
It also makes the storage layer easier to scale independently from the application database.
Storage Keys
Each uploaded file is associated with a storage key that identifies the corresponding object in R2.
The database therefore acts as the source of truth for the application’s file system while R2 acts as the underlying object storage layer.
This separation also leaves room for future functionality such as:
- File previews
- Download links
- File versioning
- Storage quotas
- Larger file support
- Image and document processing
Authentication
Session-Based Authentication
LOFTY uses session-based authentication rather than storing authentication state entirely on the frontend.
When a user authenticates, a session is created and associated with the user’s account.
Sessions have expiration times and can be invalidated when the user logs out.
Sensitive authentication values are hashed before being stored in the database.
This applies to session credentials as well as other security-sensitive tokens.
Password Security
Passwords are never stored directly in the database.
They are hashed using Argon2 before being persisted.
Password reset functionality uses randomly generated tokens which are also stored securely rather than saving the raw token.
This means that even if database contents were exposed, the stored password and reset credentials would not directly provide usable authentication secrets.
Realtime Collaboration
LOFTY also includes infrastructure for realtime desktop connections.
The backend provides a realtime endpoint that authenticates the connected user before allowing them to establish a connection.
This creates the foundation for collaborative functionality where changes made by one user can eventually be reflected to other members of the same desktop without requiring a page refresh.
Potential realtime events include:
- File creation
- File deletion
- Folder creation
- Folder deletion
- Member changes
- Desktop updates
The realtime architecture is intentionally separated from the standard REST API so that normal CRUD operations and persistent connections can evolve independently.
Development Challenges
Designing a Shared Desktop Model
One of the biggest challenges was deciding what a “desktop” actually represents in the application’s data model.
It would have been possible to treat a desktop as little more than a folder.
However, the desktop also needs to represent:
- Ownership
- Membership
- Roles
- Collaboration
- Files
- Folders
- Permissions
This made the desktop a higher-level resource rather than simply another folder.
Keeping that distinction clear simplified the implementation of membership and permission systems.
Permissions and Ownership
Collaborative applications introduce a large number of permission edge cases.
For example:
- Can a viewer modify a file?
- Can an editor invite another user?
- Can an owner remove themselves?
- What happens when the owner deletes their account?
- Can ownership be transferred to someone who is not a member?
- What happens to the previous owner’s role?
These cases need to be handled consistently across the API.
LOFTY therefore separates authentication from authorization.
Authentication answers:
Who is this user?
Authorization answers:
Is this user allowed to perform this action on this desktop?
Permission helpers are then used by different routes rather than duplicating role checks throughout the application.
Handling File Storage
File storage introduces another layer of complexity compared with a normal CRUD application.
The database and object storage need to remain consistent.
For example, uploading a file involves both:
- Storing the actual object.
- Creating the corresponding database record.
Deleting a file requires the reverse process.
This creates failure scenarios where one operation succeeds while the other fails.
Designing the storage layer therefore requires careful consideration of cleanup, error handling, and the relationship between database records and R2 objects.
Production Infrastructure
Moving from local development to production introduced several additional concerns.
The application is split across multiple services:
lofty.social
│
▼
Cloudflare
│
├── Frontend
│
└── API → Railway
│
├── PostgreSQL / Neon
│
└── Cloudflare R2
Each service requires its own configuration, environment variables, domains, and deployment process.
The API also needs to handle production CORS correctly so that the frontend can communicate with the backend while credentials remain enabled.
This required moving away from development URLs such as:
http://localhost:5173
http://localhost:3001
and configuring the production services around:
https://lofty.social
https://api.lofty.social
Deployment
Frontend
The LOFTY frontend is deployed through Cloudflare.
The production frontend is available at:
https://lofty.social
The frontend uses an environment variable for the API URL so that development and production environments can use different API endpoints.
This avoids hardcoding production infrastructure into application code.
Backend
The Fastify API is deployed using Railway.
The production API is available through:
https://api.lofty.social
Railway handles the application process while the API connects to the production PostgreSQL database and Cloudflare R2 storage.
The backend is built from the monorepo using Turborepo, allowing the shared packages to be compiled before the API starts.
Transactional email is handled through Resend.
LOFTY uses email for functionality such as:
- Account verification
- Password reset
- Desktop invitations
The production email sender uses the verified lofty.social domain.
This was particularly important when moving beyond Resend’s testing environment, which restricts emails to the account owner’s address until a sending domain has been verified.
The final production setup allows LOFTY to send invitations directly to desktop members.
Roadmap
Collaboration Features
The current foundation allows several collaborative features to be expanded in the future:
- Realtime file updates
- Drag-and-drop file management
- File previews
- Shared clipboard functionality
- Desktop activity feeds
- User presence indicators
- Notifications
- File comments
- File version history
Desktop Experience
The desktop interface can also become more sophisticated over time.
Potential additions include:
- Custom desktop backgrounds
- Resizable windows
- Multiple open applications
- Context menus
- Keyboard shortcuts
- Desktop widgets
- Custom icons
- Improved mobile support
The long-term goal is to make the desktop feel increasingly like a complete collaborative operating environment rather than simply a file browser.
Technical Improvements
The infrastructure can also evolve as the platform grows.
Potential improvements include:
- Automated integration testing
- More comprehensive realtime infrastructure
- Background jobs
- File processing workers
- Caching
- Search indexing
- Storage quotas
- Usage analytics
- Monitoring and observability
- Improved database query optimization
- More granular permissions
Conclusion
LOFTY started with a relatively simple idea: what if teams could share a desktop?
Building that idea into a production application required considerably more than creating a file browser.
The project involved designing a collaborative data model, implementing authentication and authorization, managing desktop ownership, building membership functionality, integrating cloud object storage, sending transactional emails, and deploying multiple services together.
From a technical perspective, the project has provided an opportunity to work with a modern TypeScript stack consisting of React, Fastify, PostgreSQL, Drizzle, Cloudflare R2, Railway, and Cloudflare.
More importantly, it has required thinking about the less visible parts of building a SaaS product: permissions, failure cases, data integrity, account lifecycle, deployment configuration, and the boundaries between different services.
The long-term goal for LOFTY is to develop the desktop concept into a complete collaborative workspace where teams can organize their projects, files, and communication inside an environment that feels familiar, flexible, and genuinely shared.