Skip to content

Commit b135fd0

Browse files
committed
fix: close Phase 0 acceptance gaps
1 parent fba5619 commit b135fd0

28 files changed

Lines changed: 612 additions & 194 deletions

.github/workflows/security.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,14 @@ jobs:
8888
- name: Audit JavaScript dependencies
8989
working-directory: mesh
9090
run: npm audit --audit-level=high
91-
- name: Scan repository history for secrets
91+
- name: Scan repository history for non-URI secrets
9292
uses: trufflesecurity/trufflehog@18c7b1fc33e6c16b27ea66ff27cc7e642fb7cd0a # v3.90.6
9393
with:
94-
extra_args: --results=verified,unknown
94+
extra_args: --results=verified,unknown --exclude-detectors=URI
95+
- name: Scan repository history for credential-bearing URIs
96+
uses: trufflesecurity/trufflehog@18c7b1fc33e6c16b27ea66ff27cc7e642fb7cd0a # v3.90.6
97+
with:
98+
extra_args: --results=verified,unknown --include-detectors=URI --exclude-paths=.trufflehog-uri-excludes
9599

96100
sbom:
97101
runs-on: ubuntu-latest

.trufflehog-uri-excludes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
^mesh/src/lib/community-invites\.test\.ts$
2+
^mesh/src-tauri/src/backend/matrix/tests/mod\.rs$

AGENTS.md

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,18 @@ replication.
1414

1515
## Non-negotiable UX rules
1616

17-
- Ship safe, working defaults for the recommended Mesh service in release
18-
builds.
17+
- Ship a simple, explicit account-service choice in release builds: Matrix.org
18+
as a prominent public option, a community-hosted service when an invitation
19+
offers one, additional reviewed public options, and "use another service".
20+
- Do not silently select an account service or imply that an independently
21+
operated public service is owned, endorsed, or guaranteed by Mesh.
1922
- Invitation links must carry or resolve everything needed to reach the
20-
service and community.
23+
community, but must not force the invitee's account to live on the
24+
community's homeserver. Account hosting and community hosting are separate.
2125
- Use plain product language such as "service", "community", and "voice";
2226
reserve protocol terminology for diagnostics and advanced settings.
23-
- Keep "sign in somewhere else" and custom homeserver support available as an
24-
advanced path so decentralization does not become lock-in.
27+
- Keep "use another service" and custom homeserver support as a first-class
28+
path so decentralization does not become lock-in.
2529
- Detect service capabilities and network conditions automatically. Do not ask
2630
users to make infrastructure decisions that Mesh can make safely.
2731
- Errors must explain what the user can do next without exposing raw protocol
@@ -34,8 +38,15 @@ replication.
3438

3539
## Architecture guardrails
3640

37-
- The managed Mesh homeserver is the zero-configuration default, not the only
38-
compatible service.
41+
- Mesh must not require a paid Mesh-operated homeserver. Matrix.org is a
42+
prominent independently operated public option; the Mac mini is an optional
43+
community-hosted/bring-your-own service with no uptime SLA; and arbitrary
44+
compatible homeservers remain supported.
45+
- Keep the selected account service, community room-routing servers, and any
46+
optional community admission/registration service separate in configuration
47+
and code.
48+
- A user whose account is hosted on one compatible service must be able to join
49+
a community hosted on another through federation when room policy permits.
3950
- Keep identity, membership, permissions, text history, encryption state, and
4051
synchronization on the Matrix-compatible control plane.
4152
- Treat peer-assisted storage as an optional encrypted data plane with durable

mesh/e2e/authenticated-shell.spec.ts

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ async function installAuthenticatedMatrixMock(
3030
const callbacks = new Map<number, (...args: unknown[]) => void>()
3131
let nextCallbackId = 1
3232
let nextListenerId = 1
33+
let invitationJoined = false
3334

3435
const community = {
3536
id: '!mesh-e2e:mesh.test',
@@ -47,6 +48,14 @@ async function installAuthenticatedMatrixMock(
4748
role: 'member',
4849
joinedAt: '2026-07-24T00:00:00.000Z',
4950
}
51+
const invitedCommunity = {
52+
id: '!invited:mesh.test',
53+
name: 'Invited Mesh Community',
54+
description: 'Joined from the cold-start invitation',
55+
memberCount: 4,
56+
role: 'member',
57+
joinedAt: '2026-07-29T00:00:00.000Z',
58+
}
5059
const channels = [
5160
{
5261
id: '!general:mesh.test',
@@ -159,7 +168,20 @@ async function installAuthenticatedMatrixMock(
159168
warnings: [],
160169
}
161170
case 'matrix_list_communities':
162-
return [community, secondCommunity]
171+
return invitationJoined
172+
? [community, secondCommunity, invitedCommunity]
173+
: [community, secondCommunity]
174+
case 'matrix_join_community':
175+
if (
176+
args.roomOrAlias !== invitedCommunity.id
177+
|| !Array.isArray(args.via)
178+
|| args.via.length !== 1
179+
|| args.via[0] !== 'mesh.test'
180+
) {
181+
throw new Error('Cold-start invitation was not forwarded to Matrix correctly')
182+
}
183+
invitationJoined = true
184+
return invitedCommunity
163185
case 'matrix_room_is_encrypted':
164186
return true
165187
case 'matrix_devices':
@@ -217,7 +239,17 @@ async function installAuthenticatedMatrixMock(
217239
matrixProfile.displayName = String(args.displayName)
218240
return { ...matrixProfile }
219241
case 'matrix_list_channels':
220-
return args.communityId === secondCommunity.id
242+
return args.communityId === invitedCommunity.id
243+
? [
244+
{
245+
id: '!invited-general:mesh.test',
246+
communityId: invitedCommunity.id,
247+
name: 'welcome',
248+
channelType: 'text',
249+
unreadCount: 0,
250+
},
251+
]
252+
: args.communityId === secondCommunity.id
221253
? secondCommunityChannels
222254
: channels
223255
case 'matrix_list_members':
@@ -360,9 +392,11 @@ async function openAuthenticatedShell(
360392
await expect(
361393
page.getByRole('navigation', { name: 'Communities and direct messages' }),
362394
).toBeVisible({ timeout: 10_000 })
363-
await expect(page.getByRole('log', { name: 'Messages in #general' })).toBeVisible({
364-
timeout: 10_000,
365-
})
395+
if (!currentDeepLinks?.length) {
396+
await expect(page.getByRole('log', { name: 'Messages in #general' })).toBeVisible({
397+
timeout: 10_000,
398+
})
399+
}
366400
}
367401

368402
function ipcCalls(page: Page): Promise<IpcCall[]> {
@@ -395,15 +429,29 @@ test.describe('authenticated desktop shell', () => {
395429
await expect(page.getByText('Anonymous', { exact: true })).toHaveCount(0)
396430
})
397431

398-
test('routes a cold-start Mesh invitation into the prefilled join flow', async ({ page }) => {
432+
test('joins a cold-start Mesh invitation through Matrix and opens the community', async ({ page }) => {
399433
const invite =
400434
'mesh://join?v=3&kind=matrix&room=!invited:mesh.test&via=mesh.test&service=https%3A%2F%2Fmatrix.mesh.test'
401435
await openAuthenticatedShell(page, [invite])
402436

403-
const dialog = page.getByRole('dialog', { name: 'Join a community' })
404-
await expect(dialog).toBeVisible()
405-
await expect(dialog.getByLabel('Invite code or link')).toHaveValue(invite)
406-
await expect(dialog.getByRole('button', { name: 'Join Community' })).toBeEnabled()
437+
await expect.poll(async () => (
438+
(await ipcCalls(page)).filter((call) => call.command === 'matrix_join_community')
439+
)).toEqual([{
440+
command: 'matrix_join_community',
441+
args: {
442+
roomOrAlias: '!invited:mesh.test',
443+
via: ['mesh.test'],
444+
},
445+
}])
446+
await expect(
447+
page.getByRole('button', { name: 'Invited Mesh Community', exact: true }),
448+
).toHaveAttribute('aria-current', 'true')
449+
await expect(page.getByRole('button', { name: 'Text room: welcome' })).toHaveAttribute(
450+
'aria-current',
451+
'page',
452+
)
453+
await expect(page.getByRole('dialog', { name: 'Join a community' })).toHaveCount(0)
454+
407455
})
408456

409457
test('@a11y has no automated WCAG A/AA violations in the shell and settings', async ({ page }) => {

0 commit comments

Comments
 (0)