Commit Graph
831 Commits
Author SHA1 Message Date
J3vbandClaude Opus 5 e777c35f2b chore(deps): bump rand to 0.8.7 and 0.9.5 (#1278)
The last outstanding dependabot update. #1275 grouped four cargo bumps, three
of which (tauri 2.11.1, tar 0.4.46, serde_with 3.21.0) already landed on main
via #1274; rand is what remained. Dependabot did not act on a rebase request,
so this applies it directly against current main.

Both major lines in the tree move (0.8.5 -> 0.8.7, 0.9.2 -> 0.9.5) and bs58
comes in as a new transitive dependency, matching #1275's resolution.

Verified: cargo clippy --all-targets -D warnings clean, cargo test --lib
73 passed.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 18:54:17 +02:00
J3vbandClaude Opus 5 d2e1d2deb0 fix(ci): unbreak the Docker build and the npm audit gate (#1274)
* fix(docker): build the server image with Go 1.26

The Docker verify job failed with "go.mod requires go >= 1.26 (running go
1.25.12; GOTOOLCHAIN=local)". The Go 1.26 upgrade bumped go.mod but left the
Dockerfile on golang:1.25-bookworm, and GOTOOLCHAIN=local in the base image
means it cannot download a newer toolchain.

golang:1.26-bookworm confirmed present upstream. Not verified locally (Docker
Desktop not running); the CI Docker job proves it on this PR.

* fix(client): override brace-expansion and qs to patched versions

npm audit --audit-level=high failed the Client Static Checks job with 10
vulnerabilities (8 high, 2 moderate). npm audit fix could not resolve any of
them.

There is really only one advisory behind the eight high findings:
brace-expansion <=5.0.7, a DoS via unbounded expansion length causing OOM.
minimatch, glob, test-exclude, @vitest/coverage-v8, eslint and @eslint/* were
all just transitive consumers of it, and those top-level dev deps are already
at their latest versions, so no bump reaches the fix. qs 6.11.1-6.15.1 is a
second, independent advisory arriving via @stryker-mutator/core ->
typed-rest-client.

No patch exists inside the brace-expansion 1.x or 2.x lines (the fix landed in
5.0.8), so overrides are the only route. Collapsing every copy to 5.0.9 risked
breaking minimatch 3.x, which requires it as CJS, so the whole client gate was
run to check: npm audit 0 vulnerabilities, tsc clean, oxlint unchanged
(pre-existing no-underscore-dangle warnings only), eslint exit 0, prettier
clean, and vitest 3572 tests across 129 files all passing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* ci: stop running the suite twice for one push to dev

Listing dev under both push and pull_request meant a single push to dev fired
both events, running every job twice (visible as duplicated checks on #1274).
While a dev -> main PR is open, pull_request(synchronize) already covers each
push to dev, so dev only needs the pull_request trigger. workflow_dispatch
covers a dev branch with no PR open yet.

* chore(deps): roll up the seven open dependabot PRs

Consolidates #1267-#1273 onto this branch so they land as one CI run instead
of seven, each of which was triggering the full suite including tauri-build.

- google.golang.org/grpc 1.81.1 -> 1.82.1  (#1267)
- github.com/google/cel-go 0.28.1 -> 0.29.0 (#1268)
- defu 6.1.4 -> 6.1.7, root lockfile      (#1269)
- tauri 2.11.0 -> 2.11.1                   (#1270)
- @modelcontextprotocol/sdk 1.29 -> 1.30   (#1271)
- tar 0.4.45 -> 0.4.46                     (#1272)
- serde_with 3.18.0 -> 3.21.0              (#1273)

Applied by regenerating each lockfile from its manifest rather than merging
seven lockfile diffs.

Verified: go build across all four tag variants, go vet, govulncheck (0
vulnerabilities in called code), go test -race (14 packages, 0 failures),
cargo clippy --all-targets -D warnings, cargo test --lib (73 passed).

CI covers neither the root package.json nor tools/mcp-introspect, so those two
were checked by hand: changelogen still runs under defu 6.1.7 (release.yml
depends on it) and the introspect server still imports the 1.30 SDK.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(ci): drop the brace-expansion override, scope the audit to shipped deps

The brace-expansion override I added to clear npm audit broke Client Unit
Tests in CI:

  TypeError: (0 , brace_expansion_1.default) is not a function
    at minimatch braceExpand -> TestExclude.glob
    -> V8CoverageProvider.getUntestedFiles

minimatch requires brace-expansion as CJS and v5 is not callable that way. It
only fires under --coverage, which is why a local `vitest run` missed it; CI
runs `vitest run --coverage`. Verified the fix with that exact command.

There is no patched brace-expansion in the 1.x/2.x lines those tools pin (the
fix landed in 5.0.8), and eslint, @vitest/coverage-v8 and stryker are already
latest, so no bump reaches it. Since the whole chain is dev tooling that never
ships, the gate is now `npm audit --omit=dev --audit-level=high`, which
reports 0 vulnerabilities. The reasoning and the revisit condition are
recorded in ci.yml next to the step.

The qs override stays: qs is CJS, the override is proven safe, and it closes a
real advisory.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(client): route all cert-tofu emits through the single call site

Tauri Full Build failed on all three platforms with the generated bindings
redeclaring onCertTofu (TS2323/TS2393), which killed `tauri build` at its
beforeBuildCommand:

  src/generated/events.ts(36,23): error TS2323: Cannot redeclare exported
  variable 'onCertTofu'.

tauri-typegen emits one onCertTofu binding per `emit("cert-tofu", ..)` call
site it finds. ws_proxy.rs already funnelled its emits through a helper for
exactly this reason -- its doc comment says so -- but http_proxy.rs emitted
directly from all three TOFU outcomes, so the crate had four call sites.

Makes ws_proxy::emit_cert_tofu pub(crate) and routes http_proxy's trusted,
first_use and mismatch paths through it, leaving one call site crate-wide. The
now-unused Emitter import is dropped from http_proxy so clippy -D warnings
stays clean. Behaviour is unchanged: same event name, same payloads, same
order.

Not reproducible locally -- typegen only regenerates under CI's clean
checkout, and a full `npm run tauri build` here passes tsc either way -- so the
Tauri Full Build job on this PR is the proof. Verified locally: exactly one
emit("cert-tofu") call site remains, cargo clippy --all-targets -D warnings
clean, and the release build completes through bundling.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 18:41:22 +02:00
J3vb 3176dad3c8 ci: run checks on dev, keep verification builds on main only
Adds dev to the push and pull_request triggers so work on dev gets the full
check suite, and gates the Docker image build to main. That build is
verification only, so dev now runs lint, typecheck, unit tests, server tests
and clippy without waiting on it. tauri-build was already limited to PRs
targeting main.

Also adds workflow_dispatch (previous commit) so CI can be re-run without a
push.
2026-07-30 16:51:06 +02:00
J3vb 12d948ef66 ci: allow CI to be triggered manually
Adds workflow_dispatch so CI can be re-run on demand without pushing a commit.
Actions runs again now that the repo is public (private minutes were exhausted
since 2026-07-24, which is why every job failed with zero steps).
2026-07-30 16:38:46 +02:00
J3vb 9423d45796 chore: make .mcp.json local-only too
Same treatment as .claude/ and CLAUDE.md: removed from the index and gitignored,
file left on disk. It is Claude Code project config, so it stays local with the
rest of the agent setup.

tools/mcp-introspect/ stays tracked -- it is a real dev tool, not agent config,
and .mcp.json is the only thing referencing it.
2026-07-30 16:12:33 +02:00
J3vb a68e4187d3 docs: reframe the README as a hobby project in alpha
Rewords the framing to say what this actually is: something I build for fun and
run for a small group of friends, not a product. Tagline, alpha notice and the
"How it's built" section are rewritten in that voice, and the status heading
becomes "What works right now".

Also fixes structure inherited from the original: the what-it-is paragraph was
orphaned underneath the development-model heading, so it now sits directly after
the alpha notice, with the screenshots following and "How it's built" after
those. Stray blank-line runs removed.

Badges, tables, build instructions and the docs index are unchanged.
2026-07-30 16:12:22 +02:00
J3vbandClaude Opus 5 9a0ae0dd2a feat(release): fold distribution back into the source repo
The separate J3vb/OwnCord-releases repo existed only because this repo was
private: it carried the AGPL source snapshot and provided a publicly-readable
update feed. Once this repo is public both roles collapse into its own Releases
page, so the mirror is pure redundancy.

- Server/config/config.go: github.repo default OwnCord-releases -> OwnCord.
  This one default drives both the server self-update and the client
  auto-update chain (tauri.conf.json updater.endpoints is empty, so the client
  resolves through the server). No test pinned the old value.
- release.yml: drop the mirror step and its RELEASES_REPO_TOKEN guard, whose
  AGPL/private-repo premise no longer holds. The existing Create GitHub Release
  step is now the sole publish target. All 31 SHA pins verified intact.
- Repoint the README badge/download link, both SECURITY.md links, the
  server-configuration table and sample, the system-overview diagram node and
  the CHANGELOG note. SECURITY.md's advisory link is the load-bearing one: left
  alone it would 404 once the mirror repo is deleted.
- README: Go 1.25+ -> 1.26+ (badge and prerequisite) to match the toolchain
  actually required.

Deleting the mirror repo loses nothing: both repos' v1.1.0-alpha.2 carry
byte-identical asset sets, signatures and update manifest included.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 16:05:01 +02:00
J3vbandClaude Opus 5 f78800f973 fix(client): align tauri-plugin-dialog crate with its npm package
`npm run tauri build` refused to start: Tauri rejects a Rust crate and its npm
counterpart on different minor versions, and the pair had drifted to
tauri-plugin-dialog 2.6.0 vs @tauri-apps/plugin-dialog 2.7.2.

Cargo.toml already requires "2", which permits 2.7.2 -- only Cargo.lock was
stale, so this is a lockfile-only change: one package moved, the other 162
dependencies untouched. The npm side was bumped previously without the Rust
lockfile following.

Not caught by CI because the client bundle is only built in release.yml, and
Actions has been failing since 2026-07-24 on exhausted private minutes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 16:01:47 +02:00
J3vb bac412c6d4 chore: make Claude Code config local-only
Removes .claude/ and every CLAUDE.md from the index and gitignores them; the
files stay on disk untouched. Ahead of the repo going public, this keeps agent
instructions and local skills out of the published tree. A slashless gitignore
pattern matches at any depth, so one CLAUDE.md rule covers the root, Server/
and Client/tauri-client/ copies.

Also drops a stray gitlink at .claude/worktrees/tauri-plugins (mode 160000,
committed by accident) that would have cloned as a broken submodule.

.mcp.json stays tracked: it lists MCP servers, uses ${OWNCORD_API_TOKEN}
indirection rather than a literal, and holds no secrets.
2026-07-30 16:01:35 +02:00
J3vbandClaude Opus 5 d6cc57af56 fix(ws): require DM participation to join a DM voice room (F11)
The voice gate authorized a client-supplied channel_id with role bits only,
and DM channels carry no channel_overrides rows, so any member's base
CONNECT_VOICE bit minted a LiveKit RoomJoin and CanSubscribe token for any DM.
Both voice entry points now go through a gate that re-runs the old role
predicate and additionally requires DM participation, delegating that rule to
the existing permissions.Checker.RequireChannelAccess rather than adding a
second implementation of it.

Verified by a panel of agents; a negative control of the base tree plus only
the new test file fails both non-participant tests with a LiveKit room token
issued for a DM the user is not a participant of, while both participant tests
pass on base and patched alike.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 15:18:08 +02:00
J3vbandClaude Opus 5 35acc09121 fix(ws): filter channel metadata broadcasts by READ_MESSAGES (F9)
channel_create and channel_update were handed to BroadcastToAll and enqueued
with channelID 0, so the full channel payload -- name, topic and category of a
channel that channel_overrides hides from the recipient's role -- went to every
connected client and was replayed unconditionally from the ring buffer. Both
now resolve an audience through the same READ_MESSAGES helper the voice path
uses and enqueue under the real channel id, which filters live delivery and
both replay tiers by one mechanism. channel_delete stays unfiltered by design:
the row is already gone, so a check there would strand the channel in the
sidebar of users who saw it via a positive override.

Verified by a panel of agents; a base-revert control fails on both the live
leak and the replay leak, while the pre-existing broadcast tests pass
unmodified.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 15:16:01 +02:00
J3vbandClaude Opus 5 a21628531c fix(api): bound the logged request id and path (F8)
Unbounded client-controlled values reached the 2000-entry admin log ring
buffer and its SSE fan-out, letting an unauthenticated burst pin large amounts
of heap. A boundRequestID middleware now drops an inbound X-Request-Id over
128 bytes or outside printable ASCII, so chi generates its own, and the logged
request path is capped at 256 bytes. Both hunks are needed: a raw-socket probe
showed a 1MB r.URL.Path reaches the same sink independently of the header.

Verified by a panel of agents; an unpatched-tree reproduction fails 3 of the 4
added tests with the attacker bytes visible in the log record. UUID, 32-hex,
W3C traceparent and chi's own generated id format all still pass unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 15:13:48 +02:00
J3vbandClaude Opus 5 4b56631a1b fix(updater): detach update fetches from the caller's context (F7)
Both process-wide negative caches were filled with errors from a fetch driven
by the caller's context, so an unauthenticated client that aborted a request
which hit a cache miss wrote its own context.Canceled into a 5-minute shared
failure cache -- also blocking the owner's admin panel. The outbound fetch at
both sites is now driven by a server-owned context (WithoutCancel plus a 30s
timeout), so caller cancellation can no longer reach the cache while genuine
upstream failures are still cached.

Verified by a panel of agents; the added tests fail against an unpatched base
with a poisoned cache, and the pre-existing error-caching tests still pass.
The missing singleflight on CheckForUpdate is pre-existing and unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 15:04:44 +02:00
J3vbandClaude Opus 5 6e8ae1cde5 ci(release): pin third-party actions to commit SHAs (F5)
Every third-party action in release.yml resolved through a mutable tag or
branch inside jobs that hold TAURI_SIGNING_PRIVATE_KEY and
SERVER_UPDATE_SIGNING_PRIVATE_KEY, so anyone able to repoint an upstream ref
gained code execution beside OwnCord's code-signing keys. All 31 uses refs are
now pinned to full commit SHAs with version comments, matching what ci.yml
already does.

No tests cover this change: nothing in the project exercises
.github/workflows, and GitHub Actions cannot run in the local environment.
The change was verified by a panel of agents on review alone. Confirmed here:
the diff touches 31 uses lines and nothing else, release.yml still parses with
all 6 jobs and their step counts intact, all 8 actions shared with ci.yml
carry byte-identical pins, and the 3 release-only pins were checked against
upstream. The release is now frozen to the pinned versions; Dependabot manages
that ecosystem weekly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 15:03:49 +02:00
J3vbandClaude Opus 5 1da2aaddf0 fix(service): apply the full read/announcement gate to message edits (F3)
EditMessage authorized the non-DM path with permissions.SendMessages alone
while every sibling message sink requires ReadMessages plus the mutate bit, so
a user denied READ_MESSAGES could still rewrite an old post and have the edit
broadcast to the channel. The edit gate now calls the existing
checkSendPermission helper and collapses its error into the sink's
pre-existing opaque ErrForbidden, so the reply stays a non-oracle.

Verified by a panel of agents; the added test fails against the unpatched
tree, showing the edit succeeded before the fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 15:01:21 +02:00
J3vbandClaude Opus 5 6258681731 fix(plugin): guard against wasm guests with no memory section (F2)
The guest's linear memory was taken from mod.Memory() and used unchecked, so
an untrusted plugin wasm with no memory section nil-dereferenced on the
unrecovered startup path and crashed the server. All guest-memory access now
goes through one guestMemory() helper that detects wazero's non-nil interface
wrapping a nil *MemoryInstance, binding no commands at activation and
returning the existing missing-export diagnostic on dispatch.

Verified by a panel of agents; the added regression test panics with the
finding's exact stack against the unpatched tree.

Note: TestRegistry_Activate_WithoutRuntime and
TestRegistry_EnablePlugin_RollsBackWhenActivationFails fail under
-tags wazero, confirmed here to fail identically on the base tree. They are
pre-existing and unrelated; CI builds the wazero variant but does not test it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 15:00:26 +02:00
J3vbandClaude Opus 5 77121adcaa fix(ws): revoke channel-topic subscriptions on role change (F1)
READ_MESSAGES was authorized once at channel_focus and then frozen into a
durable pub/sub subscription that no role change re-evaluated, so a demoted
user kept receiving every message posted in channels their new role can no
longer read. BroadcastMemberUpdate now recomputes the allowed set from the
user's current role and unsubscribes each held channel topic it no longer
covers, evicting the socket if visibility cannot be resolved.

Verified by a panel of agents; both added tests were confirmed failing
against the unpatched tree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 14:58:35 +02:00
J3vbandClaude Opus 4.8 58005c9c6f feat(auth): revocable API tokens, introspect MCP server, and a Go 1.26 idiom pass (#1266)
* feat(auth): add revocable API tokens (bot/service auth)

Add long-lived, revocable API tokens so headless clients (the introspection
MCP tool, bots, CI) can authenticate without a password. Presented as
"Authorization: Bearer <token>", a token authenticates as a specific user,
inheriting that user's role and permissions.

- migration 018 + dedicated api_tokens table (kept separate from sessions so
  bulk logout and the per-user session cap never touch these); only the
  SHA-256 hash is stored, raw token shown once at creation
- auth.ResolveTokenHash: one shared bearer resolver that both AuthMiddleware
  and adminAuthMiddleware now call. Sessions are matched first so existing
  login behavior is unchanged; API tokens are a fallback only on session miss.
  A DB outage is returned wrapped, never mistaken for a bad token.
- `server token create|list|revoke` CLI: mints directly against the DB with no
  HTTP and no login — the password-free bootstrap path
- tests: resolver (8 cases incl. outage-not-fallthrough), db queries (6),
  api middleware integration (valid + revoked token)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(tools): add owncord-introspect MCP server

A local MCP dev tool that lets Claude Code introspect a running OwnCord
instance: read its logs, query any REST endpoint, and tail the desktop
client's log file. It is a thin wrapper over the existing API plus the
client log — no new product surface.

- tools/mcp-introspect/index.mjs (Node/ESM, one dep: @modelcontextprotocol/sdk)
  exposes api_request (full read-write passthrough), server_logs (admin SSE
  ring-buffer stream), client_logs (reads the desktop log file)
- authenticates with an API token (OWNCORD_API_TOKEN); pins the self-signed
  cert and skips hostname checks (the cert has no SAN)
- registered in .mcp.json (secret-free ${OWNCORD_API_TOKEN})
- un-ignore tools/mcp-introspect/ so this shared dev tool is committed, while
  tools/livekit-server.exe and node_modules stay ignored
- docs/mcp-introspect.md: how it works, tool reference, setup, troubleshooting

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(dependencies): update and add various crate versions in Cargo.lock

* feat(admin): manage API tokens from the admin panel

Add Owner-gated HTTP endpoints and a UI card to create, list, and revoke
API tokens from the web admin panel. Previously only the `server token`
CLI could manage them, which requires shell access to the host.

- POST|GET|DELETE /admin/api/tokens in admin/handlers_tokens.go, wired in
  admin/api.go. All three are Owner-only (ownerOnlyMiddleware, like
  backups/updates): an HTTP token-mint endpoint is a network-reachable
  credential-minting surface, and API tokens deliberately survive password
  change + bulk logout, so a hijacked admin session must not mint one.
- Reuses the same db.*APIToken calls as the CLI; create sources the actor
  from request context (audits who clicked, not the bound user); the raw
  token is returned once in the 201 body, never stored.
- Add json tags to db.APITokenListItem for snake_case wire consistency.
- Admin panel: "API Tokens" nav item + create modal, show-once reveal,
  revoke confirm in admin/static/index.html.
- Tests: 7 in admin/api_test.go (+api_tokens table in the in-memory schema).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor: modernize to Go 1.26 idioms + enable modernize linter

Apply `golangci-lint modernize` autofixes across the server and enable the
linter in .golangci.yml so these stop re-accumulating (they built up only
because modernize was never in the config).

Production code: slices.Contains for hand-rolled membership loops (api
router, ws origin, db/account, plugin manifest); strings.SplitSeq for
allocation-free line/segment iteration (db/migrate, updater, livekit_proxy);
strings.Cut (config); fmt.Appendf (dm_handler); min() (event_pruner);
any (ws client). Tests: range-over-int, t.Context(), WaitGroup.Go,
slices.Sort, maps.Copy, new(expr), interface{}->any.

- plugin/manifest.go parent-traversal check applied by hand: modernize
  skipped it (two conflicting rewrites); used the slices.Contains form.
- Removed the now-dead ptr() test helper after newexpr inlined its callers.
- Dropped dangling sort imports left by the sort.Slice->slices.Sort rewrite.

No behavior change. All four tag variants build, full test suite is green,
and golangci-lint (with modernize enabled) reports 0 issues.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 13:25:46 +02:00
J3vb 77ae0d21a8 fix(ptt): resolve shutdown race condition in polling thread management (#1265) 2026-07-29 13:12:32 +02:00
J3vbandClaude Opus 5 17b17eb1b3 fix(security): close all 13 findings from the 2026-07-28 server scan, plus dependabot rollup (#1264)
* fix(admin): reject banned users in admin auth (F1)

adminAuthMiddleware accepted a Bearer token on session validity plus the
ADMINISTRATOR bit alone and never consulted ban state, so a ban never
revoked admin-panel access. Adds the auth.IsEffectivelyBanned guard that
api.AuthMiddleware already uses, at both admin credential-resolution points.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(ws): gate the voice-channel text subscription on READ_MESSAGES (F2)

registerNow subscribed any client with voice state to that channel's
text-message topic regardless of READ_MESSAGES. The handshake's
already-computed readable-channel set is now passed into registerNow and the
subscription only happens when the voice channel is in it, preserving
authorized reconnect delivery.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(service): require READ_MESSAGES to delete messages (F4)

The non-DM delete gate checked MANAGE_MESSAGES without READ_MESSAGES, so a
role locked out of a private channel could still delete every message in it.
Requires ReadMessages alongside ManageMessages (and alongside SendMessages on
the author path) and derives the mod flag from that same gate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(service): require READ_MESSAGES alongside MANAGE_MESSAGES in SetMessagePinned (F8)

Pin/unpin checked only MANAGE_MESSAGES, so a role denied READ on a private
channel could still pin and unpin its messages.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(service): enforce the DM block at every DM interaction sink (F5)

The DM block was only checked on send, leaving edit, reactions, pins and
typing as bypasses. One shared requireDMNotBlocked is now called from all of
them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(ws): re-check CONNECT_VOICE when minting a refreshed LiveKit token (F6)

voice_token_refresh re-minted a LiveKit token without re-checking
CONNECT_VOICE, so a revoked permission kept working for the life of the
session. The permission is now re-checked where the token is minted, and a
60s sweep evicts participants whose permission was revoked.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(ws): rate-limit voice_e2ee_offer after validation, keyed on server state (F7)

The limiter key was built from unvalidated client input, letting an attacker
grow the limiter map without bound. The limiter now runs after validation and
keys on (sender, voiceChannelID), never on client input.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(ws): deliver voice_state/voice_leave only to roles that may read the channel (F9)

Voice state of private channels was broadcast to every connected client,
leaking channel membership. All 11 emit sites now route through one
READ-filtered fan-out, channel-tagged so replay filters too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(api): redact the LiveKit access token from proxy dial-failure logs (F10)

A dial failure wrote the LiveKit access-token JWT into the server log via the
URL in the error. redactKey now runs on the error before it reaches slog.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(auth): reserve the [deleted-N] username namespace (F11, F12)

The tombstone username namespace used by account deletion was freely
registrable, letting a user impersonate a deleted account. The namespace is
now reserved at validation, and DeleteAccount retries with a random suffix on
collision.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(api): strip Unicode format characters from upload filenames (F13)

The attachment filename sanitizer stripped control characters but not
unicode.Cf, allowing bidi-override extension spoofing. Cf is now stripped
alongside controls and foreign path separators are cut.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(api): reserve the login attempt before the bcrypt compare (F3)

The per-username lockout was a read-only IsLockedOut check followed by a
failure recorded only after the ~250ms bcrypt compare, so N concurrent
requests all passed the stale check before any of them recorded a failure.
The per-username cap is the only cross-IP brute-force defence (the middleware
limits per IP), so a distributed burst landed N guesses per 15-minute window
instead of 10.

Both counters are now reserved atomically with limiter.Allow before the
compare, and the lockout decision moves to the read-only limiter.Check so the
reservation is not double-counted. The limits are sized at threshold+1, which
leaves the sequential accepted-input set byte-identical to the previous
behaviour: failures 1-10 still land, the 10th still trips the lockout, and the
account owner's correct password on attempt 10 still returns 200. Sizing at
threshold instead would make 9 cheap wrong guesses convert the victim's own
correct password into a 15-minute lockout - the regression that got two
earlier attempts at this fix rejected, now pinned by a boundary test.

Deliberately scoped to handleLogin. The report also suggested widening to the
password-confirmation endpoints, but those are authenticated, share a single
pw_confirm_fail key across the TOTP endpoints, and widening there is what got
the first attempt rejected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* chore(deps): bump five Rust dependencies in /Client/tauri-client/src-tauri

Rolls up dependabot #1259, #1260, #1261, #1262 and #1263:

  tauri-build        2.5.6 -> 2.6.3
  tauri-plugin-fs    2.4.5 -> 2.5.1
  tauri-plugin-http  2.5.7 -> 2.5.9
  tauri-plugin-store 2.4.2 -> 2.4.4
  webpki-roots       1.0.6 -> 1.0.9

All five are lockfile-only; the manifest constraints already permitted the
new versions. The five PRs each rewrote overlapping regions of the same
Cargo.lock and so could not be merged independently, so the lockfile was
regenerated with cargo update --precise for each crate instead. The combined
result is smaller than the sum of the five diffs because they share
transitive updates.

Verified with cargo check --locked --all-targets (exit 0).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* chore(deps): bump typescript-eslint from 8.58.0 to 8.65.0 in /Client/tauri-client

Dependabot #1258.

8.65.0 improves @typescript-eslint/no-unnecessary-type-assertion, which
surfaces four assertions that were already redundant and now fail the lint
gate. They are removed here rather than in a follow-up so no commit in this
branch leaves `npm run lint` red:

  UserBar.ts / members.store.ts  "online" as UserStatus -> "online"
                                 (the receiver already accepts the literal)
  media.ts                       drops `as RequestInit` on a literal that is
                                 already assignable
  LoginForm.ts                   drops `as { message: unknown }` made
                                 redundant by the `"message" in err` narrowing

All four are the rule's own autofix. Verified: npm run typecheck, npm run
lint, npm run format:check all clean, and the unit suite is 3572/3572 green
across 129 files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 12:48:47 +02:00
J3vbandClaude Opus 5 79037418cc ci: group stryker and vitest dependabot updates into single PRs
Both families version-lock their packages with exact peer pins
(typescript-checker@9.6.0 requires core@9.6.0, not ^9.6.0), so
dependabot's default PR-per-package split breaks npm install whenever
only some of them merge. This is what took main down today.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 16:27:54 +02:00
J3vbandClaude Opus 5 592caf985d fix(client): restore localStorage shadowed by Node 26 in jsdom tests
Node 26 defines localStorage as a native global accessor returning
undefined unless started with --localstorage-file. Vitest's jsdom
environment sets window === globalThis, so that accessor shadows
jsdom's own, breaking all 20 test files that touch localStorage
(479 tests). sessionStorage is unaffected.

Install an in-memory Storage in a setup file when the global is
missing. Not using --localstorage-file: it is file-backed and shared
across vitest's parallel workers, which would leak state between test
files.

Suite: 3572/3572 passing (was 3093/3572).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 16:25:26 +02:00
J3vbandClaude Opus 5 c08cdcf3f0 fix(client): repair dependency resolution after dependabot merges
@stryker-mutator/{api,core,vitest-runner} were bumped to 9.6.1 while
typescript-checker stayed at 9.6.0, which hard-pins core@9.6.0 as a peer.
npm install failed with ERESOLVE. Bump typescript-checker to match.

Also reformat two files for prettier 3.9.6, which changed how union
types are broken across lines.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 14:58:26 +02:00
dependabot[bot] b78de62fbe chore(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc (#1239)
Bumps [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc](https://github.com/open-telemetry/opentelemetry-go) from 1.43.0 to 1.44.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.43.0...v1.44.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
  dependency-version: 1.44.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:49:47 +02:00
dependabot[bot] 7df4844bfb chore(deps): bump @stryker-mutator/api in /Client/tauri-client (#1255)
Bumps [@stryker-mutator/api](https://github.com/stryker-mutator/stryker-js/tree/HEAD/packages/api) from 9.6.0 to 9.6.1.
- [Release notes](https://github.com/stryker-mutator/stryker-js/releases)
- [Changelog](https://github.com/stryker-mutator/stryker-js/blob/master/packages/api/CHANGELOG.md)
- [Commits](https://github.com/stryker-mutator/stryker-js/commits/v9.6.1/packages/api)

---
updated-dependencies:
- dependency-name: "@stryker-mutator/api"
  dependency-version: 9.6.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:48:57 +02:00
dependabot[bot] e099e906bd chore(deps): bump prettier from 3.8.1 to 3.9.6 in /Client/tauri-client (#1251)
Bumps [prettier](https://github.com/prettier/prettier) from 3.8.1 to 3.9.6.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.8.1...3.9.6)

---
updated-dependencies:
- dependency-name: prettier
  dependency-version: 3.9.6
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:48:53 +02:00
dependabot[bot] 12576a1389 chore(deps): bump rustls in /Client/tauri-client/src-tauri (#1257)
Bumps [rustls](https://github.com/rustls/rustls) from 0.23.37 to 0.23.42.
- [Release notes](https://github.com/rustls/rustls/releases)
- [Changelog](https://github.com/rustls/rustls/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rustls/rustls/compare/v/0.23.37...v/0.23.42)

---
updated-dependencies:
- dependency-name: rustls
  dependency-version: 0.23.42
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:47:01 +02:00
dependabot[bot] f38e6b92d5 chore(deps): bump @playwright/test in /Client/tauri-client (#1256)
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.61.1 to 1.62.0.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.61.1...v1.62.0)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  dependency-version: 1.62.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:46:57 +02:00
dependabot[bot] 3bfc56c5e0 chore(deps): bump livekit-client in /Client/tauri-client (#1254)
Bumps [livekit-client](https://github.com/livekit/client-sdk-js) from 2.18.0 to 2.21.0.
- [Release notes](https://github.com/livekit/client-sdk-js/releases)
- [Changelog](https://github.com/livekit/client-sdk-js/blob/main/CHANGELOG.md)
- [Commits](https://github.com/livekit/client-sdk-js/compare/v2.18.0...v2.21.0)

---
updated-dependencies:
- dependency-name: livekit-client
  dependency-version: 2.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:46:52 +02:00
dependabot[bot] c0cc72148b chore(deps): bump serde_json in /Client/tauri-client/src-tauri (#1253)
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.149 to 1.0.151.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.149...v1.0.151)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-version: 1.0.151
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:46:48 +02:00
dependabot[bot] 39251a533e chore(deps): bump @stryker-mutator/vitest-runner in /Client/tauri-client (#1252)
Bumps [@stryker-mutator/vitest-runner](https://github.com/stryker-mutator/stryker-js/tree/HEAD/packages/vitest-runner) from 9.6.0 to 9.6.1.
- [Release notes](https://github.com/stryker-mutator/stryker-js/releases)
- [Changelog](https://github.com/stryker-mutator/stryker-js/blob/master/packages/vitest-runner/CHANGELOG.md)
- [Commits](https://github.com/stryker-mutator/stryker-js/commits/v9.6.1/packages/vitest-runner)

---
updated-dependencies:
- dependency-name: "@stryker-mutator/vitest-runner"
  dependency-version: 9.6.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:46:44 +02:00
dependabot[bot] efcd6a4f16 chore(deps): bump log in /Client/tauri-client/src-tauri (#1250)
Bumps [log](https://github.com/rust-lang/log) from 0.4.29 to 0.4.33.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.29...0.4.33)

---
updated-dependencies:
- dependency-name: log
  dependency-version: 0.4.33
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:46:39 +02:00
dependabot[bot] 8f2a82cd39 chore(deps): bump @tauri-apps/plugin-dialog in /Client/tauri-client (#1249)
Bumps [@tauri-apps/plugin-dialog](https://github.com/tauri-apps/plugins-workspace) from 2.6.0 to 2.7.2.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/log-v2.6.0...dialog-v2.7.2)

---
updated-dependencies:
- dependency-name: "@tauri-apps/plugin-dialog"
  dependency-version: 2.7.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:46:35 +02:00
dependabot[bot] d7404b8fa9 chore(deps): bump @stryker-mutator/core in /Client/tauri-client (#1246)
Bumps [@stryker-mutator/core](https://github.com/stryker-mutator/stryker-js/tree/HEAD/packages/core) from 9.6.0 to 9.6.1.
- [Release notes](https://github.com/stryker-mutator/stryker-js/releases)
- [Changelog](https://github.com/stryker-mutator/stryker-js/blob/master/packages/core/CHANGELOG.md)
- [Commits](https://github.com/stryker-mutator/stryker-js/commits/v9.6.1/packages/core)

---
updated-dependencies:
- dependency-name: "@stryker-mutator/core"
  dependency-version: 9.6.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:46:32 +02:00
dependabot[bot] 7ae9b9f1b6 chore(deps): bump tokio in /Client/tauri-client/src-tauri (#1244)
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.50.0 to 1.53.1.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.50.0...tokio-1.53.1)

---
updated-dependencies:
- dependency-name: tokio
  dependency-version: 1.53.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:46:28 +02:00
dependabot[bot] 0383221d2c chore(deps): bump @tauri-apps/cli in /Client/tauri-client (#1242)
Bumps [@tauri-apps/cli](https://github.com/tauri-apps/tauri) from 2.10.1 to 2.11.4.
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/@tauri-apps/cli-v2.10.1...@tauri-apps/cli-v2.11.4)

---
updated-dependencies:
- dependency-name: "@tauri-apps/cli"
  dependency-version: 2.11.4
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:46:24 +02:00
dependabot[bot] 2704bff360 chore(deps): bump jsdom from 29.0.0 to 29.1.1 in /Client/tauri-client (#1240)
Bumps [jsdom](https://github.com/jsdom/jsdom) from 29.0.0 to 29.1.1.
- [Release notes](https://github.com/jsdom/jsdom/releases)
- [Commits](https://github.com/jsdom/jsdom/compare/v29.0.0...v29.1.1)

---
updated-dependencies:
- dependency-name: jsdom
  dependency-version: 29.1.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:46:21 +02:00
dependabot[bot] b7945f7a02 chore(deps): bump oxlint from 1.75.0 to 1.76.0 in /Client/tauri-client (#1238)
Bumps [oxlint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint) from 1.75.0 to 1.76.0.
- [Release notes](https://github.com/oxc-project/oxc/releases)
- [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint/CHANGELOG.md)
- [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.76.0/npm/oxlint)

---
updated-dependencies:
- dependency-name: oxlint
  dependency-version: 1.76.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:46:17 +02:00
dependabot[bot] 86976a2ffd chore(deps): bump tauri-plugin-opener in /Client/tauri-client/src-tauri (#1237)
Bumps [tauri-plugin-opener](https://github.com/tauri-apps/plugins-workspace) from 2.5.3 to 2.5.4.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/http-v2.5.3...http-v2.5.4)

---
updated-dependencies:
- dependency-name: tauri-plugin-opener
  dependency-version: 2.5.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:46:13 +02:00
dependabot[bot] 14f150d01b chore(deps): bump github.com/prometheus/client_golang in /Server (#1248)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.23.2 to 1.24.1.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/v1.24.1/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.23.2...v1.24.1)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-version: 1.24.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:45:36 +02:00
dependabot[bot] 45f396cef2 chore(deps): bump github.com/livekit/server-sdk-go/v2 in /Server (#1247)
Bumps [github.com/livekit/server-sdk-go/v2](https://github.com/livekit/server-sdk-go) from 2.16.0 to 2.18.1.
- [Commits](https://github.com/livekit/server-sdk-go/compare/v2.16.0...v2.18.1)

---
updated-dependencies:
- dependency-name: github.com/livekit/server-sdk-go/v2
  dependency-version: 2.18.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:45:33 +02:00
dependabot[bot] 28733edf6e chore(deps): bump go.yaml.in/yaml/v3 from 3.0.4 to 3.0.5 in /Server (#1245)
Bumps [go.yaml.in/yaml/v3](https://github.com/yaml/go-yaml) from 3.0.4 to 3.0.5.
- [Commits](https://github.com/yaml/go-yaml/compare/v3.0.4...v3.0.5)

---
updated-dependencies:
- dependency-name: go.yaml.in/yaml/v3
  dependency-version: 3.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:45:29 +02:00
J3vbandClaude Opus 5 f4be2f9b73 chore: ignore rust-review results and claude worktrees
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 14:42:53 +02:00
dependabot[bot] 7ba8cb83ef chore(deps): bump github.com/livekit/protocol in /Server (#1241)
Bumps [github.com/livekit/protocol](https://github.com/livekit/protocol) from 1.50.2 to 1.50.4.
- [Release notes](https://github.com/livekit/protocol/releases)
- [Changelog](https://github.com/livekit/protocol/blob/main/CHANGELOG.md)
- [Commits](https://github.com/livekit/protocol/compare/v1.50.2...v1.50.4)

---
updated-dependencies:
- dependency-name: github.com/livekit/protocol
  dependency-version: 1.50.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:41:27 +02:00
dependabot[bot] 52fa290685 chore(deps): bump golang.org/x/crypto from 0.52.0 to 0.54.0 in /Server (#1243)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.52.0 to 0.54.0.
- [Commits](https://github.com/golang/crypto/compare/v0.52.0...v0.54.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.54.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:40:21 +02:00
J3vb 2c424efb1e Merge pull request #1236 from J3vb/claude/test-coverage-audit-tvp07j
test: close measured test-coverage gaps across server, client and Rust
2026-07-28 14:33:22 +02:00
Claude 33a6b23cde fix: resolve golangci-lint failures and correct audit-doc inaccuracies
A pre-merge review caught things my local verification missed, because two CI
gates could not run in the sandbox and I mis-read a third.

golangci-lint (BLOCKER — would have turned CI red on both server matrix legs).
My local binary was built for Go 1.25 against a repo targeting 1.26, so I could
not run it. Installed 2.11.3 with the repo's own toolchain: 5 issues, all in
files this PR adds, base clean. Now 0 issues:
- bodyclose x3 in api/livekit_proxy_ws_test.go — websocket.Dial's *http.Response
  was discarded; adopt the repo's existing pattern from ws/ws_integration_test.go
- gocritic stringXbytes — string(got) != string(payload) -> !bytes.Equal
- staticcheck SA4000 in ws/topic_rate_limiter_test.go — `!Allow() || !Allow()`.
  This was a real defect, not just a lint: || short-circuits, so a failing first
  call skipped the second, left a token unspent, and the next assertion would
  have reported the wrong thing. Split into two statements.

Playwright: I reported this suite as passing. It does not. I read the exit code
of `tail` through a pipeline instead of playwright's own. Re-run properly: 229
of 255 web tests fail, all cascading from the shared login helper
(navigateToMainPage never sees [data-testid='app-layout']). It reproduces on a
clean b3caceb worktree, so it is pre-existing on main and unrelated to this
diff — but it was never true that I had verified it. Recorded as new finding
T-2026-07-25-21 and promoted to backlog #2; the client-e2e job stays
continue-on-error and now carries timeout-minutes so a red suite cannot burn
unbounded Actions minutes. rust-tests gets a timeout too.

Audit-doc corrections (all confirmed by re-measurement):
- screenShare.ts was listed as "untouched by this pass" at 61.1% when this PR
  takes it to 100%; T-12's wording made it the exception when it is the best
- IsEitherBlocked was NOT zero-coverage — 83.3% at base via message_test.go
- excluded LOC 2,229 -> 1,827
- "40 Playwright spec files" -> 44 (33 web + 11 native), 255 web tests
- HandleLiveKitHealthForTest callers: eight -> seven
- admin coverage: 71.4% is with only the T-01 fix; 77.9% with this PR's tests

Verified after the fixes: golangci-lint 0 issues, go vet, go test -race,
go test -tags deadlock, vitest 94.87%, cargo test --lib 74/74, tsc, prettier.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AEETs3Vh6sAHHb1jMBL75g
2026-07-28 12:25:10 +00:00
Claude 0918f859a0 test: close measured test-coverage gaps across server, client and Rust
Audits what actually has tests, then closes the gaps it found. Full write-up
with before/after numbers in docs/audit-test-coverage-2026-07-25.md.

Measurement first: `go test ./... -coverprofile` (what CI runs) instruments
each package only for itself, so code exercised through another package's
tests reads as uncovered — `service` reported 36.7% against a real 85%. All
analysis here uses -coverpkg=./..., and both views now have Makefile targets.

Features that had zero coverage at every layer:
- user blocking (db + service + the /api/v1/blocks routes)
- auth lockout persistence — the DB round-trip that survives a restart
- plugin install/enable/disable/uninstall and the plugin KV namespace
- event replay bounds (GetMaxEventSeq, PruneEventsOlderThan)
- LiveKit participant_joined webhook (replayed-token guard), the room-service
  client, and proxyWebSocket/copyWS
- ws_proxy.rs and livekit_proxy.rs — pure helpers extracted, matching the
  existing tofu.rs pattern, so cert-pin and header-injection checks are testable

Gaps that were hidden rather than absent:
- Server/admin reported 0.3% coverage with 307 tests passing. TestSpawnDetached_*
  re-execs the test binary; the child inherited GOCOVERDIR and the parent's
  stdout, clobbering the profile and printing "[no tests to run]". Now 71.4%,
  and CI's uploaded artifact is correct.
- vitest.config.ts excluded 2.2k LOC unexplained, including two files that
  already had tests. Trimmed to three entries, each justified inline.
- api.HandleLiveKitHealthForTest re-implemented the handler it claimed to
  expose, so eight call sites tested a copy. Added a hook to the real one.

Two bugs found and pinned rather than silently patched: logctx.WithGroup nests
req_id under the group, and drag-reorder.ts takes one listener ref per channel
but releases one per sidebar, so the count never reaches zero.

Coverage: client 92.93% -> 94.87% statements (3371 -> 3572 tests) even after
un-excluding hidden files; Rust 47 -> 74 tests; Go zero-coverage functions
~70 -> 21, with plugin 61->77%, admin 67->86%, db 76->84%, service 85->91%.

Verified: go vet, all four build-tag variants, go test -race, -tags deadlock,
vitest --coverage, cargo test --lib, cargo clippy --all-targets, playwright.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AEETs3Vh6sAHHb1jMBL75g
2026-07-25 14:47:21 +00:00
J3vb b3cacebd92 Merge pull request #1235 from J3vb/feat/logging-hardening
feat: logging & error-visibility hardening (server + client)
2026-07-24 11:37:57 +02:00
J3vbandClaude Opus 4.8 d35de4ca0a fix: integrate logging hardening with rebased main (F3 + tauri plugins)
Rebasing onto post-F3 main surfaced two spots the auto-merge left inconsistent:
- profile_handler UpdateIdentityKey path: thread ctx into the writeServiceError
  call (the signature gained a context param in the server logging change)
- identity-pin store lookup: drop a needless borrow flagged by clippy

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 11:29:21 +02:00