Send private, disappearing messages to your friends! An ephemeral messaging platform with self-destructing images and secure communication.
This project is organized as a pnpm monorepo with two main packages:
ghostletter/
├── UI/ # Next.js 16 frontend
├── API/ # Laravel 12 backend
└── package.json # Monorepo scripts
- Framework: Next.js 16 with React 19
- Styling: Mantine UI Component Library
- State Management: Redux Toolkit
- API Client: Native Fetch with automatic token injection
- Testing: Playwright E2E tests
- Features:
- Username-based authentication
- Real-time messaging
- Camera integration for photo capture
- Ephemeral image viewing (10-second countdown)
- Friend management
- Image upload and sending
📖 See UI/README.md for detailed frontend documentation.
- Framework: Laravel 12
- Authentication: Bspdx/Authkit with Laravel Fortify
- Features:
- RESTful API with Laravel Sanctum
- Two-factor authentication (2FA)
- User management with passkeys support
- Message and friend management
- Image upload and storage
- Ephemeral message expiry logic
- PHP 8.4+
- Composer 2.8+
- Node.js 18+
- pnpm 10+
-
Install dependencies:
pnpm install
-
Set up the API:
cd API cp .env.example .env php artisan key:generate php artisan migrate php artisan storage:link -
Run both servers concurrently:
# From root directory pnpm devOr run them separately:
# Terminal 1 - Frontend (port 3000) pnpm dev:ui # Terminal 2 - Backend (port 8000) pnpm dev:api
- Frontend: http://localhost:3000
- Backend API: http://localhost:8000
- API Documentation: http://localhost:8000/api
Users
- Standard Laravel user fields
initials,color,avatar_urlfor UI personalization
Friends
- Bilateral friendship relationships
- Links users together
Messages
- Support for text and image types
- Ephemeral image viewing with expiry timestamps
- Read status tracking
The application uses username-based authentication (not email).
- Username:
demo01 - Password:
demo01
The API uses Laravel Fortify with Bspdx/Authkit for:
- Username/password authentication
- Two-factor authentication (2FA)
- Passkey support (WebAuthn)
- API token management via Sanctum
- JWT tokens stored in cookies
- Automatic token injection in all API requests
- 401 responses trigger logout and redirect to sign-in
POST /register- Register new userPOST /login- Login with credentialsPOST /logout- Logout current userPOST /two-factor-authentication- Enable/disable 2FA
GET /api/friends- List all friendsGET /api/friends-list- Simple friends listPOST /api/friends- Add a friendDELETE /api/friends/{id}- Remove friend
GET /api/messages- List all messagesGET /api/conversations/{friendId}- Get conversationPOST /api/messages- Send messagePOST /api/messages/{id}/mark-read- Mark as readPOST /api/messages/{id}/mark-viewed- Mark image as viewedDELETE /api/messages/{id}- Delete message
POST /api/images/upload- Upload image
Root level:
pnpm dev- Run both UI and API concurrentlypnpm dev:ui- Run UI onlypnpm dev:api- Run API onlypnpm build- Build both projectspnpm lint- Lint all projects
UI:
pnpm --filter ghost-letter-ui dev- Start dev serverpnpm --filter ghost-letter-ui build- Build for productionpnpm --filter ghost-letter-ui lint- Run ESLintpnpm --filter ghost-letter-ui test:e2e- Run E2E tests
API:
pnpm --filter ghost-letter-api dev- Start Laravel serverpnpm --filter ghost-letter-api test- Run PHPUnit testspnpm --filter ghost-letter-api migrate- Run migrationspnpm --filter ghost-letter-api migrate:fresh- Reset database
cd UI
pnpm test:e2e # Run tests headless
pnpm test:e2e:ui # Interactive UI mode
pnpm test:e2e:headed # See browserPrerequisites: API must be running with seeded database.
cd API
php artisan test # Run all tests
php artisan test --filter=MessageTest # Run specific testCreative Commons Zero (CC0-1.0)