Skip to content

Commit 49f853a

Browse files
authored
Merge Phase 0 acceptance and release readiness
Preserve the hardened foundation commit and the reviewed Phase 0 acceptance commit after all protected CI and security contexts passed.
2 parents 2ca3dcc + b135fd0 commit 49f853a

95 files changed

Lines changed: 6948 additions & 466 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ jobs:
2424
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
2525

2626
- name: Install Rust toolchain
27-
uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc # 1.92.0
27+
uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc
2828
with:
29+
toolchain: 1.93.0
2930
components: clippy, rustfmt
3031

3132
- name: Install system dependencies (Linux)
@@ -77,7 +78,9 @@ jobs:
7778
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
7879

7980
- name: Install Rust toolchain
80-
uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc # 1.92.0
81+
uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc
82+
with:
83+
toolchain: 1.93.0
8184

8285
- name: Install system dependencies
8386
run: |
@@ -149,6 +152,17 @@ jobs:
149152
working-directory: mesh
150153
run: npm run check:icons
151154

155+
- name: Check homeserver recovery scripts
156+
working-directory: mesh
157+
run: |
158+
shellcheck -e SC1007 infra/homeserver/*.sh infra/homeserver/tests/*.sh
159+
sh infra/homeserver/tests/backup-tools.test.sh
160+
python -m unittest discover -s infra/homeserver/tests -p 'test_admission_service.py' -v
161+
cp infra/homeserver/.env.example infra/homeserver/.env
162+
trap 'rm -f infra/homeserver/.env' EXIT
163+
docker compose \
164+
-f infra/homeserver/docker-compose.yml config --quiet
165+
152166
- name: Run tests
153167
working-directory: mesh
154168
run: npm run test

.github/workflows/matrix-federation-acceptance.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@ jobs:
3737
cache: npm
3838
cache-dependency-path: mesh/package-lock.json
3939

40-
- name: Install Rust 1.92
41-
uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc # 1.92.0
40+
- name: Install Rust 1.93
41+
uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc
42+
with:
43+
toolchain: 1.93.0
4244

4345
- name: Install Matrix test build dependencies
4446
run: |

.github/workflows/nightly-soak.yml

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ name: Nightly Soak
77
# 1. All 13 live libp2p tests (~70s)
88
# 2. 60-second leak-detection soak (Phase A baseline + Phase B churn)
99
# 3. 45-second topology churn soak
10+
# 4. A real PostgreSQL dump and isolated Synapse restore drill
1011
#
1112
# Total runtime: ~4 minutes per run.
1213
#
@@ -35,6 +36,19 @@ env:
3536
RUST_LOG: warn
3637

3738
jobs:
39+
homeserver-restore-drill:
40+
name: Homeserver restore drill
41+
runs-on: ubuntu-latest
42+
timeout-minutes: 15
43+
steps:
44+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
45+
46+
- name: Restore a real Synapse backup into isolated services
47+
working-directory: mesh
48+
env:
49+
MESH_RUN_RESTORE_INTEGRATION: "1"
50+
run: sh infra/homeserver/tests/restore-drill.integration.sh
51+
3852
soak:
3953
name: Long-duration soak
4054
runs-on: ubuntu-latest
@@ -43,7 +57,9 @@ jobs:
4357
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
4458

4559
- name: Install Rust toolchain
46-
uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc # 1.92.0
60+
uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc
61+
with:
62+
toolchain: 1.93.0
4763

4864
- name: Install system dependencies
4965
run: |
@@ -108,7 +124,9 @@ jobs:
108124
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
109125

110126
- name: Install Rust toolchain
111-
uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc # 1.92.0
127+
uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc
128+
with:
129+
toolchain: 1.93.0
112130

113131
- name: Install system dependencies
114132
run: |

.github/workflows/release-beta.yml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@ jobs:
3333
cache: npm
3434
cache-dependency-path: mesh/package-lock.json
3535

36-
- name: Install Rust 1.92 with Clippy
37-
uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc # 1.92.0
36+
- name: Install Rust 1.93 with Clippy
37+
uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc
3838
with:
39+
toolchain: 1.93.0
3940
components: clippy
4041

4142
- name: Install Tauri system dependencies
@@ -137,8 +138,10 @@ jobs:
137138
cache: npm
138139
cache-dependency-path: mesh/package-lock.json
139140

140-
- name: Install Rust 1.92
141-
uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc # 1.92.0
141+
- name: Install Rust 1.93
142+
uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc
143+
with:
144+
toolchain: 1.93.0
142145

143146
- name: Install frontend dependencies
144147
working-directory: mesh

.github/workflows/security.yml

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ jobs:
1515
runs-on: ubuntu-latest
1616
steps:
1717
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
18-
- uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc # 1.92.0
18+
- uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc
19+
with:
20+
toolchain: 1.93.0
1921
- name: Install Tauri system dependencies
2022
run: |
2123
sudo apt-get update
@@ -41,7 +43,9 @@ jobs:
4143
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
4244
with:
4345
fetch-depth: 0
44-
- uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc # 1.92.0
46+
- uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc
47+
with:
48+
toolchain: 1.93.0
4549
- name: Verify dependency advisory boundaries
4650
working-directory: mesh/src-tauri
4751
shell: bash
@@ -84,16 +88,22 @@ jobs:
8488
- name: Audit JavaScript dependencies
8589
working-directory: mesh
8690
run: npm audit --audit-level=high
87-
- name: Scan repository history for secrets
91+
- name: Scan repository history for non-URI secrets
8892
uses: trufflesecurity/trufflehog@18c7b1fc33e6c16b27ea66ff27cc7e642fb7cd0a # v3.90.6
8993
with:
90-
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
9199

92100
sbom:
93101
runs-on: ubuntu-latest
94102
steps:
95103
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
96-
- uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc # 1.92.0
104+
- uses: dtolnay/rust-toolchain@387aeee55b189f024a4f6d94890c5b18d73339cc
105+
with:
106+
toolchain: 1.93.0
97107
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
98108
with:
99109
node-version: 22

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ node_modules/
55
dist/
66
dist-ssr/
77
target/
8+
__pycache__/
9+
*.py[cod]
810

911
# Logs & temp output
1012
*.log

.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: 77 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@ test.afterEach(async ({ page }) => {
2121
expect(runtimeErrors.get(page) ?? [], 'authenticated shell emitted runtime errors').toEqual([])
2222
})
2323

24-
async function installAuthenticatedMatrixMock(page: Page): Promise<void> {
25-
await page.addInitScript(() => {
24+
async function installAuthenticatedMatrixMock(
25+
page: Page,
26+
currentDeepLinks: string[] | null = null,
27+
): Promise<void> {
28+
await page.addInitScript((deepLinks) => {
2629
const calls: IpcCall[] = []
2730
const callbacks = new Map<number, (...args: unknown[]) => void>()
2831
let nextCallbackId = 1
2932
let nextListenerId = 1
33+
let invitationJoined = false
3034

3135
const community = {
3236
id: '!mesh-e2e:mesh.test',
@@ -44,6 +48,14 @@ async function installAuthenticatedMatrixMock(page: Page): Promise<void> {
4448
role: 'member',
4549
joinedAt: '2026-07-24T00:00:00.000Z',
4650
}
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+
}
4759
const channels = [
4860
{
4961
id: '!general:mesh.test',
@@ -156,7 +168,20 @@ async function installAuthenticatedMatrixMock(page: Page): Promise<void> {
156168
warnings: [],
157169
}
158170
case 'matrix_list_communities':
159-
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
160185
case 'matrix_room_is_encrypted':
161186
return true
162187
case 'matrix_devices':
@@ -214,7 +239,17 @@ async function installAuthenticatedMatrixMock(page: Page): Promise<void> {
214239
matrixProfile.displayName = String(args.displayName)
215240
return { ...matrixProfile }
216241
case 'matrix_list_channels':
217-
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
218253
? secondCommunityChannels
219254
: channels
220255
case 'matrix_list_members':
@@ -287,6 +322,8 @@ async function installAuthenticatedMatrixMock(page: Page): Promise<void> {
287322
case 'matrix_clear_composer_draft':
288323
case 'plugin:event|unlisten':
289324
return null
325+
case 'plugin:deep-link|get_current':
326+
return deepLinks
290327
case 'matrix_wait_for_room_update':
291328
// The real command long-polls the SDK room-update stream. Keeping
292329
// this promise pending models that boundary without a CPU-heavy loop.
@@ -343,18 +380,23 @@ async function installAuthenticatedMatrixMock(page: Page): Promise<void> {
343380
}).__TAURI_EVENT_PLUGIN_INTERNALS__ = {
344381
unregisterListener: () => {},
345382
}
346-
})
383+
}, currentDeepLinks)
347384
}
348385

349-
async function openAuthenticatedShell(page: Page): Promise<void> {
350-
await installAuthenticatedMatrixMock(page)
386+
async function openAuthenticatedShell(
387+
page: Page,
388+
currentDeepLinks: string[] | null = null,
389+
): Promise<void> {
390+
await installAuthenticatedMatrixMock(page, currentDeepLinks)
351391
await page.goto('/')
352392
await expect(
353393
page.getByRole('navigation', { name: 'Communities and direct messages' }),
354394
).toBeVisible({ timeout: 10_000 })
355-
await expect(page.getByRole('log', { name: 'Messages in #general' })).toBeVisible({
356-
timeout: 10_000,
357-
})
395+
if (!currentDeepLinks?.length) {
396+
await expect(page.getByRole('log', { name: 'Messages in #general' })).toBeVisible({
397+
timeout: 10_000,
398+
})
399+
}
358400
}
359401

360402
function ipcCalls(page: Page): Promise<IpcCall[]> {
@@ -387,6 +429,31 @@ test.describe('authenticated desktop shell', () => {
387429
await expect(page.getByText('Anonymous', { exact: true })).toHaveCount(0)
388430
})
389431

432+
test('joins a cold-start Mesh invitation through Matrix and opens the community', async ({ page }) => {
433+
const invite =
434+
'mesh://join?v=3&kind=matrix&room=!invited:mesh.test&via=mesh.test&service=https%3A%2F%2Fmatrix.mesh.test'
435+
await openAuthenticatedShell(page, [invite])
436+
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+
455+
})
456+
390457
test('@a11y has no automated WCAG A/AA violations in the shell and settings', async ({ page }) => {
391458
await openAuthenticatedShell(page)
392459
await expectNoWcagViolations(page, 'Authenticated desktop shell')

mesh/e2e/dm-message-keyboard-actions.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,8 @@ async function installDmKeyboardActionsMock(page: Page): Promise<void> {
199199
case 'matrix_clear_composer_draft':
200200
case 'plugin:event|unlisten':
201201
return null
202+
case 'plugin:deep-link|get_current':
203+
return null
202204
case 'matrix_user_preferences':
203205
return null
204206
case 'matrix_update_user_preferences':

0 commit comments

Comments
 (0)