Commit Graph

933 Commits (9a2c8ab8b769612eb5fa0f83476f00fa5032fc59)
 

Author SHA1 Message Date
zijiren 9a2c8ab8b7
release: prepare v1.0.7 (#493)
Bump workspace version to 1.0.7 via scripts/set-release-version.sh
(Cargo workspace, Cargo.lock, Helm chart, docs metadata).

- Pin embedded Web UI to synctv-app v1.1.27
(fb614c04153c81fbccbbfc0b688f10ffb7e9df9a)
4 days ago
zijiren 9ffdc3e280
chore(deps): refresh dependencies, nightly 2026-10-23, CI actions (#492)
## Workspace pins
- redis 1.7.1 → 1.8.0
- uuid 1.28.0 → 1.29.0
- sha2 0.11.0 → 0.11.1
- rusty_aac 1.1.1 → 1.1.2
- webpki-roots 1.0.10 → 1.0.11
- `cargo update`: 37 compatible transitive bumps (ureq 3.5.0, pest
2.9.4, async-compression 0.4.51, icu 2.4.0, zerocopy 0.8.63, …)

## Held pins (re-verified, comments still accurate)
- bincode 2.0.1 — 3.0.0 remains an intentionally unbuildable placeholder
- testcontainers 0.27.3 — testcontainers-modules 0.15.0 still requires
`^0.27`; 0.28 not adoptable
- rand_08 / rand_chacha_08 / sha2_010 — dual-version pins for legacy
consumers
- dashmap 7.0.0-rc2 / opaque-ke 4.1.0-pre.2 / webauthn-rs 0.6.1-dev /
http-acl 0.14 / serde_yaml 0.9 — no newer releases

## Toolchain
nightly-2026-10-22 → nightly-2026-10-23 (1.101.0-nightly),
rust-toolchain.toml + 5 workflow references.

## CI
actions/upload-artifact v7.0.1 → v7.0.2; flutter-action SHA already at
latest v2.23.0.

## Verification
- `cargo audit`: no new advisories (only the allowed serde_yaml →
unsafe-libyaml unmaintained warning)
- `cargo check --workspace --all-targets`: clean (pre-existing
unused-dep warnings only)
- `cargo test --workspace` on the new nightly: **5370 passed, 0 failed**
4 days ago
zijiren 7e7d9577e4
chore(deps): security bump tokio 1.54.1, parking_lot 0.13, nightly 2026-10-22 (#491)
## Security

- **tokio 1.54.0 → 1.54.1** — RUSTSEC-2026-0384 / GHSA-5wjr-9356-4wc8:
race condition between `tokio::time::Interval::poll_tick` and the
multi-thread runtime's time driver causes a **use-after-free on the
timer waker/task state in safe Rust** (introduced by tokio#8458, fixed
by tokio#8692). `cargo audit` is clean again; only the allowed
RUSTSEC-2024-0449 maintenance warning (unsafe-libyaml behind the pinned
serde_yaml) remains.

## Performance (breaking-major, analyzed per policy)

- **parking_lot 0.12.5 → 0.13.0**: significantly improved
`Mutex`/`RwLock` throughput under contention via deferred wakeups. The
0.13 breaking changes live in the `lock_api` layer (`RawMutex::Guard`
replacing `GuardMarker`, split rwlock guards), which this workspace
never touches — only the high-level `Mutex`/`RwLock` types are used (145
sites). The 0.12 line remains in the graph via `antlr4rust → cel →
prost-protovalidate` until those publish compatible releases; both
versions coexist legally.

## Toolchain

- nightly-2026-10-20 → **nightly-2026-10-22** (rust-toolchain.toml + all
workflows)

## Documented pins re-verified, unchanged

| pin | status |
|---|---|
| bincode 2.0.1 | 3.0.0 is still the intentionally unbuildable
placeholder (no real 3.x) |
| dashmap 7.0.0-rc2 | still no 7.0.0 final |
| opaque-ke 4.1.0-pre.2 | still the newest release |
| webauthn-rs 0.6.1-dev | still the newest release |
| testcontainers 0.27 | testcontainers-modules 0.15 still requires ^0.27
|
| serde_yaml 0.9.34 | replacement migration (saphyr/noyalib) risks
config-format divergence for the loader; kept as documented |

## Validation

- `make check`, `make clippy-check -D warnings`, `make fmt-check` clean
- `cargo nextest run --workspace --run-ignored all`: **6970/6970
passed**
- `cargo audit` and `cargo deny check` clean
4 days ago
zijiren 09b270dba1
release: prepare v1.0.6 (#489)
Bump workspace version to 1.0.6 via scripts/set-release-version.sh
(Cargo workspace, Cargo.lock, Helm chart, docs metadata).

- Pin embedded Web UI to synctv-app v1.1.26
(499c6e0e5017c1d2be30db14a5dcb89be417f04b)
- deps: rustls-webpki 0.103.15 -> 0.103.16 and webpki-root-certs 1.0.9
-> 1.0.10 (RUSTSEC-2026-0382 / RUSTSEC-2026-0383)
6 days ago
zijiren 7178142e26
fix(deps): patch rustls-webpki and webpki-root-certs advisories (#490)
CI (Security Audit / Dependency Policy) is currently red on main due to
two newly published advisories:

- rustls-webpki 0.103.15 -> 0.103.16 (RUSTSEC-2026-0382, certificate
verification may accept distrusted intermediates)
- webpki-root-certs 1.0.9 -> 1.0.10 (RUSTSEC-2026-0383, bundled root
certificates trust distrusted emSign intermediates)

Both are semver-compatible lockfile updates; no code changes required.
6 days ago
zijiren ca36865152
chore(deps): update dependencies and nightly toolchain to 2026-10-20 (#487)
## Dependency updates (exact pins)

- **http-acl 0.13.1 → 0.14.0**: security hardening — IPv4-embedded IPv6
ranges (IPv4-compatible `::/96`, translated `::ffff:0:0:0/96`, NAT64
`64:ff9b::/96`, 6to4 `2002::/16`) are now denied by default; previously
`::7f00:1` or `64:ff9b::7f00:1` could reach loopback through the SSRF
guard. Also fixes the reqwest resolver dropping every getaddrinfo answer
carrying port 0
- lru 0.19.0 (iterator Send/Sync bounds), ammonia 4.2.4, opendal 0.59.5,
oxc 0.155.0, rustls 0.23.46, rusty_aac 1.1.1, rusty_h264 0.16.1,
webpki-roots 1.0.10, zstd 0.14.3, tokio-util 0.7.20 and other
transitives

## http-acl 0.14 adaptation (synctv-common SSRF guard)

0.14 refuses adding non-global ranges via `add_denied_ip_range` at build
time. Our `DEFAULT_EXTRA_DENIED_RANGES` contained `2002::/16`, so
`try_build` failed and the guard silently degraded to the **deny-all
fallback** (every IP and hostname blocked). Fix:

- `2002::/16` moved to `DEFAULT_NON_GLOBAL_DENIED_RANGES` (the
SsrfPolicy-side list used by `is_ip_blocked`), together with the other
embedded ranges: `::/96`, `::ffff:0:0:0/96`, `64:ff9b:1::/48`
- new `test_ipv4_embedded_ipv6_blocked` covers all embedded probe forms
(including the NAT64 metadata address) and a global IPv6 control

## tokio 1.53.2 → 1.54.0

1.54.0 starts accepted sockets as readable and writable, so the raw-TCP
mocks in `error_handling_tests` could flush the response before the
client had written its request; hyper rejects such a connection with
`UnexpectedMessage` inside `reqwest::get`. The mocks now drain the
request head before answering (normal HTTP ordering, matches a real
server) and the suite passes on 1.54.0.

## Kept on purpose

- bincode 2.0.1 (3.0.0 placeholder), dashmap 7.0.0-rc2, opaque-ke
4.1.0-pre.2, webauthn-rs 0.6.1-dev, serde_yaml, testcontainers 0.27
(modules 0.15 still requires ^0.27)

## Toolchain / CI

- nightly-2026-10-13 → **nightly-2026-10-20** (rust-toolchain.toml + all
workflows)
- actions already float on latest major tags (checkout v7, cache v6, …)
— no changes needed

## Validation

- `make check`, `make check-all-targets`, `make clippy-check -D
warnings`, `make fmt-check` clean
- `cargo nextest run --workspace --run-ignored all`: **6970/6970
passed** (incl. docker-backed e2e)
- `cargo deny check`: advisories/bans/licenses/sources all ok
7 days ago
zijiren 444ba0e700
release: prepare v1.0.5 (#484)
Bump workspace version to 1.0.5 via scripts/set-release-version.sh
(Cargo workspace, Cargo.lock, Helm chart, docs metadata).

- Pin embedded Web UI to synctv-app v1.1.25
(5f4a678c3008a49f414fc1e6a41068a01bb2c8ba)
- docker.yml: Flutter 3.47.2 -> 3.47.7 to match the client toolchain
requirement
2 weeks ago
zijiren 0183a80da8
chore(deps): update dependencies and nightly toolchain to 2026-10-13 (#483)
## Dependency updates (exact pins)

- tokio 1.53.2, tokio-util 0.7.20, hyper 1.12.0, serde_json 1.0.154,
uuid 1.28.0, toml 1.1.8, opendal 0.59.4, ammonia 4.2.3, config 0.15.27,
http-acl 0.13.1, tokio-rustls 0.26.6, retina 0.4.21, zstd 0.14.2,
ffmpeg-sys-next 9.0.1, oxc 0.154.0, webrtc/rtc 0.21.1
- **rusty_aac 0.5.0 → 1.1.0**: 1.x is the stabilized release with a
security-hardening pass (crafted fill-element panic on overflow-checked
builds, stale decoder state after failed blocks; output bit-identical
otherwise). Usage in this repo is test-only, no API changes needed
- **html-escape 0.2.15 → 0.3.0**: fixes multi-codepoint HTML entity
decoding and speeds up common named entities; `decode_html_entities` is
used by provider title extraction

Deliberately kept (documented/intentional pins): bincode 2.0.1 (3.0.0
placeholder), dashmap 7.0.0-rc2, opaque-ke 4.1.0-pre.2, webauthn-rs
0.6.1-dev, serde_yaml, testcontainers 0.27 (testcontainers-modules 0.15
requires ^0.27).

## Toolchain and CI

- nightly-2026-09-27 → **nightly-2026-10-13** (rust-toolchain.toml + all
workflows)
- removed the `cargo-features = ["trim-paths"]` gate (stabilized in
1.101)
- actions: checkout v7, cache v6, setup-node v7, dependency-review v5,
setup-helm v5, create-pull-request v8

## Code changes required by new clippy lints (2026-10-13)

- `try_from_instead_of_from_str`: `RoomResourceKind` now implements
`FromStr` (call site uses `.parse()`)
- `items_after_test_module`: moved 7 `#[cfg(test)]` modules to end of
file

## Dependency cleanup (new cargo `unused_dependencies` lint)

- removed 13 unused crate-level deps, including `tonic-web` which is now
unused workspace-wide
- moved test-only `tempfile` (synctv-proxy) and `anyhow`
(synctv-media-providers) to dev-dependencies
- kept `synctv-cluster` in synctv-livestream: referenced only by TLS
feature forwarding (`synctv-cluster/tls-*`), which the lint does not
account for — a known false positive

## Validation

- `make check`, `make check-all-targets`, `make clippy-check`, `make
fmt-check` all clean
- `cargo nextest run --workspace --run-ignored all`: **6969/6969
passed** (incl. docker-backed e2e)
2 weeks ago
zijiren ca91048b9d
refactor: simplify API request handling (#460)
## Summary

- simplify API request and response plumbing by removing obsolete
indirection and legacy fields
- require explicit publish-key and notification request bodies and align
OpenAPI/protobuf contracts
- centralize pagination normalization across client room and user
endpoints
- remove the obsolete Web UI distribution environment path

## Validation

- `cargo fmt --all -- --check`
- `cargo clippy --workspace --all-targets -- -D warnings`
- `cargo test --workspace --lib --bins`
- `cargo test -p synctv-api-common --lib`
4 weeks ago
zijiren e368ec943c
perf(build): use lld on Linux and document solver/thread tuning (#477)
Stacks on #476 (same `ci.yml` regions are touched); merge after it or
rebase.

Motivation: rust-lang/rust#161748 (next-solver memory blowup) and
bevyengine/bevy#25512 (fix by avoiding deep structural auto-trait
resolution). This PR applies the applicable parts to SyncTV's build with
fresh measurements, and documents the deliberate non-changes.

## Measurements (nightly-2026-09-27, `cargo check -p synctv-api-http
--all-targets`, isolated recheck, 10-core arm64)

| Configuration | Wall | Peak rustc RSS |
| --- | --- | --- |
| `-Znext-solver=coherence` + `-Zthreads=8` (current) | 32 s | 3.8 GB |
| `-Znext-solver=coherence` + `-Zthreads=1` | 53 s | 2.8 GB |
| `-Znext-solver=globally` + `-Zthreads=8` | 37 s | **14.9 GB** |

- The #161748 regression **persists on current nightlies**: the
globally-enabled next solver still needs ~4x the peak memory on
`synctv-api-http` — beyond a 16 GB CI runner. Keeping
`-Znext-solver=coherence` is validated, now with numbers in the config
comment.
- `-Zthreads=8` is a good trade: 40% faster than 1 thread for +0.9 GB
peak. Kept, documented.
- Cold dev builds (`CARGO_TARGET_DIR` isolated, full dep graph): LLVM
**660 s / 7.7 GB** vs cranelift **671 s / 7.4 GB** → a cranelift dev
backend brings no win here (rustc frontend dominates this workspace), so
it is **not** introduced.

## Changes

- `.cargo/config.toml`: Linux gnu targets (x86_64 + aarch64) now link
with **lld** and keep `-z pack-relative-relocs`, replacing the ad-hoc
flags the Docker builder passed. `nextest` test binaries and `make
build-workspace` in CI now link with parallel lld instead of
single-threaded bfd. Target-specific rustflags replace
`build.rustflags`, so the nightly solver/thread flags are repeated in
the Linux tables and documented once in `[build]`.
- CI installs the `lld` package on every Linux job.
- `Dockerfile`: the duplicated `-Clink-arg` flags are removed (config
provides them).
- `CONTRIBUTING.md`: local Linux builds now note the `lld` package.
- Not done (deliberately): a bevy#25512-style code change (eager/manual
`Send`/`Sync` impls to shorten auto-trait chains). With coherence
enabled the heaviest crate peaks at 3.8 GB, well inside CI budgets, so
the invasive rewrite buys nothing today; the measured numbers above make
that call explicit.

## Validation

- `SQLX_OFFLINE=true cargo check -p synctv-api-http` passes with the new
config (non-Linux hosts keep the original rustflags — target tables only
match Linux).
- `cargo fmt --check` clean. The `cross-*` Makefile targets run
check/clippy only, so cross containers never invoke the linker and do
not need lld.
4 weeks ago
zijiren 6ec3fad709
chore(deps): update backend dependencies and nightly toolchain (#476)
## Summary

**Toolchain**
- Advance the pinned toolchain `nightly-2026-08-31` →
`nightly-2026-09-27` in `rust-toolchain.toml` and every workflow that
names it. The date pin is independent of the next-solver memory
regression — that regression is present in the pinned nightly too and is
handled by `-Znext-solver=coherence` in `.cargo/config.toml`, which is
unchanged.

**Dependency updates (33 pins, current releases at 2026-09-28)**
- Majors: **webrtc/rtc 0.20.4 → 0.21.0** (TURN relayer STUN fix on
0.20.5 plus the 0.21 line's SCTP zero-window deadlock, ICE-restart stall
and data-channel drop fixes), **utoipa 5.5.0 → 6.0.0** +
**utoipa-swagger-ui 9.0.2 → 10.0.1** (OpenAPI 3.2 support, Swagger UI
5.32.6), **brotli 8 → 9**, **zstd 0.13 → 0.14**, **opendal 0.58.2 →
0.59.3** (+http transport), **redis 1.6.0 → 1.7.1**, **rusty_h264 0.12 →
0.16**, **oxc 0.147 → 0.151**.
- Minors/patches: tower-http 0.7.1, reqwest 0.13.5, jsonwebtoken 11.1.0,
uuid 1.26.1, clap 4.6.7, clap_complete 4.6.11, config 0.15.26,
hyper-util 0.1.21, indexmap 2.14.2, ipnet 2.12.2, lru 0.18.5, rand
0.10.3, rustls 0.23.45, tokio-rustls 0.26.5, thiserror 2.0.21, toml
1.1.6, ammonia 4.2.0, and the transitive refresh in `Cargo.lock`. tokio
stays at 1.53.1 (current).

**Code adaptations**
- `synctv-xiu` webrtc 0.21: `RtpCodecKind` is now `#[non_exhaustive]`
(wildcard arms at five match sites), `TrackLocalStaticSample::new` takes
a `now: Instant`, and `rtc`'s `Sample` replaces `Default` with
`Sample::new(now)` in tests.
- `rusty_h264` drops `default-features = false`: the split-out
`rusty_h264-common` requires std (or `libm`) for its floating-point
math, and it is a test-only dependency here.

**Patch removal**
- The `[patch.crates-io]` combine override (installed for the Redis 1.6
`opaque!` future-incompat report) is removed: redis 1.7 no longer
depends on combine, clippy confirmed the patch was unused, and the
lockfile resolves cleanly from the registry.

**Deliberately unchanged (documented in Cargo.toml)**
- bincode 2.0.1 (3.0.0 is an intentionally unbuildable placeholder),
testcontainers 0.27.3 (modules 0.15 requires ^0.27), rand/sha2 legacy
aliases, dashmap 7.0.0-rc2, opaque-ke 4.1.0-pre.2, webauthn-rs
0.6.1-dev, serde_yaml.

## Validation

- `SQLX_OFFLINE=true cargo check/clippy --workspace --all-targets` clean
under nightly-2026-09-27.
- `cargo fmt --check` clean; `cargo deny check` passes.
- `nextest`: **5353 passed, 1618 skipped** — includes the
WebRTC/streaming e2e suites exercising the new webrtc 0.21 stack and
rusty_h264 0.16 encoders.
- `make proto-freshness`: regenerated proto artifacts produce no diff.

Supersedes #462, #463, #464, #465, #466, #467, #468, #469, #470 and #475
(the open dependabot batch), which can be closed on merge.
4 weeks ago
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 months ago
zijiren 355b7790b6
fix: bilibili dash codec filter (#459) 2 months 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
2 months ago
zijiren 940d566ff0
chore: update nightly toolchain and dependencies (#455) 2 months 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`
2 months 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)
2 months 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
2 months ago
zijiren ad3a8b3930
ci: isolate documentation validation (#444) 2 months 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
2 months 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.
2 months 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.
2 months 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`
2 months 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
2 months 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
2 months 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
2 months 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
2 months 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
2 months 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
2 months 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`
2 months 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
2 months 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`
2 months 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`
2 months 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
2 months 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
2 months ago
zijiren 77b8632124
feat(playlist): add browse access controls (#423) 2 months 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
2 months 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.
2 months 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`
2 months 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
2 months 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`
2 months 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`
2 months 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`
2 months 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`
2 months 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
2 months 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
2 months ago
zijiren dd80379d67
feat(provider): improve media discovery and direct playback (#411) 2 months 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`
3 months 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.
3 months 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
3 months ago