refactor: re-point paths after the Client flatten (mechanical, no behaviour change)

This commit is contained in:
Claude
2026-08-26 06:14:07 +00:00
parent 4befe699ce
commit 38ddca738a
33 changed files with 983 additions and 983 deletions
+2 -2
View File
@@ -34,7 +34,7 @@ fault, same verdict, especially when the diff touches no Go code. Rerun the
job (`gh run rerun --job <id>`); a job cannot be rerun while its parent run is job (`gh run rerun --job <id>`); a job cannot be rerun while its parent run is
still in progress. still in progress.
## Client (from `Client/tauri-client/`) ## Client (from `Client/`)
```bash ```bash
NODE_OPTIONS=--no-experimental-webstorage npm test NODE_OPTIONS=--no-experimental-webstorage npm test
@@ -47,7 +47,7 @@ The `NODE_OPTIONS` flag is mandatory on Node 22+ — see the client CLAUDE.md.
`npm audit --audit-level=high` and `knip` also run in CI but are advisory. `npm audit --audit-level=high` and `knip` also run in CI but are advisory.
## Rust (from `Client/tauri-client/src-tauri/`) ## Rust (from `Client/src-tauri/`)
```bash ```bash
cargo test cargo test
+3 -3
View File
@@ -1,6 +1,6 @@
--- ---
name: protocol-change name: protocol-change
description: Add or change a WebSocket message type in OwnCord. Use before editing docs/protocol-schema.json, Server/ws/message_types.go, or Client/tauri-client/src/lib/protocolTypes.ts. description: Add or change a WebSocket message type in OwnCord. Use before editing docs/protocol-schema.json, Server/ws/message_types.go, or Client/src/lib/protocolTypes.ts.
--- ---
# protocol-change # protocol-change
@@ -11,7 +11,7 @@ generated from it by `Server/scripts/genprotocol/`.
1. Edit `docs/protocol-schema.json`. 1. Edit `docs/protocol-schema.json`.
2. Run `make protocol-generate` from `Server/`. 2. Run `make protocol-generate` from `Server/`.
3. Commit **both** outputs — `Server/ws/message_types.go` and 3. Commit **both** outputs — `Server/ws/message_types.go` and
`Client/tauri-client/src/lib/protocolTypes.ts`. One run regenerates the `Client/src/lib/protocolTypes.ts`. One run regenerates the
pair; committing only the Go side is the usual mistake, and CI's pair; committing only the Go side is the usual mistake, and CI's
`make protocol-verify` fails on either being stale. `make protocol-verify` fails on either being stale.
@@ -20,4 +20,4 @@ shapes, not behaviour.
Adding a message type is not enough to make it work: a server handler must be Adding a message type is not enough to make it work: a server handler must be
registered in the `ws` V1/V2 dispatch tables, and the client needs a registered in the `ws` V1/V2 dispatch tables, and the client needs a
`ws.on(...)` subscription in `Client/tauri-client/src/lib/dispatcher.ts`. `ws.on(...)` subscription in `Client/src/lib/dispatcher.ts`.
+6 -6
View File
@@ -46,7 +46,7 @@ export async function run({ agentStub, args = undefined, budget = undefined }) {
export const rec = (id, over = {}) => ({ export const rec = (id, over = {}) => ({
id, id,
title: `bug ${id}`, title: `bug ${id}`,
file: 'Client/tauri-client/src/lib/livekitE2EE.ts', file: 'Client/src/lib/livekitE2EE.ts',
line: 100, line: 100,
severity: 'high', severity: 'high',
why: 'w', why: 'w',
@@ -78,7 +78,7 @@ scenarios.f1_clusters_by_file = async () => {
assert.equal(result.branch, 'fix/test') assert.equal(result.branch, 'fix/test')
assert.equal(result.clusters.length, 2) assert.equal(result.clusters.length, 2)
const byFile = Object.fromEntries(result.clusters.map((c) => [c.file, c.ids])) const byFile = Object.fromEntries(result.clusters.map((c) => [c.file, c.ids]))
assert.deepEqual(byFile['Client/tauri-client/src/lib/livekitE2EE.ts'], ['OC-0001', 'OC-0002']) assert.deepEqual(byFile['Client/src/lib/livekitE2EE.ts'], ['OC-0001', 'OC-0002'])
assert.deepEqual(byFile['Server/ws/hub_sweep.go'], ['OC-0003']) assert.deepEqual(byFile['Server/ws/hub_sweep.go'], ['OC-0003'])
} }
@@ -580,7 +580,7 @@ scenarios.f14_disjoint_touched_paths_both_commit = async () => {
if (opts.label.includes('livekitE2EE')) if (opts.label.includes('livekitE2EE'))
return { return {
results: [{ id: 'OC-0001', outcome: 'fixed', testPath: 't/OC-0001.test.ts', rationale: '' }], results: [{ id: 'OC-0001', outcome: 'fixed', testPath: 't/OC-0001.test.ts', rationale: '' }],
touchedPaths: ['Client/tauri-client/src/lib/otherHelper.ts'], touchedPaths: ['Client/src/lib/otherHelper.ts'],
} }
return { return {
results: [{ id: 'OC-0003', outcome: 'fixed', testPath: 'Server/ws/hub_sweep_test.go', rationale: '' }], results: [{ id: 'OC-0003', outcome: 'fixed', testPath: 'Server/ws/hub_sweep_test.go', rationale: '' }],
@@ -616,7 +616,7 @@ scenarios.f15_prove_prompt_names_every_touched_path = async () => {
if (String(opts.label).startsWith('fix:')) if (String(opts.label).startsWith('fix:'))
return { return {
results: [{ id: 'OC-0001', outcome: 'fixed', testPath: 't/OC-0001.test.ts', rationale: '' }], results: [{ id: 'OC-0001', outcome: 'fixed', testPath: 't/OC-0001.test.ts', rationale: '' }],
touchedPaths: ['Client/tauri-client/src/lib/sharedCrypto.ts'], touchedPaths: ['Client/src/lib/sharedCrypto.ts'],
} }
if (String(opts.label).startsWith('prove:')) { if (String(opts.label).startsWith('prove:')) {
provePromptText = prompt provePromptText = prompt
@@ -651,8 +651,8 @@ scenarios.f15_prove_prompt_names_every_touched_path = async () => {
const FOUR_FILES = [ const FOUR_FILES = [
rec('OC-0001'), rec('OC-0001'),
rec('OC-0002', { file: 'Server/ws/hub_sweep.go' }), rec('OC-0002', { file: 'Server/ws/hub_sweep.go' }),
rec('OC-0003', { file: 'Client/tauri-client/src/lib/livekitSession.ts' }), rec('OC-0003', { file: 'Client/src/lib/livekitSession.ts' }),
rec('OC-0004', { file: 'Client/tauri-client/src/components/VoiceWidget.ts' }), rec('OC-0004', { file: 'Client/src/components/VoiceWidget.ts' }),
] ]
// A fix stub that reports every id in its prompt as fixed. Ids go through a Set because rec() // A fix stub that reports every id in its prompt as fixed. Ids go through a Set because rec()
// puts each id in both `id` and `title`, so the raw matchAll yields every id twice. // puts each id in both `id` and `title`, so the raw matchAll yields every id twice.
+6 -6
View File
@@ -150,7 +150,7 @@ function fixPrompt(cluster) {
`alone, return outcome "declined" with a rationale. Do not invent a fix you do not believe in.\n` + `alone, return outcome "declined" with a rationale. Do not invent a fix you do not believe in.\n` +
` 8. If you cannot fix it for a mechanical reason (missing fixture, unclear repro), return "blocked" ` + ` 8. If you cannot fix it for a mechanical reason (missing fixture, unclear repro), return "blocked" ` +
`with a rationale.\n\n` + `with a rationale.\n\n` +
`Client tests run from Client/tauri-client with:\n` + `Client tests run from Client with:\n` +
` NODE_OPTIONS=--no-experimental-webstorage npx vitest run <testfile>\n` + ` NODE_OPTIONS=--no-experimental-webstorage npx vitest run <testfile>\n` +
`Server tests run from Server with:\n` + `Server tests run from Server with:\n` +
` go test ./<pkg>/ -run <TestName>\n\n` + ` go test ./<pkg>/ -run <TestName>\n\n` +
@@ -336,7 +336,7 @@ function provePrompt(cluster, fixedIds, testPaths, sourcePaths) {
` Use a conventional-commit area matching the file (voice, ws, client, identity...). Do not add a ` + ` Use a conventional-commit area matching the file (voice, ws, client, identity...). Do not add a ` +
`Co-Authored-By trailer.\n` + `Co-Authored-By trailer.\n` +
` 9. Return the short sha.\n\n` + ` 9. Return the short sha.\n\n` +
`Client tests run from Client/tauri-client with:\n` + `Client tests run from Client with:\n` +
` NODE_OPTIONS=--no-experimental-webstorage npx vitest run <testfile>\n` + ` NODE_OPTIONS=--no-experimental-webstorage npx vitest run <testfile>\n` +
`Server tests run from Server with:\n` + `Server tests run from Server with:\n` +
` go test ./<pkg>/ -run <TestName>` ` go test ./<pkg>/ -run <TestName>`
@@ -433,7 +433,7 @@ function stacksFor(files) {
const s = new Set() const s = new Set()
for (const f of files) { for (const f of files) {
if (f.startsWith('Server/')) s.add('server') if (f.startsWith('Server/')) s.add('server')
else if (f.startsWith('Client/tauri-client/src-tauri/')) s.add('rust') else if (f.startsWith('Client/src-tauri/')) s.add('rust')
else if (f.startsWith('Client/')) s.add('client') else if (f.startsWith('Client/')) s.add('client')
} }
return [...s] return [...s]
@@ -441,7 +441,7 @@ function stacksFor(files) {
const GATE_COMMANDS = { const GATE_COMMANDS = {
client: client:
`From Client/tauri-client:\n` + `From Client:\n` +
` NODE_OPTIONS=--no-experimental-webstorage npm test\n` + ` NODE_OPTIONS=--no-experimental-webstorage npm test\n` +
` npm run typecheck\n` + ` npm run typecheck\n` +
` npm run lint\n` + ` npm run lint\n` +
@@ -456,10 +456,10 @@ const GATE_COMMANDS = {
` make sqlc-verify protocol-verify # generated output must not be stale. If make is not on PATH, ` + ` make sqlc-verify protocol-verify # generated output must not be stale. If make is not on PATH, ` +
`run the equivalent commands directly instead: ` + `run the equivalent commands directly instead: ` +
`"sqlc generate && git diff --exit-code db/dbgen" and ` + `"sqlc generate && git diff --exit-code db/dbgen" and ` +
`"go run ./scripts/genprotocol && git diff --exit-code ws/message_types.go ../Client/tauri-client/src/lib/protocolTypes.ts" ` + `"go run ./scripts/genprotocol && git diff --exit-code ws/message_types.go ../Client/src/lib/protocolTypes.ts" ` +
`- a non-empty diff in either means generated code is stale and the gate fails`, `- a non-empty diff in either means generated code is stale and the gate fails`,
rust: rust:
`From Client/tauri-client/src-tauri:\n` + `From Client/src-tauri:\n` +
` cargo test\n` + ` cargo test\n` +
` cargo clippy --all-targets -- -D warnings`, ` cargo clippy --all-targets -- -D warnings`,
} }
+3 -3
View File
@@ -58,7 +58,7 @@ export function makeStub({ hunt, verify, recon = defaultRecon }) {
} }
} }
export function defaultRecon() { export function defaultRecon() {
return 'Server/ws/hub.go 12\nServer/api/user.go 9\nClient/tauri-client/src/lib/dispatcher.ts 8' return 'Server/ws/hub.go 12\nServer/api/user.go 9\nClient/src/lib/dispatcher.ts 8'
} }
export const none = { findings: [] } export const none = { findings: [] }
export const finding = (n, over = {}) => ({ export const finding = (n, over = {}) => ({
@@ -363,7 +363,7 @@ scenarios.s_graph_missing_fallback = async () => {
scenarios.s7_adaptive_lenses = async () => { scenarios.s7_adaptive_lenses = async () => {
const A = finding(1, { file: 'Server/ws/hub.go', line: 120, title: 'alpha race window one' }) const A = finding(1, { file: 'Server/ws/hub.go', line: 120, title: 'alpha race window one' })
const B = finding(2, { file: 'Server/ws/pubsub.go', line: 60, title: 'beta subscription leak two' }) const B = finding(2, { file: 'Server/ws/pubsub.go', line: 60, title: 'beta subscription leak two' })
const C = finding(3, { file: 'Client/tauri-client/src/lib/livekitE2EE.ts', line: 200, title: 'gamma epoch desync three' }) const C = finding(3, { file: 'Client/src/lib/livekitE2EE.ts', line: 200, title: 'gamma epoch desync three' })
const { result, calls } = await run({ const { result, calls } = await run({
agentStub: makeStub({ agentStub: makeStub({
hunt: (round, key) => { hunt: (round, key) => {
@@ -380,7 +380,7 @@ scenarios.s7_adaptive_lenses = async () => {
assert.equal(result.rounds[3].family, 'adaptive') assert.equal(result.rounds[3].family, 'adaptive')
const r4Keys = [...new Set(calls.filter((c) => /^r4:hunt:/.test(c.opts.label || '')).map((c) => c.opts.label.split(':')[2]))] const r4Keys = [...new Set(calls.filter((c) => /^r4:hunt:/.test(c.opts.label || '')).map((c) => c.opts.label.split(':')[2]))]
assert.ok(r4Keys.includes('hotspot-server-ws'), `r4 keys: ${r4Keys}`) assert.ok(r4Keys.includes('hotspot-server-ws'), `r4 keys: ${r4Keys}`)
assert.ok(r4Keys.includes('hotspot-client-tauri-client-src-lib'), `r4 keys: ${r4Keys}`) assert.ok(r4Keys.includes('hotspot-client-src-lib'), `r4 keys: ${r4Keys}`)
assert.ok(r4Keys.includes('explore-1'), `r4 keys: ${r4Keys}`) assert.ok(r4Keys.includes('explore-1'), `r4 keys: ${r4Keys}`)
const hotspot = calls.find((c) => (c.opts.label || '').includes('hotspot-server-ws')) const hotspot = calls.find((c) => (c.opts.label || '').includes('hotspot-server-ws'))
assert.match(hotspot.prompt, /Server\/ws\/hub\.go/) assert.match(hotspot.prompt, /Server\/ws\/hub\.go/)
+7 -7
View File
@@ -87,7 +87,7 @@ const VERDICTS = {
// ---------- rules ---------- // ---------- rules ----------
const RULES = ` const RULES = `
Repo: OwnCord, checked out at your current working directory (the repo root - do not assume any absolute Repo: OwnCord, checked out at your current working directory (the repo root - do not assume any absolute
path; run every command from there and use repo-relative paths). Go 1.26 server in Server/, Tauri v2 client in Client/tauri-client/ path; run every command from there and use repo-relative paths). Go 1.26 server in Server/, Tauri v2 client in Client/
(Rust in src-tauri/src/, TypeScript in src/lib/ and src/stores/). (Rust in src-tauri/src/, TypeScript in src/lib/ and src/stores/).
You are hunting REAL BUGS: wrong behavior, not style. In scope: You are hunting REAL BUGS: wrong behavior, not style. In scope:
@@ -132,8 +132,8 @@ const SURFACE_LENSES = [
key: 'voice-e2ee', key: 'voice-e2ee',
prompt: prompt:
`Surface: voice/video E2EE key lifecycle, spanning three languages. Files: Server/ws/handler_v2_voice*.go and ` + `Surface: voice/video E2EE key lifecycle, spanning three languages. Files: Server/ws/handler_v2_voice*.go and ` +
`any Server/ws/*voice*.go or *e2ee*.go; Client/tauri-client/src/lib/e2eeCrypto.ts, livekitE2EE.ts, ` + `any Server/ws/*voice*.go or *e2ee*.go; Client/src/lib/e2eeCrypto.ts, livekitE2EE.ts, ` +
`livekitSession.ts, identity.ts; Client/tauri-client/src-tauri/src/tofu.rs, secret_store.rs, fallback_crypto.rs, dpapi.rs.\n\n` + `livekitSession.ts, identity.ts; Client/src-tauri/src/tofu.rs, secret_store.rs, fallback_crypto.rs, dpapi.rs.\n\n` +
`Hunt specifically for: a key-rotation window where a participant can decrypt after they should be excluded; ` + `Hunt specifically for: a key-rotation window where a participant can decrypt after they should be excluded; ` +
`TOFU pin re-check that reads state captured before a rotation (time-of-check/time-of-use); a participant ` + `TOFU pin re-check that reads state captured before a rotation (time-of-check/time-of-use); a participant ` +
`joining mid-rotation getting the wrong epoch key; key material outliving the session; an error path that ` + `joining mid-rotation getting the wrong epoch key; key material outliving the session; an error path that ` +
@@ -167,7 +167,7 @@ const SURFACE_LENSES = [
{ {
key: 'tauri-rust', key: 'tauri-rust',
prompt: prompt:
`Surface: the Tauri Rust backend. Files: Client/tauri-client/src-tauri/src/*.rs.\n\n` + `Surface: the Tauri Rust backend. Files: Client/src-tauri/src/*.rs.\n\n` +
`Hunt specifically for: a panic reachable from a Tauri command (unwrap/expect on attacker- or ` + `Hunt specifically for: a panic reachable from a Tauri command (unwrap/expect on attacker- or ` +
`environment-controlled input) - a panic here can take down the app; a lock held across .await; ` + `environment-controlled input) - a panic here can take down the app; a lock held across .await; ` +
`state in tauri::State mutated from two commands without coordination; the http_proxy / livekit_proxy / ` + `state in tauri::State mutated from two commands without coordination; the http_proxy / livekit_proxy / ` +
@@ -178,7 +178,7 @@ const SURFACE_LENSES = [
{ {
key: 'client-state', key: 'client-state',
prompt: prompt:
`Surface: TypeScript client state and event handling. Files: Client/tauri-client/src/lib/*.ts and ` + `Surface: TypeScript client state and event handling. Files: Client/src/lib/*.ts and ` +
`src/stores/*.ts - prioritize dispatcher.ts, reconcile.ts, read-state.ts, router.ts, roomEventHandlers.ts, ` + `src/stores/*.ts - prioritize dispatcher.ts, reconcile.ts, read-state.ts, router.ts, roomEventHandlers.ts, ` +
`navigation-guard.ts, rate-limiter.ts, channel-navigation.ts, and whatever the churn recon flagged.\n\n` + `navigation-guard.ts, rate-limiter.ts, channel-navigation.ts, and whatever the churn recon flagged.\n\n` +
`Hunt specifically for: a listener/interval/observer registered without a matching teardown (check ` + `Hunt specifically for: a listener/interval/observer registered without a matching teardown (check ` +
@@ -505,8 +505,8 @@ const recon = await parallel([
`${RULES}\n\nRECON TASK (mechanical, do not hunt bugs yourself):\n` + `${RULES}\n\nRECON TASK (mechanical, do not hunt bugs yourself):\n` +
`Inventory the concurrency and lifecycle surface so the finders know where to look. Report:\n` + `Inventory the concurrency and lifecycle surface so the finders know where to look. Report:\n` +
` (a) every Server/ non-test .go file containing "go func", "sync.", "chan ", "select {", or "context.WithCancel"\n` + ` (a) every Server/ non-test .go file containing "go func", "sync.", "chan ", "select {", or "context.WithCancel"\n` +
` (b) every Client/tauri-client/src/**/*.ts (non-test) containing "addEventListener", "setInterval", "setTimeout", or "new AbortController"\n` + ` (b) every Client/src/**/*.ts (non-test) containing "addEventListener", "setInterval", "setTimeout", or "new AbortController"\n` +
` (c) every Client/tauri-client/src-tauri/src/*.rs containing "unsafe", "Mutex", "RwLock", "spawn", or "unwrap()"\n` + ` (c) every Client/src-tauri/src/*.rs containing "unsafe", "Mutex", "RwLock", "spawn", or "unwrap()"\n` +
`For each file give the path and a rough hit count. Return plain text grouped under (a)/(b)/(c). No commentary, no analysis.`, `For each file give the path and a rough hit count. Return plain text grouped under (a)/(b)/(c). No commentary, no analysis.`,
{ label: 'recon:surface', phase: 'Recon', model: 'haiku', effort: 'xhigh' }, { label: 'recon:surface', phase: 'Recon', model: 'haiku', effort: 'xhigh' },
), ),
+5 -5
View File
@@ -51,13 +51,13 @@ if printf '%s\n' "$staged" | grep -qE '^(docs/protocol-schema\.json|Server/scrip
fi fi
# ---------- Client (TypeScript) ---------- # ---------- Client (TypeScript) ----------
ts_staged=$(printf '%s\n' "$staged" | grep -E '^Client/tauri-client/(src|tests)/.*\.ts$' | grep -v '/generated/') ts_staged=$(printf '%s\n' "$staged" | grep -E '^Client/(src|tests)/.*\.ts$' | grep -v '/generated/')
if [ -n "$ts_staged" ]; then if [ -n "$ts_staged" ]; then
if [ ! -d Client/tauri-client/node_modules ]; then if [ ! -d Client/node_modules ]; then
printf 'pre-commit: WARNING: node_modules missing in Client/tauri-client; skipping client checks (run npm install there).\n' >&2 printf 'pre-commit: WARNING: node_modules missing in Client; skipping client checks (run npm install there).\n' >&2
else else
rel=$(printf '%s\n' "$ts_staged" | sed 's|^Client/tauri-client/||') rel=$(printf '%s\n' "$ts_staged" | sed 's|^Client/||')
cd Client/tauri-client || exit 1 cd Client || exit 1
# shellcheck disable=SC2086 # shellcheck disable=SC2086
npx oxlint $rel || fail "oxlint" npx oxlint $rel || fail "oxlint"
# shellcheck disable=SC2086 # shellcheck disable=SC2086
+5 -5
View File
@@ -28,7 +28,7 @@ if [ "$changed" = "__all__" ]; then
client_changed=1 client_changed=1
else else
if printf '%s\n' "$changed" | grep -q '^Server/'; then server_changed=1; fi if printf '%s\n' "$changed" | grep -q '^Server/'; then server_changed=1; fi
if printf '%s\n' "$changed" | grep -q '^Client/tauri-client/'; then client_changed=1; fi if printf '%s\n' "$changed" | grep -q '^Client/'; then client_changed=1; fi
if printf '%s\n' "$changed" | grep -q '^docs/protocol-schema\.json'; then if printf '%s\n' "$changed" | grep -q '^docs/protocol-schema\.json'; then
server_changed=1 server_changed=1
client_changed=1 client_changed=1
@@ -49,12 +49,12 @@ if [ "$server_changed" = 1 ] && command -v go >/dev/null 2>&1; then
fi fi
if [ "$client_changed" = 1 ]; then if [ "$client_changed" = 1 ]; then
if [ -d Client/tauri-client/node_modules ]; then if [ -d Client/node_modules ]; then
echo "pre-push: client typecheck + eslint..." echo "pre-push: client typecheck + eslint..."
(cd Client/tauri-client && npm run -s typecheck) || fail "tsc --noEmit" (cd Client && npm run -s typecheck) || fail "tsc --noEmit"
(cd Client/tauri-client && npx eslint src/) || fail "eslint" (cd Client && npx eslint src/) || fail "eslint"
else else
printf 'pre-push: WARNING: node_modules missing in Client/tauri-client; skipping client checks.\n' >&2 printf 'pre-push: WARNING: node_modules missing in Client; skipping client checks.\n' >&2
fi fi
fi fi
+2 -2
View File
@@ -38,7 +38,7 @@ updates:
# Tauri client npm dependencies # Tauri client npm dependencies
- package-ecosystem: npm - package-ecosystem: npm
directory: /Client/tauri-client directory: /Client
schedule: schedule:
interval: weekly interval: weekly
day: monday day: monday
@@ -58,7 +58,7 @@ updates:
# Tauri Rust/Cargo dependencies # Tauri Rust/Cargo dependencies
- package-ecosystem: cargo - package-ecosystem: cargo
directory: /Client/tauri-client/src-tauri directory: /Client/src-tauri
schedule: schedule:
interval: weekly interval: weekly
day: monday day: monday
+24 -24
View File
@@ -119,7 +119,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
defaults: defaults:
run: run:
working-directory: Client/tauri-client/ working-directory: Client/
steps: steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
@@ -127,7 +127,7 @@ jobs:
with: with:
node-version: 24 node-version: 24
cache: npm cache: npm
cache-dependency-path: Client/tauri-client/package-lock.json cache-dependency-path: Client/package-lock.json
- name: Install npm dependencies - name: Install npm dependencies
run: npm ci run: npm ci
@@ -177,7 +177,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
defaults: defaults:
run: run:
working-directory: Client/tauri-client/ working-directory: Client/
steps: steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
@@ -185,7 +185,7 @@ jobs:
with: with:
node-version: 24 node-version: 24
cache: npm cache: npm
cache-dependency-path: Client/tauri-client/package-lock.json cache-dependency-path: Client/package-lock.json
- name: Install npm dependencies - name: Install npm dependencies
run: npm ci run: npm ci
@@ -198,7 +198,7 @@ jobs:
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with: with:
name: client-coverage name: client-coverage
path: Client/tauri-client/coverage/ path: Client/coverage/
retention-days: 7 retention-days: 7
# Rust unit tests used to live inside tauri-build, which only runs on PRs to # Rust unit tests used to live inside tauri-build, which only runs on PRs to
@@ -211,7 +211,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
defaults: defaults:
run: run:
working-directory: Client/tauri-client/src-tauri/ working-directory: Client/src-tauri/
steps: steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
@@ -236,7 +236,7 @@ jobs:
- name: Rust cache - name: Rust cache
uses: swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 uses: swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with: with:
workspaces: Client/tauri-client/src-tauri workspaces: Client/src-tauri
- name: Clippy lint (including test targets) - name: Clippy lint (including test targets)
run: cargo clippy --all-targets -- -D warnings run: cargo clippy --all-targets -- -D warnings
@@ -264,7 +264,7 @@ jobs:
timeout-minutes: 25 timeout-minutes: 25
defaults: defaults:
run: run:
working-directory: Client/tauri-client/ working-directory: Client/
steps: steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
@@ -272,7 +272,7 @@ jobs:
with: with:
node-version: 24 node-version: 24
cache: npm cache: npm
cache-dependency-path: Client/tauri-client/package-lock.json cache-dependency-path: Client/package-lock.json
- name: Install npm dependencies - name: Install npm dependencies
run: npm ci run: npm ci
@@ -295,8 +295,8 @@ jobs:
with: with:
name: playwright-report name: playwright-report
path: | path: |
Client/tauri-client/playwright-report/ Client/playwright-report/
Client/tauri-client/test-results/ Client/test-results/
retention-days: 7 retention-days: 7
# Admin-panel journey against a REAL server (no mocks): start-server.sh # Admin-panel journey against a REAL server (no mocks): start-server.sh
@@ -318,7 +318,7 @@ jobs:
timeout-minutes: 20 timeout-minutes: 20
defaults: defaults:
run: run:
working-directory: Client/tauri-client/ working-directory: Client/
steps: steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
@@ -331,7 +331,7 @@ jobs:
with: with:
node-version: 24 node-version: 24
cache: npm cache: npm
cache-dependency-path: Client/tauri-client/package-lock.json cache-dependency-path: Client/package-lock.json
- name: Install npm dependencies - name: Install npm dependencies
run: npm ci run: npm ci
@@ -348,8 +348,8 @@ jobs:
with: with:
name: admin-e2e-report name: admin-e2e-report
path: | path: |
Client/tauri-client/playwright-report/ Client/playwright-report/
Client/tauri-client/test-results/ Client/test-results/
retention-days: 7 retention-days: 7
# Blocking e2e subset: the parity-feature specs (tagged "@parity"), covering # Blocking e2e subset: the parity-feature specs (tagged "@parity"), covering
@@ -365,7 +365,7 @@ jobs:
timeout-minutes: 15 timeout-minutes: 15
defaults: defaults:
run: run:
working-directory: Client/tauri-client/ working-directory: Client/
steps: steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
@@ -373,7 +373,7 @@ jobs:
with: with:
node-version: 24 node-version: 24
cache: npm cache: npm
cache-dependency-path: Client/tauri-client/package-lock.json cache-dependency-path: Client/package-lock.json
- name: Install npm dependencies - name: Install npm dependencies
run: npm ci run: npm ci
@@ -390,8 +390,8 @@ jobs:
with: with:
name: playwright-report-parity name: playwright-report-parity
path: | path: |
Client/tauri-client/playwright-report/ Client/playwright-report/
Client/tauri-client/test-results/ Client/test-results/
retention-days: 7 retention-days: 7
# Image build is verification only, so it is skipped on dev to keep day-to-day # Image build is verification only, so it is skipped on dev to keep day-to-day
@@ -457,7 +457,7 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
defaults: defaults:
run: run:
working-directory: Client/tauri-client/ working-directory: Client/
steps: steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
@@ -465,7 +465,7 @@ jobs:
with: with:
node-version: 24 node-version: 24
cache: npm cache: npm
cache-dependency-path: Client/tauri-client/package-lock.json cache-dependency-path: Client/package-lock.json
- name: Install Linux system dependencies - name: Install Linux system dependencies
if: startsWith(matrix.os, 'ubuntu') if: startsWith(matrix.os, 'ubuntu')
@@ -491,20 +491,20 @@ jobs:
- name: Rust cache - name: Rust cache
uses: swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 uses: swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with: with:
workspaces: Client/tauri-client/src-tauri workspaces: Client/src-tauri
- name: Install npm dependencies - name: Install npm dependencies
run: npm ci run: npm ci
- name: Clippy lint (Rust) - name: Clippy lint (Rust)
working-directory: Client/tauri-client/src-tauri/ working-directory: Client/src-tauri/
run: cargo clippy -- -D warnings run: cargo clippy -- -D warnings
# Rust unit tests moved to the standalone `rust-tests` job so they run on # Rust unit tests moved to the standalone `rust-tests` job so they run on
# every event, not just PRs to main. # every event, not just PRs to main.
- name: Security audit (Rust dependencies) - name: Security audit (Rust dependencies)
working-directory: Client/tauri-client/src-tauri/ working-directory: Client/src-tauri/
run: | run: |
cargo install cargo-audit@0.22.1 --quiet cargo install cargo-audit@0.22.1 --quiet
cargo audit cargo audit
+24 -24
View File
@@ -30,9 +30,9 @@ jobs:
shell: bash shell: bash
run: | run: |
TAG_VERSION="${GITHUB_REF_NAME#v}" TAG_VERSION="${GITHUB_REF_NAME#v}"
TAURI_VERSION=$(node -p "require('./Client/tauri-client/src-tauri/tauri.conf.json').version") TAURI_VERSION=$(node -p "require('./Client/src-tauri/tauri.conf.json').version")
NPM_VERSION=$(node -p "require('./Client/tauri-client/package.json').version") NPM_VERSION=$(node -p "require('./Client/package.json').version")
CARGO_VERSION=$(sed -n 's/^version = "\(.*\)"$/\1/p' Client/tauri-client/src-tauri/Cargo.toml | head -1) CARGO_VERSION=$(sed -n 's/^version = "\(.*\)"$/\1/p' Client/src-tauri/Cargo.toml | head -1)
fail=0 fail=0
for pair in "tauri.conf.json:$TAURI_VERSION" "package.json:$NPM_VERSION" "Cargo.toml:$CARGO_VERSION"; do for pair in "tauri.conf.json:$TAURI_VERSION" "package.json:$NPM_VERSION" "Cargo.toml:$CARGO_VERSION"; do
file="${pair%%:*}"; ver="${pair#*:}" file="${pair%%:*}"; ver="${pair#*:}"
@@ -56,7 +56,7 @@ jobs:
with: with:
node-version: 24 node-version: 24
cache: npm cache: npm
cache-dependency-path: Client/tauri-client/package-lock.json cache-dependency-path: Client/package-lock.json
- name: Install Rust - name: Install Rust
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
@@ -64,14 +64,14 @@ jobs:
- name: Rust cache - name: Rust cache
uses: swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 uses: swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with: with:
workspaces: Client/tauri-client/src-tauri workspaces: Client/src-tauri
- name: Install npm dependencies - name: Install npm dependencies
working-directory: Client/tauri-client working-directory: Client
run: npm ci run: npm ci
- name: Build Tauri app - name: Build Tauri app
working-directory: Client/tauri-client working-directory: Client
env: env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
@@ -81,7 +81,7 @@ jobs:
shell: bash shell: bash
run: | run: |
mkdir -p release-staging mkdir -p release-staging
NSIS_DIR="Client/tauri-client/src-tauri/target/release/bundle/nsis" NSIS_DIR="Client/src-tauri/target/release/bundle/nsis"
INSTALLER=$(find "$NSIS_DIR" -name "*.exe" | head -1) INSTALLER=$(find "$NSIS_DIR" -name "*.exe" | head -1)
cp "$INSTALLER" release-staging/ cp "$INSTALLER" release-staging/
NSIS_ZIP=$(find "$NSIS_DIR" -name "*_x64-setup.nsis.zip" ! -name "*.sig" | head -1) NSIS_ZIP=$(find "$NSIS_DIR" -name "*_x64-setup.nsis.zip" ! -name "*.sig" | head -1)
@@ -108,7 +108,7 @@ jobs:
with: with:
node-version: 24 node-version: 24
cache: npm cache: npm
cache-dependency-path: Client/tauri-client/package-lock.json cache-dependency-path: Client/package-lock.json
- name: Install Linux system dependencies - name: Install Linux system dependencies
run: | run: |
@@ -131,14 +131,14 @@ jobs:
- name: Rust cache - name: Rust cache
uses: swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 uses: swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with: with:
workspaces: Client/tauri-client/src-tauri workspaces: Client/src-tauri
- name: Install npm dependencies - name: Install npm dependencies
working-directory: Client/tauri-client working-directory: Client
run: npm ci run: npm ci
- name: Build Tauri app (AppImage + deb) - name: Build Tauri app (AppImage + deb)
working-directory: Client/tauri-client working-directory: Client
env: env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
@@ -149,7 +149,7 @@ jobs:
# EGL_BAD_PARAMETER). Strip them and regenerate the updater artifact + # EGL_BAD_PARAMETER). Strip them and regenerate the updater artifact +
# signatures for the patched image. # signatures for the patched image.
- name: Strip host-incompatible libs from AppImage and re-sign - name: Strip host-incompatible libs from AppImage and re-sign
working-directory: Client/tauri-client working-directory: Client
shell: bash shell: bash
env: env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
@@ -173,7 +173,7 @@ jobs:
shell: bash shell: bash
run: | run: |
mkdir -p linux-staging mkdir -p linux-staging
BUNDLE_DIR="Client/tauri-client/src-tauri/target/release/bundle" BUNDLE_DIR="Client/src-tauri/target/release/bundle"
# AppImage # AppImage
APPIMAGE=$(find "$BUNDLE_DIR/appimage" -name "*.AppImage" ! -name "*.sig" | head -1) APPIMAGE=$(find "$BUNDLE_DIR/appimage" -name "*.AppImage" ! -name "*.sig" | head -1)
if [ -n "$APPIMAGE" ] && [ -f "$APPIMAGE" ]; then cp "$APPIMAGE" linux-staging/; fi if [ -n "$APPIMAGE" ] && [ -f "$APPIMAGE" ]; then cp "$APPIMAGE" linux-staging/; fi
@@ -298,7 +298,7 @@ jobs:
with: with:
node-version: 24 node-version: 24
cache: npm cache: npm
cache-dependency-path: Client/tauri-client/package-lock.json cache-dependency-path: Client/package-lock.json
- name: Install Linux system dependencies - name: Install Linux system dependencies
run: | run: |
@@ -321,14 +321,14 @@ jobs:
- name: Rust cache - name: Rust cache
uses: swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 uses: swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with: with:
workspaces: Client/tauri-client/src-tauri workspaces: Client/src-tauri
- name: Install npm dependencies - name: Install npm dependencies
working-directory: Client/tauri-client working-directory: Client
run: npm ci run: npm ci
- name: Build Tauri app (AppImage + deb) - name: Build Tauri app (AppImage + deb)
working-directory: Client/tauri-client working-directory: Client
env: env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
@@ -336,7 +336,7 @@ jobs:
# Same strip + re-sign as the x86_64 job — see the comment there. # Same strip + re-sign as the x86_64 job — see the comment there.
- name: Strip host-incompatible libs from AppImage and re-sign - name: Strip host-incompatible libs from AppImage and re-sign
working-directory: Client/tauri-client working-directory: Client
shell: bash shell: bash
env: env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
@@ -360,7 +360,7 @@ jobs:
shell: bash shell: bash
run: | run: |
mkdir -p linux-arm64-staging mkdir -p linux-arm64-staging
BUNDLE_DIR="Client/tauri-client/src-tauri/target/release/bundle" BUNDLE_DIR="Client/src-tauri/target/release/bundle"
# AppImage + updater artifact (.tar.gz) + signatures. Every filename # AppImage + updater artifact (.tar.gz) + signatures. Every filename
# must carry the arch: FindClientAssets matches on the # must carry the arch: FindClientAssets matches on the
# _aarch64.AppImage.tar.gz suffix, and arch-less names would collide # _aarch64.AppImage.tar.gz suffix, and arch-less names would collide
@@ -465,7 +465,7 @@ jobs:
with: with:
node-version: 24 node-version: 24
cache: npm cache: npm
cache-dependency-path: Client/tauri-client/package-lock.json cache-dependency-path: Client/package-lock.json
- name: Download Windows client assets - name: Download Windows client assets
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
@@ -532,7 +532,7 @@ jobs:
"$VERSION" "$WIN_HASH" "$WIN_HASH" "$LINUX_HASH" > windows/server-update-manifest.json "$VERSION" "$WIN_HASH" "$WIN_HASH" "$LINUX_HASH" > windows/server-update-manifest.json
- name: Sign server update assets - name: Sign server update assets
working-directory: Client/tauri-client working-directory: Client
shell: bash shell: bash
env: env:
SERVER_UPDATE_SIGNING_PRIVATE_KEY: ${{ secrets.SERVER_UPDATE_SIGNING_PRIVATE_KEY }} SERVER_UPDATE_SIGNING_PRIVATE_KEY: ${{ secrets.SERVER_UPDATE_SIGNING_PRIVATE_KEY }}
@@ -542,8 +542,8 @@ jobs:
printf '%s' "$SERVER_UPDATE_SIGNING_PRIVATE_KEY" > "$KEY_PATH" printf '%s' "$SERVER_UPDATE_SIGNING_PRIVATE_KEY" > "$KEY_PATH"
trap 'rm -f "$KEY_PATH"' EXIT trap 'rm -f "$KEY_PATH"' EXIT
npm ci npm ci
npx tauri signer sign -f "$KEY_PATH" -p "$SERVER_UPDATE_SIGNING_PRIVATE_KEY_PASSWORD" ../../windows/chatserver.exe npx tauri signer sign -f "$KEY_PATH" -p "$SERVER_UPDATE_SIGNING_PRIVATE_KEY_PASSWORD" ../windows/chatserver.exe
npx tauri signer sign -f "$KEY_PATH" -p "$SERVER_UPDATE_SIGNING_PRIVATE_KEY_PASSWORD" ../../windows/server-update-manifest.json npx tauri signer sign -f "$KEY_PATH" -p "$SERVER_UPDATE_SIGNING_PRIVATE_KEY_PASSWORD" ../windows/server-update-manifest.json
# Fail closed before publishing: prove the freshly signed assets verify # Fail closed before publishing: prove the freshly signed assets verify
# against the pinned public key that ships inside the server binary. # against the pinned public key that ships inside the server binary.
+4 -4
View File
@@ -36,8 +36,8 @@ docs/security-findings/
# Mutation-testing output (npm run test:mutate). Local-only by design: a # Mutation-testing output (npm run test:mutate). Local-only by design: a
# surviving-mutant report maps exactly which behaviour nothing tests. # surviving-mutant report maps exactly which behaviour nothing tests.
Client/tauri-client/.stryker-tmp/ Client/.stryker-tmp/
Client/tauri-client/reports/ Client/reports/
# Server runtime artifacts # Server runtime artifacts
Server/chatserver.exe Server/chatserver.exe
@@ -65,7 +65,7 @@ Client/login-mockup.html
Client/ui-mockup.html Client/ui-mockup.html
# Tauri typegen (auto-generated IPC bindings) # Tauri typegen (auto-generated IPC bindings)
Client/tauri-client/src/generated/ Client/src/generated/
.typecache .typecache
# Node modules # Node modules
@@ -103,7 +103,7 @@ Client/CLIENT-REVIEW.md
.serena/ .serena/
# Client env (holds API keys - never commit) # Client env (holds API keys - never commit)
Client/tauri-client/.env Client/.env
# Rust review output # Rust review output
.rust-review-results/ .rust-review-results/
+442 -442
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -1,9 +1,9 @@
# OwnCord # OwnCord
Self-hosted chat platform (alpha). `Server/` is a Go 1.26 REST + WebSocket Self-hosted chat platform (alpha). `Server/` is a Go 1.26 REST + WebSocket
server over SQLite with LiveKit voice/video; `Client/tauri-client/` is a Tauri server over SQLite with LiveKit voice/video; `Client/` is a Tauri
v2 desktop app (TypeScript frontend, thin Rust backend). Per-component detail v2 desktop app (TypeScript frontend, thin Rust backend). Per-component detail
lives in `Server/CLAUDE.md` and `Client/tauri-client/CLAUDE.md`; the protocol lives in `Server/CLAUDE.md` and `Client/CLAUDE.md`; the protocol
and schema are documented in `docs/protocol.md`, `docs/schema.md`, and and schema are documented in `docs/protocol.md`, `docs/schema.md`, and
`docs/architecture/README.md`. `docs/architecture/README.md`.
@@ -14,8 +14,8 @@ CI fails on drift, and the next generator run silently discards your edit.
| Generated | Source of truth | Workflow | | Generated | Source of truth | Workflow |
| --- | --- | --- | | --- | --- | --- |
| `Server/db/dbgen/` | `Server/db/queries/*.sql`, `Server/migrations/` | `db-change` skill | | `Server/db/dbgen/` | `Server/db/queries/*.sql`, `Server/migrations/` | `db-change` skill |
| `Server/ws/message_types.go` **and** `Client/tauri-client/src/lib/protocolTypes.ts` | `docs/protocol-schema.json` | `protocol-change` skill | | `Server/ws/message_types.go` **and** `Client/src/lib/protocolTypes.ts` | `docs/protocol-schema.json` | `protocol-change` skill |
| `Client/tauri-client/src/generated/` | `tauri-typegen` | CI patches known typegen bugs — see `.github/workflows/ci.yml` | | `Client/src/generated/` | `tauri-typegen` | CI patches known typegen bugs — see `.github/workflows/ci.yml` |
## Knowledge graph (graphify) ## Knowledge graph (graphify)
+1 -1
View File
@@ -7,7 +7,7 @@
set -euo pipefail set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SERVER_DIR="$(cd "$SCRIPT_DIR/../../../../../Server" && pwd)" SERVER_DIR="$(cd "$SCRIPT_DIR/../../../../Server" && pwd)"
PORT="${OWNCORD_ADMIN_E2E_PORT:-18446}" PORT="${OWNCORD_ADMIN_E2E_PORT:-18446}"
RUN_DIR="$(mktemp -d -t owncord-admin-e2e-XXXXXX)" RUN_DIR="$(mktemp -d -t owncord-admin-e2e-XXXXXX)"
@@ -10,7 +10,7 @@ import { JSDOM } from "jsdom";
import { readFileSync } from "node:fs"; import { readFileSync } from "node:fs";
import path from "node:path"; import path from "node:path";
const ADMIN_HTML_PATH = path.resolve(__dirname, "../../../../Server/admin/static/index.html"); const ADMIN_HTML_PATH = path.resolve(__dirname, "../../../Server/admin/static/index.html");
const ADMIN_HTML_SOURCE = readFileSync(ADMIN_HTML_PATH, "utf8"); const ADMIN_HTML_SOURCE = readFileSync(ADMIN_HTML_PATH, "utf8");
// The page's own <script> is a classic (non-module) script, so its top-level // The page's own <script> is a classic (non-module) script, so its top-level
+2 -2
View File
@@ -145,7 +145,7 @@ cd Server
CGO_ENABLED=0 go build -o chatserver -ldflags "-s -w -X main.version=1.2.0-alpha.3" . CGO_ENABLED=0 go build -o chatserver -ldflags "-s -w -X main.version=1.2.0-alpha.3" .
# Client # Client
cd Client/tauri-client cd Client
npm install npm install
npm run tauri build npm run tauri build
``` ```
@@ -158,7 +158,7 @@ cd Server
go test ./... go test ./...
# Client # Client
cd Client/tauri-client cd Client
npm run typecheck npm run typecheck
npm run lint npm run lint
npm test npm test
+1 -1
View File
@@ -86,7 +86,7 @@ protocol-generate:
protocol-verify: protocol-verify:
go run ./scripts/genprotocol go run ./scripts/genprotocol
@git diff --exit-code ws/message_types.go ../Client/tauri-client/src/lib/protocolTypes.ts || ( \ @git diff --exit-code ws/message_types.go ../Client/src/lib/protocolTypes.ts || ( \
echo "ERROR: generated protocol constants are stale. Run 'make protocol-generate' and commit the result." ; \ echo "ERROR: generated protocol constants are stale. Run 'make protocol-generate' and commit the result." ; \
exit 1 ; \ exit 1 ; \
) )
+1 -1
View File
@@ -40,7 +40,7 @@ type schema struct {
func main() { func main() {
schemaPath := flag.String("schema", "../docs/protocol-schema.json", "path to protocol-schema.json") schemaPath := flag.String("schema", "../docs/protocol-schema.json", "path to protocol-schema.json")
goOut := flag.String("go-out", "ws/message_types.go", "path to the generated Go file") goOut := flag.String("go-out", "ws/message_types.go", "path to the generated Go file")
tsOut := flag.String("ts-out", "../Client/tauri-client/src/lib/protocolTypes.ts", "path to the generated TypeScript file") tsOut := flag.String("ts-out", "../Client/src/lib/protocolTypes.ts", "path to the generated TypeScript file")
flag.Parse() flag.Parse()
raw, err := os.ReadFile(*schemaPath) raw, err := os.ReadFile(*schemaPath)
+1 -1
View File
@@ -43,7 +43,7 @@ var onEventAttr = regexp.MustCompile(`(?i)<[a-z/][^>]*\bon\w+\s*=`)
// Fuzzing confirmed the bare-substring version false-positives on plain // Fuzzing confirmed the bare-substring version false-positives on plain
// text (seed "jAvAsCript:0"), and the client's own markdown renderer // text (seed "jAvAsCript:0"), and the client's own markdown renderer
// independently refuses to autolink a javascript: pseudo-URL (see // independently refuses to autolink a javascript: pseudo-URL (see
// tauri-client/tests/unit/content-markdown.test.ts, "does not autolink a // Client/tests/unit/content-markdown.test.ts, "does not autolink a
// javascript: pseudo-URL"), so plain-text "javascript:" is not exploitable // javascript: pseudo-URL"), so plain-text "javascript:" is not exploitable
// through any known rendering path. // through any known rendering path.
var jsURLInTag = regexp.MustCompile(`(?i)<[a-z/][^>]*\bjavascript:`) var jsURLInTag = regexp.MustCompile(`(?i)<[a-z/][^>]*\bjavascript:`)
+1 -1
View File
@@ -628,7 +628,7 @@ func TestAssetFilenameFromURL(t *testing.T) {
} }
func TestDefaultServerSignaturePublicKey_DiffersFromTauriUpdaterKey(t *testing.T) { func TestDefaultServerSignaturePublicKey_DiffersFromTauriUpdaterKey(t *testing.T) {
tauriConfigPath := filepath.Clean(filepath.Join("..", "..", "Client", "tauri-client", "src-tauri", "tauri.conf.json")) tauriConfigPath := filepath.Clean(filepath.Join("..", "..", "Client", "src-tauri", "tauri.conf.json"))
raw, err := os.ReadFile(tauriConfigPath) raw, err := os.ReadFile(tauriConfigPath)
if err != nil { if err != nil {
t.Fatalf("ReadFile(%s): %v", tauriConfigPath, err) t.Fatalf("ReadFile(%s): %v", tauriConfigPath, err)
+1 -1
View File
@@ -286,7 +286,7 @@ func TestAuthenticateConn_InvalidToken_ReceivesAuthError(t *testing.T) {
// returning a genuine error rather than sql.ErrNoRows) must NOT be reported // returning a genuine error rather than sql.ErrNoRows) must NOT be reported
// as the terminal auth_error frame. The client treats auth_error as // as the terminal auth_error frame. The client treats auth_error as
// non-recoverable — it stops reconnecting and clears the user's stored // non-recoverable — it stops reconnecting and clears the user's stored
// credentials (see Client/tauri-client/src/lib/ws.ts and dispatcher.ts) — so // credentials (see Client/src/lib/ws.ts and dispatcher.ts) — so
// collapsing "DB unreachable" into "bad token" force-logs-out every client // collapsing "DB unreachable" into "bad token" force-logs-out every client
// that reconnects during a sub-second SQLite hiccup even though its session // that reconnects during a sub-second SQLite hiccup even though its session
// row is perfectly valid. A DB error must surface as a non-terminal error // row is perfectly valid. A DB error must surface as a non-terminal error
+1 -1
View File
@@ -107,4 +107,4 @@ Revisit when that commitment is on the table.
**Source of truth:** `Server/main.go`, `Server/config/config.go`, **Source of truth:** `Server/main.go`, `Server/config/config.go`,
`Server/docker-compose.yml`, `docs/deployment.md`, `docs/server-configuration.md`, `Server/docker-compose.yml`, `docs/deployment.md`, `docs/server-configuration.md`,
`Client/tauri-client/src-tauri/src/lib.rs`. `Client/src-tauri/src/lib.rs`.
+3 -3
View File
@@ -74,6 +74,6 @@ and surface a blocking mismatch modal if it later changes (see
[ux/voice-and-e2ee.md](ux/voice-and-e2ee.md)). [ux/voice-and-e2ee.md](ux/voice-and-e2ee.md)).
**Source of truth:** `Server/ws/voice_e2ee.go`, `Server/ws/livekit.go`, **Source of truth:** `Server/ws/voice_e2ee.go`, `Server/ws/livekit.go`,
`Client/tauri-client/src/lib/livekitSession.ts`, `Client/src/lib/livekitSession.ts`,
`Client/tauri-client/src/lib/e2eeCrypto.ts`, `Client/src/lib/e2eeCrypto.ts`,
`Client/tauri-client/src-tauri/src/livekit_proxy.rs`. `Client/src-tauri/src/livekit_proxy.rs`.
+1 -1
View File
@@ -10,7 +10,7 @@ pipeline, and a single typed (V2) command dispatch.
Message-type constants are **generated**: `docs/protocol-schema.json` is the Message-type constants are **generated**: `docs/protocol-schema.json` is the
single source of truth, and `Server/scripts/genprotocol` emits both single source of truth, and `Server/scripts/genprotocol` emits both
`Server/ws/message_types.go` and `Server/ws/message_types.go` and
`Client/tauri-client/src/lib/protocolTypes.ts` from it `Client/src/lib/protocolTypes.ts` from it
(`make protocol-generate`; CI fails on drift via `make protocol-verify`). (`make protocol-generate`; CI fails on drift via `make protocol-verify`).
The one exception is the plugin command family (`chat_command`, The one exception is the plugin command family (`chat_command`,
`command_reply`, `plugin_broadcast`), declared by hand in `command_reply`, `plugin_broadcast`), declared by hand in
+3 -3
View File
@@ -3,7 +3,7 @@
Date: 2026-08-08 Date: 2026-08-08
Status: partially implemented (verified 2026-08-19) — Tier 1a's `make fuzz` Status: partially implemented (verified 2026-08-19) — Tier 1a's `make fuzz`
target exists (`Server/Makefile`) and Tier 2's five custom ESLint rules target exists (`Server/Makefile`) and Tier 2's five custom ESLint rules
shipped 2026-08-08 (`Client/tauri-client/eslint-rules.js`), so the gap table shipped 2026-08-08 (`Client/eslint-rules.js`), so the gap table
below is stale for those two rows; Tiers 1b/1c are on-demand npm scripts; below is stale for those two rows; Tiers 1b/1c are on-demand npm scripts;
Tiers 34 remain unimplemented. Tiers 34 remain unimplemented.
@@ -76,7 +76,7 @@ has no native Windows support (WSL or Docker only), so on this machine it would
join `make` as tooling that cannot be run locally. ESLint flat config supports join `make` as tooling that cannot be run locally. ESLint flat config supports
an inline plugin, so custom rules cost no new dependency — and `npx eslint an inline plugin, so custom rules cost no new dependency — and `npx eslint
src/` is already a blocking CI gate, which removes the promotion step entirely. src/` is already a blocking CI gate, which removes the promotion step entirely.
Rules live in `Client/tauri-client/eslint-rules.js`, tested with `RuleTester` Rules live in `Client/eslint-rules.js`, tested with `RuleTester`
in `tests/unit/eslint-rules.test.ts`. See "Tier 2 — delivered" below. in `tests/unit/eslint-rules.test.ts`. See "Tier 2 — delivered" below.
## Tier 1 — Turn on what already exists ## Tier 1 — Turn on what already exists
@@ -135,7 +135,7 @@ configured surface that observes that class.
### 1d. Prerequisite ### 1d. Prerequisite
Confirm `Client/tauri-client/reports/`, `Client/tauri-client/.stryker-tmp/`, Confirm `Client/reports/`, `Client/.stryker-tmp/`,
`Server/coverage-all.out`, and `Server/**/testdata/fuzz/` interim output are `Server/coverage-all.out`, and `Server/**/testdata/fuzz/` interim output are
covered by `.gitignore` before running any of the above. Add entries where covered by `.gitignore` before running any of the above. Add entries where
they are missing. they are missing.
+1 -1
View File
@@ -81,7 +81,7 @@ no coverage (repo rule: "Target 80%+ coverage; TDD is the expected workflow").
### W1-2. E2EE key rotation drops peers in 7+ participant calls ### W1-2. E2EE key rotation drops peers in 7+ participant calls
- **Files:** `Server/ws/voice_e2ee.go` (~line 151); - **Files:** `Server/ws/voice_e2ee.go` (~line 151);
`Client/tauri-client/src/lib/livekitSession.ts` (~lines 1317-1349) `Client/src/lib/livekitSession.ts` (~lines 1317-1349)
- **Root cause:** the `voice_e2ee_offer` limit is 5/sec, but the key holder - **Root cause:** the `voice_e2ee_offer` limit is 5/sec, but the key holder
loops over every peer sending one offer each, back-to-back, with no pacing loops over every peer sending one offer each, back-to-back, with no pacing
and no retry on `RATE_LIMITED`. With 6+ peers, offers to the 6th+ peer are and no retry on `RATE_LIMITED`. With 6+ peers, offers to the 6th+ peer are
@@ -35,8 +35,8 @@ This is a continuation/handoff doc: what is done, what remains, and how to resum
## Resume checklist (do these first) ## Resume checklist (do these first)
1. **Confirm the F4/F8 Rust compiles.** It could not be built in the dev sandbox 1. **Confirm the F4/F8 Rust compiles.** It could not be built in the dev sandbox
(no local Tauri builds per `Client/tauri-client/CLAUDE.md`). Run (no local Tauri builds per `Client/CLAUDE.md`). Run
`cd Client/tauri-client/src-tauri && cargo clippy -- -D warnings` (or push and `cd Client/src-tauri && cargo clippy -- -D warnings` (or push and
let CI do it). Pure `tofu` logic has `#[cfg(test)]` unit tests; the frontend is let CI do it). Pure `tofu` logic has `#[cfg(test)]` unit tests; the frontend is
covered by the 3311-green unit suite. covered by the 3311-green unit suite.
2. ~~**Then F3**~~**DONE 2026-07-23** on branch `feat/e2ee-identity-tofu` (see the 2. ~~**Then F3**~~**DONE 2026-07-23** on branch `feat/e2ee-identity-tofu` (see the
+4 -4
View File
@@ -208,8 +208,8 @@ namespace collisions are confusing for users.
| `Server/plugin/manifest.go` | Add `Commands []CommandSpec` to `Manifest`, `validateCommands()`, and a `Manifest.Command(name)` lookup. | | `Server/plugin/manifest.go` | Add `Commands []CommandSpec` to `Manifest`, `validateCommands()`, and a `Manifest.Command(name)` lookup. |
| `Server/store/sqlite_plugin_commands.go` | **New file.** CRUD over the `plugin_commands` table. | | `Server/store/sqlite_plugin_commands.go` | **New file.** CRUD over the `plugin_commands` table. |
| `Server/migrations/016_plugin_commands.sql` | New migration. | | `Server/migrations/016_plugin_commands.sql` | New migration. |
| `Client/tauri-client/src/state/commands.ts` | **New module.** Caches per-server command list (fetched at `auth_ok` time via a new `commands_list` REST endpoint), feeds the autocomplete UI. | | `Client/src/state/commands.ts` | **New module.** Caches per-server command list (fetched at `auth_ok` time via a new `commands_list` REST endpoint), feeds the autocomplete UI. |
| `Client/tauri-client/src/components/Composer/SlashCommandPopup.tsx` | New component — autocomplete dropdown that opens when the message buffer starts with `/`. | | `Client/src/components/Composer/SlashCommandPopup.tsx` | New component — autocomplete dropdown that opens when the message buffer starts with `/`. |
| `docs/protocol.md` | Document the four new wire messages. | | `docs/protocol.md` | Document the four new wire messages. |
### Permission model ### Permission model
@@ -341,7 +341,7 @@ Each step is independently shippable.
- [ ] `Server/ws/deps.go``CommandDeps` - [ ] `Server/ws/deps.go``CommandDeps`
- [ ] `Server/ws/handlers_command_test.go` - [ ] `Server/ws/handlers_command_test.go`
- [ ] `Server/api/router.go``GET /api/v1/commands` (cached schema dump) - [ ] `Server/api/router.go``GET /api/v1/commands` (cached schema dump)
- [ ] `Client/tauri-client/src/state/commands.ts` - [ ] `Client/src/state/commands.ts`
- [ ] `Client/tauri-client/src/components/Composer/SlashCommandPopup.tsx` - [ ] `Client/src/components/Composer/SlashCommandPopup.tsx`
- [ ] `docs/protocol.md` — four new wire messages - [ ] `docs/protocol.md` — four new wire messages
- [ ] `CHANGELOG.md` — Phase D entry - [ ] `CHANGELOG.md` — Phase D entry
+2 -2
View File
@@ -1,7 +1,7 @@
# Tauri HTTP Capability Narrowing — Design # Tauri HTTP Capability Narrowing — Design
**Status:** implemented (2026-07-20), re-verified 2026-08-04 — the Decision **Status:** implemented (2026-07-20), re-verified 2026-08-04 — the Decision
below landed in `Client/tauri-client/src-tauri/capabilities/default.json`, below landed in `Client/src-tauri/capabilities/default.json`,
guarded by `tests/unit/capabilities-scope.test.ts`. The follow-up at the end guarded by `tests/unit/capabilities-scope.test.ts`. The follow-up at the end
of this document (move the link-preview fetch behind a Rust command that of this document (move the link-preview fetch behind a Rust command that
resolves DNS and rejects private/loopback IPs, closing DNS rebinding and resolves DNS and rejects private/loopback IPs, closing DNS rebinding and
@@ -13,7 +13,7 @@ the remaining outbound host set enumerable.
## Problem ## Problem
`Client/tauri-client/src-tauri/capabilities/default.json` grants three HTTP `Client/src-tauri/capabilities/default.json` grants three HTTP
identifiers — `http:allow-fetch`, `http:allow-fetch-send`, identifiers — `http:allow-fetch`, `http:allow-fetch-send`,
`http:allow-fetch-read-body` — each scoped to `https://*`, `https://*:*` and `http:allow-fetch-read-body` — each scoped to `https://*`, `https://*:*` and
`http://127.0.0.1:*`. In practice that is "the renderer may reach any host on `http://127.0.0.1:*`. In practice that is "the renderer may reach any host on
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"$comment": "Single source of truth for WebSocket protocol message-type constants. Server/ws/message_types.go and Client/tauri-client/src/lib/protocolTypes.ts are generated from this file — edit here, then run `make protocol-generate` in Server/. CI runs `make protocol-verify` to reject drift.", "$comment": "Single source of truth for WebSocket protocol message-type constants. Server/ws/message_types.go and Client/src/lib/protocolTypes.ts are generated from this file — edit here, then run `make protocol-generate` in Server/. CI runs `make protocol-verify` to reject drift.",
"version": 1, "version": 1,
"client_to_server": [ "client_to_server": [
{ "wire": "auth", "go": "MsgTypeAuth", "ts": "AUTH" }, { "wire": "auth", "go": "MsgTypeAuth", "ts": "AUTH" },
+1 -1
View File
@@ -62,7 +62,7 @@ cd Server
CGO_ENABLED=0 go build -o chatserver -ldflags "-s -w -X main.version=1.2.0-alpha.3" . CGO_ENABLED=0 go build -o chatserver -ldflags "-s -w -X main.version=1.2.0-alpha.3" .
# Client # Client
cd Client/tauri-client cd Client
npm install npm install
npm run tauri build npm run tauri build
``` ```