Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

644 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

The Desk — open source, self-hosted team chat

A calm, fast, self-hosted team chat app you run yourself — workspaces, channels, threads, reactions, search, reminders, and scheduled messages. Built with Laravel 13, Inertia + Vue 3, Laravel Reverb (WebSockets), and Meilisearch.

License: MIT PHP 8.5 Laravel 13 Vue 3

Tests Latest release Latest release date Last commit GitHub stars

Live demo · Website · Docs · Install · vs Slack · MIT License

The Desk: a channel timeline with threads, reactions, and direct messages

Self-hosting

The Desk ships as a single Docker Compose stack with a prebuilt image. The installer fetches the compose file, generates secrets, and pins the latest release, then up -d runs it with no build step:

curl -fsSL https://raw.githubusercontent.com/deskhq/the-desk/master/docker/install.sh | sh
# edit .env (APP_URL, mail, REVERB_*_PUBLIC), then:
docker compose up -d

Full operator docs (requirements, configuration, reverse proxy & TLS, and upgrades) live at docs.thedeskhq.app.

Public demo

Try it live at demo.thedeskhq.app — the login page shows the shared credentials.

Set DEMO_MODE=true to run a public, single-shared-account demo off the seeded "Northwind Labs" workspace (php artisan demo:seed). Every visitor signs in as the same owner, so the mode adds guard rails: destructive owner actions are blocked, all outbound email is swallowed, message/attachment writes are rate-limited per IP, self-registration is forced off, and an hourly reset heals the workspace. It defaults to false — leave it off on any real deployment. See Running a public demo.

Development

Local development uses Laravel Sail:

git clone https://github.com/deskhq/the-desk.git
cd the-desk
cp .env.example .env
composer install
./vendor/bin/sail up -d
./vendor/bin/sail composer setup

Run the quality gate before pushing:

./vendor/bin/sail composer test        # Pint, PHPStan, Rector (dry-run), and tests at 100% coverage
./vendor/bin/sail npm run lint:check   # ESLint
./vendor/bin/sail npm run format:check # Prettier
./vendor/bin/sail npm run types:check  # vue-tsc
./vendor/bin/sail npm run test:js      # Vitest unit suite (composables, lib helpers, eslint rules)
./vendor/bin/sail npm run build        # Vite build

./vendor/bin/sail composer ci:check runs both gates in one command.

Rector handles automated structural refactoring (the semantic counterpart to Pint's formatter). The gate runs it in dry-run mode; when it reports pending changes, apply them and re-run the gate:

./vendor/bin/sail composer refactor    # apply Rector's suggested refactors

Browser (E2E) realtime tests

tests/Browser holds Pest 4 browser tests that drive real Playwright browsers against the app served in-process, with two clients exchanging messages over a live Reverb server — the realtime send/receive, typing, edit, and delete paths that headless feature tests can't reach. They live in a separate browser test group excluded from the coverage gate, so composer test never runs them (and they never affect the 100% coverage requirement). CI runs them in a dedicated browser job.

Prerequisites (one-time, inside the Sail container):

./vendor/bin/sail npm ci                              # playwright npm package
./vendor/bin/sail npx playwright install chromium     # the browser binary

Then, with Sail up (Reverb is part of sail up -d) and the frontend built:

./vendor/bin/sail npm run build                       # tests use the built assets
./vendor/bin/sail composer test:browser               # runs bin/browser-tests

composer test:browser runs the suite through bin/browser-tests, which shards it across paratest workers (~4.4x faster than the single-process run it replaced). The worker count is capped at half the available cores, never below two: leaving --parallel to its own default runs one worker per core, and browsers, PHP servers and Playwright then compete for the same cores, which makes the suite both slower and flakier. Pin a different count with BROWSER_TEST_PROCESSES=N to measure another setting.

Both suites stop at the first defect, so a run whose verdict is already decided no longer costs the full wall clock. Under paratest that stops scheduling new work rather than killing what is in flight, so expect a handful of results after the first rather than a hard stop. When you want the whole picture instead (a broad refactor, a flaky sweep, a triage run), turn it off:

BROWSER_TEST_BAIL=0 ./vendor/bin/sail composer test:browser        # browser suite
./vendor/bin/sail artisan test --parallel --coverage --min=100     # php suite

The same runner reaps the processes a run leaves behind — the Playwright server every run used to leak, plus the paratest workers a killed run strands — both before it starts and once it ends, naming each one it kills. Left alone those leftovers eat the cores the cap is there to protect, so a machine that had run a few sweeps failed unrelated tests as though the application had regressed. Only processes orphaned onto init and running out of this checkout are touched, so a second suite (or the PHP gate, whose workers look identical) is safe alongside.

Rebuild the frontend (./vendor/bin/sail npm run build) after changing any Vue component the tests touch, since the in-process server serves the compiled Vite assets — and after pulling someone else's, for the same reason. You don't have to remember: the suite compares public/build/manifest.json against everything Vite bundles and stops the run with that command if the bundle is behind, rather than letting a stale bundle fail the tests as though the app were broken.

The live Reverb server gets the same treatment. Running the PHP gate can leave the reverb container stopped, and with no WebSocket server the realtime tests fail one by one on the message each was waiting for, which reads as a broadcasting regression rather than as the absent server it is. The suite therefore probes the endpoint once before the first test and stops with ./vendor/bin/sail up -d reverb if nothing answers.

Local SSO providers (OIDC & LDAP)

Two opt-in dev containers let you exercise the single sign-on flows against real providers locally, without registering an app at an external IdP. They sit behind the sso compose profile, so a plain sail up never starts them. Enable them by setting COMPOSE_PROFILES=sso in your .env (or sail up --profile sso), then uncomment the matching dev values in .env.example:

  • OIDC — a mock provider (soluto/oidc-server-mock) seeded with [email protected][email protected] (password password) and a pre-registered the-desk-dev client. It's reached as oidc:8081 from both the browser and the app container so the derived issuer stays consistent, so add one line to your host's /etc/hosts once:

    127.0.0.1 oidc
    

    Then set SSO_OIDC_ISSUER=http://oidc:8081, SSO_OIDC_CLIENT_ID=the-desk-dev, SSO_OIDC_CLIENT_SECRET=the-desk-dev-secret and use "Sign in with SSO".

  • LDAP — an OpenLDAP directory (osixia/openldap) seeded with [email protected][email protected] (password password) under dc=the-desk,dc=local. Uncomment the dev block in .env.example:

    LDAP_HOST=ldap
    LDAP_PORT=389
    LDAP_BASE_DN="dc=the-desk,dc=local"
    LDAP_USERNAME="cn=admin,dc=the-desk,dc=local"
    LDAP_PASSWORD=adminpassword
    LDAP_ATTR_GUID=entryuuid   # OpenLDAP's stable id, not AD's objectGUID
    

    Then sign in through the normal login form with a seeded email.

Contributing

Contributions are welcome — bug reports, docs fixes, and pull requests. Please read CONTRIBUTING.md for the development setup, the quality gates (100% test coverage, Pint/PHPStan/Rector, and the frontend checks), the Conventional Commits convention, and the PR workflow.

Security

Found a vulnerability? Please report it privately through GitHub's private vulnerability reporting rather than opening a public issue. See SECURITY.md for the full policy, supported versions, and response timeline. The codebase is scanned continuously with CodeQL, dependency review, and Dependabot; findings surface in the Security tab.

License

The Desk is open source under the MIT License.

About

Calm, fast, self-hosted team chat you run yourself — channels, threads, reminders, scheduled messages. One Docker compose file, MIT-licensed. Laravel + Vue + Reverb.

Topics

Resources

Contributing

Security policy

Stars

20 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages