Matrix-based chat client with Spaces, Categories, and Rooms.
Status: Alpha (v0.1.0). Priorities follow GitHub Issues for this repo.
- Framework: Nuxt 4, Vue 3
- Matrix: matrix-js-sdk
- UI: Nuxt UI, Tailwind CSS (Dark Mode default)
- Tests: Vitest (Unit/Integration), Cucumber + Playwright (E2E)
- Node.js 20+
- Matrix account (e.g. matrix.org)
npm installnpm run devOpen http://localhost:3000.
By default, Decentra logs debug traces only to the browser console. You can optionally forward those traces to a remote ingest endpoint by setting environment variables (recommended via a local untracked file):
cp .env.example .env.localThen set one or more of:
NUXT_PUBLIC_DEBUG_LOG_INGEST_URL(enables remote logging)NUXT_PUBLIC_DEBUG_LOG_SESSION_ID(optional)NUXT_PUBLIC_DEBUG_LOG_SESSION_HEADER(optional, e.g.X-Debug-Session-Id)
npm run build
npm run previewProduction bundles are larger than dev because of matrix-js-sdk
(~1 MB+ minified). Vite may warn about chunks > 500 kB — that is
normal for Matrix clients. See docs/build-bundling.md
for chunk splitting, baseline sizes, and npm run build:chunks.
# Unit tests
npm run test:unit
# Integration tests
npm run test:integration
# E2E tests (builds app, starts server, runs Cucumber)
npm run test:e2e
# E2E smoke tests only (@smoke-tagged scenarios)
npm run test:e2e:smoke
# E2E with visible browser
npm run test:e2e:headed
# E2E login only (Synapse + seed + one @login scenario) — debug auth first
npm run test:e2e:login
# E2E: run one or more Cucumber scenarios in isolation (local debug, not CI)
# Requires Synapse seeded + preview on :3000 (run:debug builds and starts preview)
npm run test:e2e:run:debug -- --name "Create space from rail appears in navigation"
npm run test:e2e:cucumber:debug -- --feature tests/e2e/features/chat.feature:18
npm run test:e2e:cucumber:debug -- --help
# Coverage (currently from unit Vitest config)
npm run test:coverage
# CI fast lane (unit + integration + e2e smoke)
npm run test:ci:fast
# CI full lane (unit + integration + coverage + full e2e)
npm run test:ci:full
# Install Playwright browser (one-time)
npm run prepare:e2e| Layer | Location | Role today |
|---|---|---|
| Unit | tests/unit/ |
Main safety net (~438 tests); composables, utils, components |
| Integration | tests/integration/ |
Small module-level checks (3 files, 9 tests); not full Nuxt/Matrix wiring |
| E2E | tests/e2e/ |
Cucumber + Playwright against Docker Synapse |
Coverage: npm run test:coverage reports ~50% lines overall
(vitest.config.ts thresholds are intentionally low at 10–20% so CI
passes while pages/plugins stay mostly untested). A ~90% target is
reasonable for critical modules (Matrix client, timeline, auth) but is not
the current baseline — raise thresholds incrementally per epic/issue.
Integration gap: Issue templates mention integration tests; most features
today rely on unit + E2E. Expanding tests/integration/ (e.g. composable
chains, i18n + component mount, timeline with mocked Matrix room) is backlog
work, not part of the CI lane fix.
For credential-based E2E scenarios, create a local env file:
cp tests/e2e/.env.e2e.example tests/e2e/.env.e2e.localThen fill E2E_MATRIX_USERNAME and E2E_MATRIX_PASSWORD.
The local file stays untracked.
The full E2E lane runs many scenarios and takes several minutes. To debug
one scenario locally, use tests/e2e/scripts/run-cucumber-debug-scenario.mjs
(not invoked by CI):
| Option | Purpose |
|---|---|
--name "Scenario title" |
Match scenario name (repeat for OR) |
--feature path/to/file.feature:line |
Run scenarios from a feature file or line |
--tags "expression" |
Cucumber tag filter (e.g. not @wip) |
--help |
Show usage |
Prerequisites: Synapse up and seeded (same as full E2E), app on port 3000.
# Build + preview + one scenario
npm run test:e2e:run:debug -- --name "Create space from rail appears in navigation"
# Preview already running; Synapse + seed done
npm run test:e2e:cucumber:debug -- --feature tests/e2e/features/chat.feature:18This repo uses .github/workflows/ci.yml with two lanes:
- Fast lane: runs on push to non-
masterbranches (feature branches,develop); runstest:ci:fast(unit + integration + E2E smoke). - Full lane: runs on every pull request (targets
developormaster), push tomaster, nightly schedule, and manual dispatch withrun_full: true; runstest:ci:full(integration + coverage + full Synapse E2E via Docker).
The full lane starts a local Synapse stack in Docker and writes
tests/e2e/.env.e2e.generated during seeding — no GitHub secrets are
required for the default CI full-lane path.
For optional scenarios against an external homeserver (e.g. matrix.org), set repository secrets under Settings > Secrets and variables > Actions:
E2E_MATRIX_HOMESERVER(optional, defaults tohttps://matrix.org)E2E_MATRIX_USERNAMEE2E_MATRIX_PASSWORD
For local credential-based runs, copy tests/e2e/.env.e2e.example to
tests/e2e/.env.e2e.local and fill in username/password (see above).
- Open your repository on GitHub.
- Go to Actions.
- Select workflow CI.
- Click Run workflow.
- Set
run_fulltotrueif you want the full lane.
@nuxt/test-utils is intentionally not part of the current test runtime.
In our current dependency set, it caused peer dependency conflicts in CI while
its Nuxt runtime helpers were not actively used by our tests.
We can reintroduce it later when we add dedicated Nuxt runtime integration
tests (setupTest(), plugin/runtime integration, Nitro route tests).
High-level layout: app/ (Nuxt pages, composables, components, utils),
tests/ (unit, integration, e2e), .cursor/rules/ (CCD and workflow).
Architecture and where to change things: docs/architecture.md — Matrix client, chat UI, unread pipeline, spaces/sidebar, settings, tests, cheat sheet. docs/README.md links all project docs.
Signup uses Matrix User-Interactive Authentication against
/register: first request may return 401 with session, flows,
and params; the client keeps the session and completes stages in order.
Delegated auth (MAS / OAuth — e.g. matrix.org)
If /.well-known/matrix/client includes org.matrix.msc2965.authentication,
the homeserver typically blocks legacy POST /register for web clients but
delegates new-account creation (and related flows) to a Matrix Authentication
Service (OAuth/OIDC).
- Set
NUXT_PUBLIC_SITE_URLto your app's publichttps://origin without path (Matrix dynamic client registration rejectshttp://localhostredirects; use an HTTPS tunnel / preview URL for local OAuth). - Optional
NUXT_PUBLIC_MATRIX_OIDC_CLIENT_ID— static OAuth client id if you register one yourself. - Redirect/callback
/auth/matrix-oidc/callbackcompletes the PKCE exchange, restores the Matrix JS SDK session (matrixOidcNative.ts,useMatrixClient).
Supported stages today (legacy /register UIA)
| Stage | Notes |
|---|---|
m.login.email.identity |
Email verification link; continuation on /signup/verify-email |
m.login.recaptcha |
reCAPTCHA v2/v3 from homeserver params |
m.login.registration_token |
Opaque token; unstable ids ending in .login.registration_token treated the same |
m.login.terms |
Policy links from params; user accepts before continuing |
m.login.dummy |
Included in finalize loop when required by the server |
Explicit non-support
m.login.ssoas an in-flow stage after/registeralready returned UIA (session+ flows) — not wired in-app; where MSC2965 is advertised, prefer the delegated OAuth button on the sign-up page instead.m.login.msisdn– Phone/SMS registration is not implemented; users get a clear “not supported” message instead of failing silently.
When several flows include email verification, Decentra picks a shortest flow whose stages are all supported.
Locations: matrixRegistrationUia.ts (logic), signup.vue /
signup/verify-email.vue + SignupTermsStep.vue (UI).
Regression tests: npm run test:unit (see tests/unit/composables/matrixRegistrationUia.spec.ts
and signup page specs).
This project is licensed under the GNU Affero General Public License v3.0
or later (AGPL-3.0-or-later). See LICENSE for details.