Commit Graph

922 Commits (main)
 

Author SHA1 Message Date
zijiren 7f3e625b4e
fix: hydrate image chat events and expose room password state (#472)
Fix failures found during the frontend integration sweep:

- Hydrate image attachment views in chat edit/reaction/pin/unpin
responses, pinned-message lists, and durable pin replay. These mutations
could commit successfully and then fail serialization or disconnect
observers.
- Expose room password presence in client/admin projections, using batch
lookups for room lists.
- Preserve nullable category IDs in room-creation review queries so
uncategorized submissions can be read and listed; refresh SQLx metadata.

Validation: Rust formatting, workspace Clippy with warnings denied, five
Docker-backed chat regression tests, eight secondary-read repository
integration tests, eight CLI human-output tests, protobuf freshness, and
a dynamic-Web backend build passed. Remote CI results are tracked on
this PR.

Frontend integration: https://github.com/synctv-org/synctv-app/pull/70
2 weeks ago
zijiren 355b7790b6
fix: bilibili dash codec filter (#459) 3 weeks ago
zijiren 3a98ef42b0
release: prepare v1.0.4 (#456)
## Summary\n- bump server, Helm chart, and documentation metadata to
1.0.4\n- pin production Web UI to synctv-app commit
24109a49b2e27f7772d067fae66b622431c942d5\n- use Flutter 3.47.2 in the
Docker Web UI build to satisfy the pinned client SDK\n- release the
latest upstream main commit 940d566ff\n\n## Verification\n- make
fmt-check\n- make validate-helm\n- make check\n- make web-ui-build
(Flutter 3.47.2; generated version.json 1.1.24+23)\n-
verify-web-ui-pin\n- cargo test -p synctv-api-common user_block --locked
3 weeks ago
zijiren 940d566ff0
chore: update nightly toolchain and dependencies (#455) 3 weeks ago
zijiren dd6428ebca
feat(livestream): add WebRTC ingest and playback (#454)
## Summary

- add WHIP publishing and WHEP playback backed by the xiu StreamHub
- support external WHEP live proxy sources and expose WebRTC playback
through the live/RTMP provider APIs
- relay RTP and WebRTC session lifecycle across cluster nodes with
generation and lease fencing
- add WebRTC configuration, environment variables, CLI output, Helm
values, OpenAPI definitions, and bilingual documentation
- keep frame and RTP relay liveness deadlines independent so a stalled
path triggers relay recovery

## Validation

- `cargo fmt --all -- --check`
- `cargo clippy -p synctv-livestream --all-targets -- -D warnings`
- `cargo test -p synctv-livestream` (239 passed, 20 ignored)
- `git diff --check`
3 weeks ago
zijiren 2445da8630
refactor(metrics): centralize registry and lifecycle tracking (#446)
## Summary

- split Prometheus definitions into domain-owned modules backed by one
registry
- eagerly initialize every metric family and fail startup on invalid or
duplicate definitions
- add reusable RAII guards for gauges and relay durations across
cancellation, panic, and retry paths
- centralize HTTP, WebSocket, gRPC, and relay recording behind
bounded-label helpers
- enforce the 65-metric bilingual catalog from registered descriptors
- persist completed explicit-message deletion and user-ban moderation
steps
- update the pinned Rust toolchain and CI jobs to `nightly-2026-08-25`
- make the CI Clippy job fail on every Rust warning with `-D warnings`
- keep Helm configuration-validation builds visible to avoid silent-run
termination
- separate Rust caches by artifact type and let Helm reuse codegen
artifacts from the Build job
- work around the nightly global next-solver memory regression and
remove obsolete rustc recursion-limit overrides
- preserve workspace Rust flags in Docker builds while applying linker
flags to the final binary

## Compatibility

- preserve all existing metric names, HELP text, label order, and
histogram buckets
- keep route-template and bounded status/error labels
- return HTTP 500 when Prometheus exposition encoding fails
- keep the latest nightly while restoring the previous coherence-only
trait-solver behavior

## Nightly memory regression

The failed CI jobs were terminated by the runner while compiling
`synctv-api-http` (SIGTERM, exit 143). Local peak-RSS measurements for
that crate were:

| Configuration | Peak RSS |
| --- | ---: |
| `nightly-2026-08-21` | 6.62 GB |
| `nightly-2026-08-25`, global next solver | 10.76 GB |
| `nightly-2026-08-25`, `-Znext-solver=coherence` | 6.64 GB |

Rust enabled `-Znext-solver=globally` by default on nightly in
[rust-lang/rust#160619](https://github.com/rust-lang/rust/pull/160619).
The same memory blow-up is tracked in
[rust-lang/rust#161748](https://github.com/rust-lang/rust/issues/161748),
and the official tracking issue documents `-Znext-solver=coherence` as
the temporary opt-out. The workspace now applies that option in
`.cargo/config.toml`.

The Dockerfile previously set `RUSTFLAGS` for linker options, which
overrode the workspace configuration and re-enabled the global solver
inside image builds. It now uses `cargo rustc` to pass linker flags only
to the final binary, preserving the workspace solver setting for every
crate.

All rustc `#![recursion_limit = "256"]` attributes were removed. The
default limit passes with the coherence-only solver, confirming that a
higher recursion limit was unrelated to the CI termination.

## Validation

- `make build-workspace`
- `make clippy-check` (`--workspace --all-targets -- -D warnings`)
- `cargo check --locked -p synctv-api-http`
- `cargo check --locked -p synctv-api-common -p synctv-api-grpc`
- `cargo test -p synctv-api-common --lib` (655 passed, 165 Docker tests
ignored)
- `cargo test -p synctv-core metrics --lib` (9 passed)
- `make fmt-check`
- `docker build --check .`
- `make validate-helm`
- `actionlint .github/workflows/ci.yml .github/workflows/helm-ci.yml`
- `npm run validate` in `docs` (124 pages, 353 links, Astro 0
errors/warnings)
4 weeks ago
zijiren 7fb7f6c64f
feat(admin): add async chat moderation cleanup (#445)
## Summary
- add an asynchronous admin chat moderation job pipeline for message,
reaction, and ban cleanup
- expose the moderation operation through HTTP/gRPC admin APIs and
persist resumable worker progress
- remove the obsolete version parameter from the async moderation path
while retaining CAS for synchronous chat edits/deletes

## Verification
- cargo check --workspace --all-targets
- cargo test -p synctv-core --lib (1350 passed)
- cargo test -p synctv-api-common --lib (655 passed)
- PostgreSQL moderation race regression passed
4 weeks ago
zijiren ad3a8b3930
ci: isolate documentation validation (#444) 4 weeks ago
zijiren c77f42243d
fix(emby): tolerate hidden browse ancestors (#443)
## Summary

- classify empty Emby item lookups and provider 404 responses as not
found
- stop dynamic browse breadcrumb traversal after a visible item when an
ancestor is hidden
- preserve errors for missing target items and non-not-found provider
failures

## Validation

- `cargo test -p synctv-media-providers`
- `cargo test -p synctv-core`
- `cargo test -p synctv-api-common --lib`
- `cargo clippy -p synctv-media-providers -p synctv-core --all-targets
-- -D warnings`
- `cargo fmt --all -- --check`
- real Emby dynamic-playlist browse and playback validation with two
media items
4 weeks ago
zijiren 0633b83b4e
feat(emby): support passwordless accounts (#442)
## Summary
- Allow Emby users with an explicitly empty password to authenticate.
- Preserve password whitespace and keep missing credentials or empty API
keys invalid.
- Accept passwordless credentials through the remote provider API and
expose the CLI `--no-password` option.
- Update proto comments and CLI documentation.

## Validation
- Media provider test suite: 552 passed, 5 ignored.
- Focused Emby, gRPC, CLI, formatting, clippy, proto freshness, and
documentation checks passed.
1 month ago
zijiren c7aa2b0636
docs: add Nginx reverse proxy guide (#441)
## Summary

- add Chinese and English Nginx reverse proxy guides for the SyncTV HTTP
and WebSocket entrypoint
- disable Nginx proxy caching and upstream response buffering for all
API, Web UI, HLS, and media responses
- preserve SyncTV `Cache-Control` headers and explain why request
buffering remains independent
- link the guide from navigation, production checks, and media latency
troubleshooting

## Scope

The example covers the HTTP/WebSocket entrypoint only. It does not add
gRPC, RTMP, or Ingress configuration.

## Verification

- `npm run validate` in `docs`
- docs content validation: 124 pages, 353 internal links
- Astro diagnostics: 0 errors, 0 warnings, 0 hints
- static build and internal link validation
- `git diff --check`

`nginx -t` was not run because Nginx is not installed in the local
environment.
1 month ago
zijiren 9ebfef16e9
feat(web): support runtime UI assets (#440)
## Summary

- add a `web-ui-dynamic` feature that serves a mutable Web distribution
without compile-time embedding
- add `server.web_ui_directory` and `SYNCTV_SERVER_WEB_UI_DIRECTORY`,
with per-request disk reads, SPA fallback, security headers, and path
containment checks
- keep `web-ui` release builds embedded while allowing the same
production binary and Docker image to use a mounted runtime directory
- configure `make dev-serve` to use `synctv-web-ui/dist` and document
both modes

## Docker behavior

CI continues to build release images with `web-ui`, so embedded assets
remain the default. Mounting a distribution and setting
`SYNCTV_SERVER_WEB_UI_DIRECTORY` makes that directory authoritative at
runtime.

## Verification

- `cargo fmt --all -- --check`
- `git diff --check`
- `cargo test -p synctv-api-http --features web-ui-dynamic
http::web_ui::tests`
- `cargo test -p synctv-api-http --features web-ui http::web_ui::tests`
- `cargo check -p synctv --features web-ui-dynamic`
- `cargo check -p synctv --features web-ui`
1 month ago
zijiren 0778c2b23e
feat(rtmp): add advertised publish workflow (#439)
## Summary

- add the RTMP advertised address runtime setting and use it when
returning generated publish URLs
- generate reusable media-scoped publish sessions through the
provider-owned workflow
- preserve single-use publish keys while allowing creators to mint
replacements
- update runtime-setting documentation and generated protobuf contracts

## Verification

- cargo check -p synctv --features web-ui
- git diff --check
- end-to-end RTMP publish and playback against the embedded Web UI
1 month ago
zijiren c7d885c4d8
chore: finalize web UI root coverage and docs (#438)
## Summary

- verify that the embedded Web UI serves the SPA entrypoint directly at
the root path without a redirect
- use idiomatic Option handling in the embedded asset response path
- remove outdated setup sections from the English and Chinese project
READMEs

## Validation

- cargo test -p synctv-api-http --features web-ui
root_serves_spa_entrypoint_without_redirect
- make clippy-check
- cargo fmt --check
- git diff --check
1 month ago
zijiren b946d397dd
fix(proxy): preserve seek range requests after inconclusive HEAD (#437)
## Summary

- keep non-zero Range requests when a HEAD response does not advertise
Accept-Ranges
- use slice caching when the upstream answers 206 and preserve
full-response fallback when it answers 200
- add regression coverage for inconclusive HEAD probes and range
fallback behavior

## Validation

- cargo test -p synctv-proxy --test slice_cache_tests (94 passed)
- cargo check --workspace --all-targets
- cargo fmt --check
- verified Alist, Emby, and Bilibili proxy seeks return correct 206
responses and byte ranges
1 month ago
zijiren 882cbe09bb
feat(logging): add structured transport access logs (#436)
## Summary

- add structured HTTP and gRPC access logs with request correlation,
status-aware levels, response bytes, and complete body lifecycle timing
- route diagnostic and access events through independently configurable
text or JSON outputs with non-blocking writers and dropped-line
accounting
- honor the configured IANA timezone across text, JSON, diagnostic, and
access logs with DST-aware RFC 3339 offsets
- keep HTTP metric labels bounded while logging concrete unmatched paths
without query strings
- reduce local development noise by defaulting global logs to info while
retaining component debug logs

## Behavior

- HTTP 2xx and 3xx complete at info, 4xx at warn, and 5xx or body
failures at error
- slow handlers are raised to warn; ordinary client response
cancellation remains debug
- gRPC completion waits for final trailers and records the canonical
gRPC status
- request IDs are validated, propagated in responses, and included in
mapped gRPC errors
- header latency and full response lifecycle latency are reported
separately
- log timestamps use the configured time.timezone and include a numeric
UTC offset

## Testing

- cargo test -p synctv-api-common transport_access_log --lib
- cargo test -p synctv-core logging::tests --lib
- cargo test -p synctv resource_options::tests --lib
- cargo clippy for affected crates and all targets with warnings denied
- cargo check --workspace --all-targets
- cargo fmt --all -- --check
- git diff --check
- local server smoke tests for request completion, request ID
propagation, byte counts, query omission, and Asia/Shanghai timestamps
in diagnostic and access logs
1 month ago
zijiren 63c370876d
feat(web): add client-aware playback and reproducible UI (#433)
## Summary

- add a versioned playback client profile for browser/runtime protocol,
container, codec, header, proxy, insecure-media, and P2P-loader
capabilities
- generate compatible direct and proxy resources inside each provider
and return a structured incompatibility error when route policy leaves
no viable result
- force provider proxy delivery for browser-forbidden headers, including
affected Bilibili variants, while preserving explicit direct-only
failures
- keep legacy clients compatible and isolate capability-aware playback
cache entries
- serve `/oauth2/callback` through the same Flutter SPA entry point and
keep public discovery anonymous

## Reproducible Web UI

- move Flutter acquisition/build, source configuration, asset manifests,
Brotli/gzip compression, and compile-time embedding into the independent
`synctv-web-ui` crate
- support prebuilt distributions, local projects, and Git sources pinned
to an immutable full commit, with an ignored local override
- fingerprint source, Flutter version, build arguments, dart-defines,
builder generation, output, and compression settings
- validate cached Git repository/revision/commit identity, support
offline cache reuse, and rebuild only when relevant inputs change
- pin and checksum the Flutter SDK in the Docker Web asset stage;
backend-only builds require no Flutter, Git fetch, or network
- serve embedded assets with ETags, compression negotiation, CSP, cache
policy, Origin/CORS handling, and SPA fallback outside API/media routes

## Verification

- `cargo fmt --all -- --check`
- workspace `cargo check`, test/doc-test, and all-targets Clippy with
warnings denied
- provider tests: 200 passed, 1 ignored
- OAuth core/API tests: 72 + 16 passed
- `synctv-web-ui`: 10 passed
- `synctv-api-http --features web-ui`: 12 passed
- default Git source cold build from the pinned App commit, followed by
offline hot-cache reuse in about 1.1 seconds
- `cargo check -p synctv --features web-ui` and `docker buildx build
--check .`
- real Chrome playback, P2P, OAuth/Casdoor, multi-user sync, chat,
media, playlist, upload, settings, and playback-history flows

Companion frontend PR: https://github.com/synctv-org/synctv-app/pull/52

Release pin validation:
https://github.com/synctv-org/synctv-release/pull/9
1 month ago
zijiren 9936e555d7
feat: add playback history management (#431)
## Summary

- add ordered cursor pagination for room playback history
- add room-manager APIs to delete individual entries or clear history
- publish history mutations through the existing playback realtime path
- preserve live playback progress and cross-instance version fencing
during mutations
- document the new query parameters and refresh SQLx offline metadata

## Testing

- `cargo check --workspace --all-targets`
- `make clippy-check`
- `cargo fmt --all -- --check`
- core playback tests: 108 passed, 8 ignored
- HTTP/OpenAPI tests: 226 passed, 59 ignored
- PostgreSQL integration test for delete, clear, current-entry handling,
and playback position continuity
1 month ago
zijiren 89b16ab21e
release: prepare v1.0.3 (#430)
## Summary

- bump the server, Helm chart, and documentation metadata to v1.0.3
- keep the Compose image fallback validation aligned with the
configurable image tag
- release the latest upstream main commit with personal user blocking

## Verification

- `make fmt-check`
- `make validate-helm`
- `cargo test -p synctv-api-common user_block --locked`
1 month ago
zijiren 52b004bab2
feat: add personal user blocking (#429)
## Summary

- add idempotent block, unblock, and paginated block-list APIs across
HTTP, gRPC, service, and repository layers
- hide blocked creators from authenticated personal discovery while
leaving anonymous discovery unchanged
- preserve joined, favorited, and directly accessed rooms with a
creator_blocked marker
- filter blocked users from message history, search, context, pinned
messages, replay, unread counts, live events, and reconnect replay
- add the user_blocks migration, SQLx offline metadata, and
repository/API coverage

## Behavior

Blocking is private to the current account. It does not alter the
blocked account or public anonymous discovery. Detailed personal room
lists remain complete and expose creator_blocked so clients can label
the creator. Observer-specific realtime filtering advances event cursors
and fails closed when block-state lookup is unavailable.

## Verification

- make check
- make fmt-check
- cargo test --workspace: 5222 passed
- synctv-api-common library tests: 638 passed, 165 ignored
- PostgreSQL favorite-room creator_blocked integration test
- SQLx prepare across the workspace
- real two-account HTTP, realtime, and room-flow integration test

Client: https://github.com/synctv-org/synctv-app/pull/47
1 month ago
zijiren 61a837a424
docs: document TOTP support and limitations (#428)
## Summary

- document TOTP enrollment, verification, recovery codes, lifecycle, and
security controls in Chinese and English
- add HTTP and gRPC client flows, MFA request examples, and
recovery-code status fields
- describe clock synchronization, data retention, OAuth2 behavior, and
encryption-key rotation limitations

## Validation

- `npm run validate:content`
- `npx astro check`
- `npx astro build`
- `git diff --check`
1 month ago
zijiren 96d22abe99
fix(webrtc): use public STUN by default (#427)
## Summary

- disable the built-in SyncTV STUN server by default across application,
Compose, image, environment, and Helm configuration
- default `webrtc.externalIceServers` to Cloudflare and Google public
STUN endpoints while preserving persisted overrides and explicit `[]`
- update deployment validation, tests, examples, and English/Chinese
documentation

Tailscale is not included because it publishes STUN endpoints through
its dynamic DERP map and does not provide a stable general-purpose
hostname suitable for this default.

## Testing

- `cargo fmt --all -- --check`
- `cargo test -p synctv-core --test global_settings_registry_tests`
- `cargo test -p synctv-core
test_runtime_settings_default_to_public_stun_servers`
- `cargo test -p synctv default_webrtc_config_disables_builtin_stun`
- `cargo check --workspace --all-targets`
- `bash scripts/validate-helm.sh`
- Compose configuration rendering
- `npm run validate:content`
- `npm run check`
- `git diff --check`
1 month ago
zijiren 8711158d60
refactor: remove low-value thin wrappers (#426)
## Summary

- remove unused compatibility APIs and low-value forwarding wrappers
across the workspace
- inline service, repository, provider, cluster, proxy, and protocol
paths while preserving existing behavior
- remove stale dependencies, metrics, tests, and SQLx metadata generated
for deleted queries

## Validation

- `cargo check --workspace --all-targets`
- `cargo fmt --all -- --check`
- `git diff --check`
- relevant test suites: 1,408 passed, 184 ignored
- broader test run: 2,286 passed
1 month ago
zijiren b74e8c279c
fix(room): enforce member-owned resource lifecycle (#425)
## Summary
- preserve room membership and owned resources across global bans while
blocking browsing and playback until unban
- clean up member-owned media and dynamic playlists on leave, kick, and
account deletion while preserving ordinary shared playlists
- enforce lifecycle checks for playback, history, dynamic browsing,
direct IDs, backend generation, publish keys, RTMP, and historical
dynamic ancestors
- serialize playback transitions with ban and lifecycle cleanup
transactions and emit realtime outbox events atomically

## Verification
- `make fmt-check`
- `make clippy-check`
- `make nextest-default` (5238 passed, 1596 skipped)
- 15 targeted ignored Testcontainers lifecycle/concurrency tests passed
- `make sqlx-prepare` with only changed offline query cache entries
retained
1 month ago
zijiren 77b8632124
feat(playlist): add browse access controls (#423) 1 month ago
zijiren 716a28087a
feat(presence): separate members and guests (#422)
## Summary

- split room, node, and global presence into authenticated member and
anonymous guest counts
- deduplicate member and guest actors independently while retaining
total connection counts
- update discovery ordering and realtime online-count messages to use
the separated values
- remove the previous mixed online-user field as a breaking protocol
change

Frontend: https://github.com/synctv-org/synctv-app/pull/41

## Verification

- make nextest-default (5231 passed)
- make clippy-check
- make proto-freshness
- cargo fmt --all -- --check
- git diff --check
1 month ago
zijiren 503880e6a1
chore(deps): update backend dependencies (#421)
## Summary
- update pinned backend dependencies and regenerate Cargo.lock
- keep intentional compatibility pins for bincode, testcontainers,
rand/sha2 aliases, and the combine patch
- update streaming encoder configuration and resolve current Clippy
compatibility

## Validation
- cargo check --workspace --all-targets
- cargo test --workspace --lib
- make nextest-default
- make clippy-check
- make proto-freshness
- make check-all-targets
- cargo fmt --all -- --check
- cargo audit --quiet
- cargo deny check
- cargo update --dry-run

All backend checks passed.
1 month ago
zijiren c80f660637
feat(cli): allow system room visibility updates (#420)
## Summary

- allow `synctv room visibility` to omit the actor and run as an
authenticated management-plane operation
- preserve optional `--username`, `--user-id`, and `--email` actor
selection for normal room permission enforcement
- keep the permission bypass confined to the management service and
reuse the same visibility update, cache invalidation, and guest
revocation behavior

Follow-up to #419.

## Validation

- `cargo fmt --all -- --check`
- `cargo check -p synctv-core -p synctv-management -p synctv`
- `cargo clippy -p synctv-core -p synctv-management -p synctv
--all-targets -- -D warnings`
- `cargo test -p synctv room_visibility` (3 passed)
- verified `synctv room visibility --help` shows actor selectors as
optional and still requires exactly one of `--public` or `--private`
1 month ago
zijiren 198cd5a9cb
feat: separate room visibility from guest settings (#419)
## Summary

- store room public visibility as a first-class database field
independent of password and guest settings
- expose visibility through client, management, HTTP, gRPC, CLI,
discovery, and room creation review flows
- add `synctv room visibility ROOM_ID --public|--private` for changing
existing rooms with an explicit actor
- deny anonymous guest access to private rooms while preserving
authenticated member access
- revoke existing guest sessions immediately when a room becomes private
without disconnecting authenticated members

## Validation

- `cargo fmt --all -- --check`
- `cargo clippy -p synctv-core -p synctv-api-common -p synctv-api-http
-p synctv-api-grpc -p synctv --all-targets -- -D warnings`
- `cargo clippy -p synctv-management -p synctv --all-targets -- -D
warnings`
- `cargo check -p synctv-core -p synctv-api-common -p synctv-api-http -p
synctv-api-grpc -p synctv`
- `cargo check -p synctv-management -p synctv`
- `cargo test -p synctv room_visibility`
- verified `synctv room visibility --help` renders the required actor
and exactly one of `--public` or `--private`
- targeted realtime bridge, guest principal, realtime event, and room
service tests
- real macOS client verification: changing a public room to private
terminated the guest connection in about two seconds with
`REALTIME_TERMINATION_CODE_GUEST_ACCESS_REVOKED`; authenticated users
remained connected
1 month ago
zijiren 4a5f7630ad
feat: add publish key lifecycles (#418)
## Summary

- add single-use, reusable expiring, and permanent RTMP publish keys
- validate lifecycle and expiration combinations in the core service
- expose lifecycle fields through HTTP, management gRPC, and CLI
- keep JTI consumption limited to single-use keys

## Validation

- `cargo fmt --all -- --check`
- `make clippy-check`
- `make check-all-targets`
- `cargo test -p synctv-core service::publish_key::tests`
1 month ago
zijiren 90b8abf1e6
fix(auth): warn on weak JWT secrets (#417)
## Summary

- keep JWT minimum length and character-class requirements as blocking
validation errors
- downgrade heuristic quality checks such as low diversity, repeated
patterns, and low entropy to startup warnings
- emit configuration warnings after logging initialization, with stderr
fallback for config validation and unknown settings
- count Unicode characters consistently in secret length, entropy, and
pattern checks

## Tests

- `cargo test -p synctv-core service::auth::jwt::tests`
- `cargo test -p synctv app_config::validation::tests`
- `cargo test -p synctv config_loader::tests`
- `cargo check -p synctv`
- `cargo fmt --all -- --check`
1 month ago
zijiren 6b884e9964
fix(bilibili): restore subtitles and dynamic live danmaku (#415)
## Summary
- Propagate dynamic playlist identity into Bilibili playback generation
and expose room-scoped live danmaku SSE.
- Resolve each SSE connection against the current playlist target and
provider instance, retaining the playlist credential owner.
- Restore loopback-only PostgreSQL and Redis ports in the development
Compose profile so host-based `make dev-serve` can connect.

## Validation
- `cargo clippy -p synctv-core -p synctv-api-common -p synctv-api-http
--all-targets -- -D warnings`
- `cargo test -p synctv-core
dynamic_live_playback_has_a_live_danmaku_track`
- `cargo test -p synctv-api-common
dynamic_live_danmaku_provider_converts_to_room_scoped_endpoint`
- `cargo test -p synctv-api-http`
- `docker compose -p synctv-dev -f docker-compose.dev.yml config -q`
1 month ago
zijiren 5afaad79ff
fix(realtime): separate presence events from disconnects (#416)
## Summary
- Route `UserLeft` through room presence delivery only.
- Reserve disconnect signals and access-revocation events for
terminating realtime sessions.
- Keep cross-replica presence coverage and explicitly release E2E test
infrastructure.

## Testing
- `make nextest`
- `cargo nextest run -p synctv-api-common --no-fail-fast`
- `cargo nextest run -p synctv-realtime --no-fail-fast`
- `cargo nextest run -p synctv-api --test websocket_test
websocket_e2e::test_ws_cross_replica_same_user_partial_disconnect_does_not_emit_user_left
--run-ignored ignored-only --no-fail-fast`
1 month ago
zijiren 980c409e26
chore(docker): keep database and cache internal (#414)
## Changes
- Remove PostgreSQL and Redis host port publications from production and
development Compose files.
- Keep application connectivity through the existing Compose networks
and service names.

## Validation
- docker compose -f docker-compose.dev.yml config --quiet
- git diff --check
1 month ago
zijiren d560b8efce
fix: harden Bilibili playback reliability (#412)
## Summary

- Treat explicit null collection fields in Bilibili VOD playback
responses as empty collections.
- Cover DASH streams, backup URLs, Dolby audio, supported formats, DURL
responses, subtitles, video pages, UGC collections, and PGC episodes.
- Resolve Bilibili DASH resources from versioned playback metadata and
provide every equivalent CDN URL to the existing candidate executor.
- Prefer the URL selected by the player, then retry primary and backup
CDNs before committing the downstream response.

## Root cause

Bilibili occasionally emits explicit null for fields normally
represented as JSON arrays, which caused playback/start deserialization
errors. Proxy DASH resource requests also used a single CDN even though
the provider response included backup URLs.

## Validation

- cargo fmt --all -- --check
- cargo check for synctv-api-common, synctv-api-http, and
synctv-api-grpc
- synctv-media-providers Bilibili tests: 91 passed
- synctv-core Bilibili tests: 17 passed, 2 ignored
- candidate executor tests: 2 passed
- Real playback/start, proxy MPD, Range request, CDN fault injection,
dev build, and dev smoke checks
1 month ago
zijiren dd80379d67
feat(provider): improve media discovery and direct playback (#411) 1 month ago
zijiren 2f61d0e8de
release: prepare v1.0.2 (#408)
## Summary

- bump the Cargo workspace to 1.0.2
- synchronize Helm chart and documentation versions

## Verification

- `make fmt-check`
- `make clippy-check`
- `make nextest-default` (5,186 passed)
- `make nextest-ignored` (1,585 passed)
- `make validate-helm`
1 month ago
E-larex c30d3e40b1
fix(oauth2): accept trailing slash in OIDC issuer (#401)
Fix OIDC ID Token validation when the provider returns an issuer with a
trailing slash.

SyncTV removes trailing slashes from the configured issuer, but
`jsonwebtoken` previously performed an exact issuer check before
SyncTV's normalized issuer comparison. This caused valid tokens from
providers such as Authentik to fail with `InvalidIssuer`.

The JWT validation now accepts both trailing-slash variants of the same
issuer while continuing to reject unrelated issuers.

Added RS256/JWKS regression tests covering both accepted configurations
and a different-issuer rejection case.
1 month ago
zijiren 9e0e0c280e
chore(dev): remove background server targets (#407)
## Summary

- remove the `dev-start` and `dev-stop` Makefile targets and related
documentation
- keep `dev-serve` as the foreground development server entry point
- make the provider smoke test own and clean up its server process
- keep `dev-down` and `dev-clean` scoped to Compose services

## Verification

- `bash -n scripts/dev-e2e-smoke.sh`
- Makefile dry runs for `dev-serve`, `dev-down`, and `dev-clean`
- `make help` target inspection
- `git diff --check`
- existing development backend health check
1 month ago
zijiren b6acab535e
fix: type playback identities and stabilize FNOS playback (#400) 1 month ago
zijiren a6c0af59ff
fix(settings): recover stale version fences (#399)
## Changes
- roll back local and Redis reservations when a settings transaction
aborts
- atomically reconcile stale committed fences against the database
version
- repair runtime-setting fences during startup and before writes
- retry paths now refresh fence state after SQL optimistic-lock
conflicts

## Verification
- cargo test -p synctv-core --lib
- cargo check --workspace
- cargo clippy -p synctv-core -p synctv-api-common --all-targets -- -D
warnings
- real HTTP regression with Redis fence seeded above the database
version
1 month ago
zijiren 0043ec8b2e
feat(providers): unify discovery and playback routing (#397)
## Summary

- expose server-side provider discovery and listing for media and
dynamic playlist variants
- keep provider-specific playback proxy policy inside each provider and
add meaningful prefer/only controls
- harden Emby item classification, cloud/NAS playback routing,
account-bound credentials, and unknown source-config fields
- add real Docker fixtures and end-to-end smoke coverage for Nextcloud,
Seafile, Emby, Jellyfin, OpenList, and provider playback

Related App PR: https://github.com/synctv-org/synctv-app/pull/18

## Testing

- cargo +nightly check --workspace --locked
- cargo +nightly test -p synctv-media-providers seafile::client::tests
--locked
- bash -n scripts/dev-e2e-smoke.sh
- scripts/dev-e2e-smoke.sh with real Docker services and playback URL
downloads
- git diff --check
1 month ago
zijiren 9bc010c568
fix: classify forced realtime disconnects (#395)
## Summary

- send a typed realtime termination message before server-initiated
WebSocket disconnects
- restore the view_playback_history permission bit across effective
permissions and runtime settings round trips
- remove the unused common ErrorCode enum and document the dedicated
termination codes

## Validation

- make nextest: 6715 passed
- make clippy
- git diff --check
1 month ago
zijiren da1abd1a68
feat: add recoverable resource deletion lifecycle (#394)
## Summary

- Add recoverable soft deletion for users, rooms, playlists, media, and
chat messages, with retention-based physical cleanup.
- Hide deleted users and owned resources from active API, room,
playback, messaging, and realtime paths while preserving restoration
metadata.
- Add user lifecycle administration across HTTP, gRPC, management
runtime, and SyncTV CLI, including deleted-user listing and restore
operations.
- Add lifecycle migration, cleanup orchestration, audit fields,
resource-change events, and refreshed SQLx offline metadata.

## Design

- Model account, administrator, system, room, and user deletion sources
explicitly so cascade ownership and audit history remain deterministic.
- Release email and OAuth identity occupancy during deletion;
restoration reclaims available identities and reports conflicts
according to restore options.
- Preserve recoverable aggregate rows during the retention window,
remove ephemeral membership and playback state immediately, and purge
expired data in dependency order.
- Wait for realtime observation acknowledgements before publishing
lifecycle changes to avoid subscription registration races.

## Verification

- `make nextest`: 6707 passed, 0 skipped
- `make clippy`: passed for the workspace and all targets
- `make clippy-check`: passed
- `cargo fmt --all -- --check`: passed
- `git diff --check`: passed
2 months ago
zijiren c0e3c2dae5
fix(docs): preserve TAR end-of-archive blocks (#389)
## Summary
- Allocate an explicit TAR output buffer sized to the written bytes plus
two 512-byte end blocks.
- Copy the archive payload into the buffer so TAR downloads always
include the required zero blocks.

## Testing
- `npm run validate` from `docs/`
- TAR end-block assertion with `tar-js`
2 months ago
zijiren e42aed8b9f
fix(docs): package Compose environment files as TAR (#388)
## Summary
- Fix Compose environment archive filenames for issue #385.
- Provide a lazy-loaded TAR download containing all three environment
files.
- Use Starlight code blocks for environment previews and built-in copy
controls.
- Document Linux hidden files and the `ls -a` command after extraction.

## Testing
- `npm run validate` (from `docs/)
2 months ago
zijiren 8208b5a27f
fix(account): skip standalone room outbox events (#387)
## Summary
- persist deleted-room events only when the prepared event requires the
realtime outbox
- keep local post-commit fanout for standalone deployments
- prevent account closure from failing when cluster mode is disabled

## Verification
- cargo fmt --all -- --check
- cargo check -p synctv-api-common -p synctv-api-grpc -p synctv-api-http
- cargo test -p synctv-core deleted_room_outbox_config --lib
2 months ago
zijiren f4665ebea4
Merge pull request #382 from synctv-org/codex/apple-review-docs
docs(apple): explain Developer ID browser authentication
2 months ago
zijiren233 6eeb974c9b
docs(apple): explain Developer ID browser authentication 2 months ago
zijiren 4b1e677d21
Merge pull request #381 from zijiren233/codex/fix-compose-validation-fallback
fix(helm): validate configurable compose image
2 months ago