Skip to content

feat(sync-v2): complete native server convergence - #94

Merged
InstaZDLL merged 17 commits into
mainfrom
codex/m4-server-completion
Aug 9, 2026
Merged

feat(sync-v2): complete native server convergence#94
InstaZDLL merged 17 commits into
mainfrom
codex/m4-server-completion

Conversation

@InstaZDLL

@InstaZDLL InstaZDLL commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Résumé

  • ajoute le journal SQLite idempotent de synchronisation user-data, les snapshots, curseurs/ACK et notifications WebSocket décrits par RFC-003
  • complète /api/v2 pour le setup, le catalogue, les bibliothèques, notes/historique, partages, utilisateurs, identifiants Subsonic et l’état du transcodage
  • remplace les secrets navigateur en localStorage par un access token en mémoire et un refresh rotatif HttpOnly/SameSite avec contrôle d’origine + CSRF
  • livre les parcours fonctionnels du client embarqué : favoris, playlists, queue persistante, partages et administration des bibliothèques, scans, comptes et identifiants Subsonic
  • rend le contrôle DCO compatible avec l’identité de signature réellement utilisée par Dependabot

Le catalogue reste autoritaire côté serveur et WaveFlow Desktop le consommera comme source distante séparée. Aucun rapprochement avec la bibliothèque locale n’est introduit ici.

Validation

  • bun run format, bun run lint, bun run test, bun run build dans webapp/ — 21 tests
  • cargo fmt --all --check
  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo check --all-targets --all-features
  • cargo test --all-features — 37 tests
  • bun run build à la racine — SPA puis binaire release LTO
  • smoke test navigateur sur installation vide : setup, sessions, rôles, playlists, favoris, queue, partages, bibliothèque/scan, comptes et rotation Subsonic
  • fixture DCO rejouée sur un vrai commit Dependabot

Aucun tag de release n’est créé par cette PR.

Summary by CodeRabbit

  • Nouvelles fonctionnalités

    • Ajout de pages pour les favoris, playlists, files d’attente et partages.
    • Administration des bibliothèques, scans, utilisateurs et identifiants Subsonic.
    • Synchronisation entre appareils avec mises à jour en temps réel.
    • Initialisation du premier compte administrateur.
    • Consultation des évaluations, de l’historique et du transcodage.
    • Restauration et gestion de la file d’attente.
  • Améliorations de sécurité

    • Sessions protégées par cookies HttpOnly, CSRF et validation de l’origine.
    • Les jetons ne sont plus conservés localement ni récupérables depuis les partages.
  • Documentation

    • Documentation actualisée pour les API et la synchronisation v2.

Signed-off-by: InstaZDLL <github.105mh@8shield.net>
Signed-off-by: InstaZDLL <github.105mh@8shield.net>
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 19 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5358acfe-10d1-43bd-99bb-6f56a6b0c443

📥 Commits

Reviewing files that changed from the base of the PR and between a6e5085 and 5390906.

📒 Files selected for processing (1)
  • src/services.rs
📝 Walkthrough

Walkthrough

Cette PR ajoute la synchronisation v2 durable, les sessions web, le bootstrap administrateur, l’administration native et les interfaces web associées. Elle ajoute les routes API v2, les migrations, les tests et la documentation correspondante.

Changes

Fonctionnalités v2

Layer / File(s) Summary
Contrats et journal de synchronisation
docs/rfcs/RFC-002-waveflow-server-v2.md, docs/rfcs/RFC-003-waveflow-sync-v2.md, migrations-v2/..., src/sync.rs
Le protocole définit les mutations idempotentes, les curseurs, les acquittements et les notifications. SQLite stocke les opérations, événements et acquittements.
Installation, authentification et administration
src/database.rs, src/catalog.rs, src/authentication.rs, src/http.rs, src/services.rs
Le serveur ajoute le bootstrap initial, les sessions web, les bibliothèques, les utilisateurs, les partages et les identifiants Subsonic.
Mutations et endpoints de synchronisation
src/services.rs, src/http.rs, tests/v2_foundations.rs
Les mutations deviennent contextuelles, transactionnelles et idempotentes. Les endpoints REST, snapshot, ACK et WebSocket exposent ces flux.
Session et interfaces du client web
webapp/src/api.ts, webapp/src/main.tsx, webapp/src/pages.tsx, webapp/src/player.tsx, webapp/src/styles.css
Le client web utilise une session mémoire et un cookie de rafraîchissement HttpOnly. Il ajoute les pages de données utilisateur, l’administration et la persistance de la file.
Validation et documentation
tests/v2_foundations.rs, webapp/src/api.test.ts, README.md, docs/M4-handoff.md, src/lib.rs
Les tests couvrent les sessions, le bootstrap, l’administration, la synchronisation, l’isolation des tenants et les parcours web. La documentation et OpenAPI décrivent les routes et contrats v2.

Signature DCO

Layer / File(s) Summary
Signature DCO Dependabot
.github/workflows/dco.yml
Le workflow accepte la signature dependabot[bot] <support@github.com> pour les commits Dependabot.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WebApp
  participant HTTP_API
  participant DomainServices
  participant SyncService
  participant SQLite
  WebApp->>HTTP_API: Envoie une mutation avec operation_id
  HTTP_API->>DomainServices: Transmet le contexte de mutation
  DomainServices->>SyncService: Réclame l’opération
  SyncService->>SQLite: Enregistre l’opération et l’événement
  SyncService-->>HTTP_API: Retourne le reçu
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.95% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Le titre identifie clairement la finalisation de la convergence native du serveur et correspond aux changements principaux de synchronisation v2.
Description check ✅ Passed La description couvre le résumé, les changements substantiels et la validation, avec des résultats de tests détaillés et un périmètre clairement défini.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/m4-server-completion

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added type: feat New feature scope: server Server core (Rust) scope: deps Dependencies scope: web Embedded web player (React) scope: routes Web routes scope: components Web UI components scope: tooling Build / dev tooling scope: docs Docs, README, assets scope: db SQLite schema, migrations, queries scope: api Native /api/v2 surface scope: subsonic Subsonic / OpenSubsonic compatibility size: xl > 500 lines and removed type: feat New feature labels Aug 9, 2026
Comment thread tests/v2_foundations.rs Fixed
Comment thread tests/v2_foundations.rs Fixed
Comment thread tests/v2_foundations.rs Fixed
Comment thread tests/v2_foundations.rs Fixed
Signed-off-by: InstaZDLL <github.105mh@8shield.net>
@github-actions github-actions Bot added the type: feat New feature label Aug 9, 2026
Signed-off-by: InstaZDLL <github.105mh@8shield.net>
@github-actions github-actions Bot added type: feat New feature and removed type: feat New feature labels Aug 9, 2026
Signed-off-by: InstaZDLL <github.105mh@8shield.net>
@github-actions github-actions Bot added type: feat New feature and removed type: feat New feature labels Aug 9, 2026
@InstaZDLL InstaZDLL self-assigned this Aug 9, 2026
@InstaZDLL
InstaZDLL marked this pull request as ready for review August 9, 2026 11:37
@github-actions github-actions Bot added type: feat New feature and removed type: feat New feature labels Aug 9, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/services.rs`:
- Around line 1799-1805: Dans le flux de création de partage autour de
songs_by_ids_on, définissez ou réutilisez MAX_SHARE_TRACKS et rejetez ids dès
qu’il contient plus de cette limite, après le traitement des rejeux et avant
toute insertion. Ajoutez un test couvrant exactement MAX_SHARE_TRACKS + 1 pistes
et vérifiez l’erreur attendue, en conservant la prise du writer_guard pour toute
mutation de base de données.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1b5b548a-7fbd-4f77-841c-e03c0ca7472e

📥 Commits

Reviewing files that changed from the base of the PR and between 765a64e and c871fad.

📒 Files selected for processing (6)
  • README.md
  • docs/rfcs/RFC-002-waveflow-server-v2.md
  • src/security.rs
  • src/services.rs
  • tests/v2_foundations.rs
  • webapp/src/pages.tsx

Comment thread src/services.rs Outdated
Signed-off-by: InstaZDLL <github.105mh@8shield.net>
@github-actions github-actions Bot added the type: feat New feature label Aug 9, 2026
@InstaZDLL

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@InstaZDLL

Copy link
Copy Markdown
Owner Author

@coderabbitai review

1 similar comment
@InstaZDLL

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions github-actions Bot added type: feat New feature and removed type: feat New feature labels Aug 9, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/services.rs (3)

1867-1872: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Évitez la course lors de la lecture d’un partage public.

public_share lit le partage et ses pistes avant de prendre _writer à Line 1867. Si delete_share_with_context supprime le partage entre ces opérations, l’UPDATE à Line 1868 affecte zéro ligne. Le code ignore ce résultat et renvoie les données obsolètes.

Prenez le writer gate avant la lecture, puis exécutez la lecture et l’incrément dans une transaction cohérente. Sinon, vérifiez rows_affected() et renvoyez ServiceError::NotFound.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services.rs` around lines 1867 - 1872, Update public_share to acquire the
writer guard before reading the share and its tracks, then perform the read and
visit-count increment within one coherent transaction/critical section. Ensure
deletion cannot occur between the read and UPDATE; alternatively, validate the
UPDATE result via rows_affected() and return ServiceError::NotFound when no row
was updated.

1265-1273: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Filtrez les favoris par visibilité.

starred_ids_on sélectionne toutes les lignes user_star de l’utilisateur. La requête ne vérifie ni library_member ni le type d’entité. Après le retrait d’une bibliothèque, sync_snapshot peut donc renvoyer des identifiants devenus inaccessibles.

Ajoutez des prédicats EXISTS par type d’entité, comme dans ratings_on aux Lines 1287-1292. Conservez l’identifiant utilisateur comme premier bind.

As per coding guidelines, la règle exige « Enforce tenancy in repository queries using library_member, not only in HTTP handlers. »

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services.rs` around lines 1265 - 1273, Update starred_ids_on to restrict
user_star results by entity visibility using type-specific EXISTS predicates
against library_member, following the pattern in ratings_on. Keep user_id as the
first bind and preserve the existing ordering, row mapping, and return type.

Source: Coding guidelines


2145-2146: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Libérez le writer gate avant la lecture finale des utilisateurs.

create_subsonic_user conserve _writer pendant self.users(actor_id) après le commit. update_user applique le même schéma. users() charge tous les comptes et les adhésions. Cette lecture peut donc bloquer toutes les mutations et les scans.

Ajoutez drop(_writer); immédiatement après chaque tx.commit().await? et avant self.users(...).

Correction minimale
         tx.commit().await?;
+        drop(_writer);
         self.users(actor_id)

Also applies to: 2203-2208, 2253-2263, 2297-2302

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services.rs` around lines 2145 - 2146, Release the writer guard
immediately after each successful transaction commit and before the final user
reads. Update create_subsonic_user, update_user, and the other affected mutation
paths around their tx.commit().await? calls by adding drop(_writer); before
invoking self.users(...) or equivalent user-loading logic, while preserving the
existing transaction behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/services.rs`:
- Around line 1867-1872: Update public_share to acquire the writer guard before
reading the share and its tracks, then perform the read and visit-count
increment within one coherent transaction/critical section. Ensure deletion
cannot occur between the read and UPDATE; alternatively, validate the UPDATE
result via rows_affected() and return ServiceError::NotFound when no row was
updated.
- Around line 1265-1273: Update starred_ids_on to restrict user_star results by
entity visibility using type-specific EXISTS predicates against library_member,
following the pattern in ratings_on. Keep user_id as the first bind and preserve
the existing ordering, row mapping, and return type.
- Around line 2145-2146: Release the writer guard immediately after each
successful transaction commit and before the final user reads. Update
create_subsonic_user, update_user, and the other affected mutation paths around
their tx.commit().await? calls by adding drop(_writer); before invoking
self.users(...) or equivalent user-loading logic, while preserving the existing
transaction behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1b03d23d-f37a-4080-9dcb-5ec4bcaab62f

📥 Commits

Reviewing files that changed from the base of the PR and between c871fad and 1e3f7da.

📒 Files selected for processing (2)
  • src/services.rs
  • tests/v2_foundations.rs

starred_ids_on returned every user_star row unfiltered while ratings_on
already restricted results to entities the user can still reach. A listener
removed from a library kept seeing favourites for tracks, albums and artists
outside their scope. Apply the same type-specific EXISTS predicates against
library_member, keeping user_id as the first bind and the existing ordering.

public_share read the share and its tracks outside the writer gate, so a
share revoked in between was still served with an incremented visit count.
Let the UPDATE arbitrate instead: no affected row means the share is gone and
the visitor gets the same 404 as for an unknown token.

Release the writer guard right after tx.commit() in create_subsonic_user and
update_user: both then run self.users(), a full account read, while still
holding the process-wide gate. The other commit sites only publish a sync
receipt in memory, so they were left alone.

Claude-Session: https://claude.ai/code/session_01NJBwjsQ17Bx2PgvPbBGNpM
Signed-off-by: InstaZDLL <github.105mh@8shield.net>
@github-actions github-actions Bot added type: feat New feature and removed type: feat New feature labels Aug 9, 2026
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #96

coderabbitai Bot added a commit that referenced this pull request Aug 9, 2026
Docstrings generation was requested by @InstaZDLL.

* #94 (comment)

The following files were modified:

* `src/authentication.rs`
* `src/catalog.rs`
* `src/database.rs`
* `src/http.rs`
* `src/lib.rs`
* `src/main.rs`
* `src/media.rs`
* `src/security.rs`
* `src/services.rs`
* `src/subsonic.rs`
* `src/sync.rs`
* `webapp/src/api.ts`
* `webapp/src/main.tsx`
* `webapp/src/pages.tsx`
* `webapp/src/player.tsx`

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/services.rs`:
- Around line 1877-1887: Rendre la validation d’expiration atomique dans la
méthode contenant la requête UPDATE share en ajoutant le prédicat expires_at à
la clause WHERE, avec le paramètre de temps actuel. Conserver le contrôle de
visited == 0 pour retourner ServiceError::NotFound lorsqu’aucune ligne n’est
modifiée, notamment si le partage a expiré pendant l’attente de writer_guard.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: cffb5a62-775c-4aae-88cf-1d9f911a74af

📥 Commits

Reviewing files that changed from the base of the PR and between c871fad and a6e5085.

📒 Files selected for processing (2)
  • src/services.rs
  • tests/v2_foundations.rs

Comment thread src/services.rs
public_share filtered on expires_at during the initial SELECT, then waited on
the process-wide writer gate before incrementing the visit count. That wait can
block behind a scan, so a share could expire in between and still be served
once. Carry the expiry predicate into the UPDATE and reuse the same timestamp
for last_visited_at, so revocation and expiry are both arbitrated at write
time; visited == 0 keeps returning NotFound.

Claude-Session: https://claude.ai/code/session_01NJBwjsQ17Bx2PgvPbBGNpM
Signed-off-by: InstaZDLL <github.105mh@8shield.net>
@github-actions github-actions Bot added type: feat New feature and removed type: feat New feature labels Aug 9, 2026
@InstaZDLL
InstaZDLL merged commit 6716df9 into main Aug 9, 2026
12 checks passed
@InstaZDLL
InstaZDLL deleted the codex/m4-server-completion branch August 9, 2026 19:58
InstaZDLL added a commit that referenced this pull request Aug 9, 2026
The handoff still described PR #94 as pending and listed the browser-session
debt as open, both of which landed. Bring it back in line with main:

- M4 is closed: base in 14aec76, server completion in 6716df9.
- Document the user-data sync layer and RFC-003, absent until now.
- Move the localStorage debt and the Dependabot DCO exemption to a "closed,
  do not reopen" section, with the evidence for each.
- Record the three security fixes found in review after #94 (unfiltered
  favourites, share visits surviving revocation or expiry, writer gate held
  across a read) as pitfalls to apply to new code, not just as history.
- Note the second FFmpeg CI failure mode: choco exits 0 on a 503, so a green
  install step can still mean no binary.
- Record that @vitejs/plugin-react 6 requires Vite >= 7, and why vite-env.d.ts
  is load-bearing under TypeScript 7.
- Add the dependency state, including the cross-version check proving
  chacha20poly1305 0.11 still reads secrets sealed by 0.10.

Next step is the WaveFlow Desktop integration pass, not M5.

Claude-Session: https://claude.ai/code/session_01NJBwjsQ17Bx2PgvPbBGNpM
Signed-off-by: InstaZDLL <github.105mh@8shield.net>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: api Native /api/v2 surface scope: auth Authentication, tokens, sessions scope: components Web UI components scope: db SQLite schema, migrations, queries scope: deps Dependencies scope: docs Docs, README, assets scope: routes Web routes scope: server Server core (Rust) scope: streaming Streaming, transcoding, FFmpeg scope: subsonic Subsonic / OpenSubsonic compatibility scope: tooling Build / dev tooling scope: web Embedded web player (React) size: xl > 500 lines type: feat New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants