Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ jobs:
npm ci
npx playwright install --with-deps ${{ matrix.browser }}

- name: Configure headless audio output
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends pulseaudio pulseaudio-utils
pulseaudio --start --exit-idle-time=-1
pactl load-module module-null-sink sink_name=rustchan_ci rate=48000
pactl set-default-sink rustchan_ci
pactl list short sinks

- name: Build RustChan
run: cargo build --locked --bin rustchan-cli

Expand Down Expand Up @@ -164,6 +173,15 @@ jobs:
npm ci
npx playwright install --with-deps ${{ matrix.browser }}

- name: Configure headless audio output
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends pulseaudio pulseaudio-utils
pulseaudio --start --exit-idle-time=-1
pactl load-module module-null-sink sink_name=rustchan_ci rate=48000
pactl set-default-sink rustchan_ci
pactl list short sinks

- name: Build RustChan
run: cargo build --locked --bin rustchan-cli

Expand Down
40 changes: 39 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ jobs:
- name: Run Clippy
run: cargo clippy --locked --workspace --all-targets --all-features

- name: Test native update packaging and signing
run: python3 -m unittest discover -s tools -p "test_update_package.py"

- name: Run tests
run: cargo test --locked --workspace --all-features

Expand Down Expand Up @@ -203,11 +206,19 @@ jobs:
)
fi

- name: Package signed-update inputs for native Linux
if: runner.os == 'Linux' && !contains(github.ref_name, '-')
shell: bash
run: python3 tools/update_package.py package --binary target/${{ matrix.target }}/release/rustchan-cli --target ${{ matrix.target }} --version "$GITHUB_REF_NAME" --output dist

- name: Upload artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: rustchan-cli-${{ matrix.asset_suffix }}
path: dist/*.zip
path: |
dist/*.zip
dist/rustchan-update-*.tar.gz
dist/build-info-*.json
if-no-files-found: error
retention-days: 7

Expand Down Expand Up @@ -276,11 +287,38 @@ jobs:
cd release-assets
sha256sum --check --strict SHA256SUMS.txt

- name: Create draft and sign native update artifacts
if: ${{ !contains(github.ref_name, '-') }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RUSTCHAN_UPDATE_SIGNING_KEY: ${{ secrets.RUSTCHAN_UPDATE_SIGNING_KEY }}
shell: bash
run: |
set -euo pipefail
test -n "$RUSTCHAN_UPDATE_SIGNING_KEY"
key_file="${RUNNER_TEMP}/rustchan-update-key.pem"
umask 077
trap 'rm -f "$key_file"' EXIT
printf '%s\n' "$RUSTCHAN_UPDATE_SIGNING_KEY" > "$key_file"
if ! gh release view "$GITHUB_REF_NAME" --json isDraft --jq '.isDraft' | grep -qx true; then
if gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1; then
echo "Refusing to alter an already published release" >&2
exit 1
fi
gh release create "$GITHUB_REF_NAME" --draft --title "Release $GITHUB_REF_NAME" --notes-file RELEASE_NOTES.md
fi
release_id="$(gh api "repos/$GITHUB_REPOSITORY/releases/tags/$GITHUB_REF_NAME" --jq '.id')"
python3 tools/update_package.py sign --directory release-assets --key "$key_file" --release-id "$release_id" --version "$GITHUB_REF_NAME"
rm release-assets/build-info-*.json

- name: Create GitHub Release
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
draft: false
prerelease: ${{ contains(github.ref_name, '-') }}
make_latest: ${{ !contains(github.ref_name, '-') }}
tag_name: ${{ github.ref_name }}
name: Release ${{ github.ref_name }}
body_path: RELEASE_NOTES.md
Expand Down
53 changes: 40 additions & 13 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,55 @@

All notable changes to RustChan will be documented in this file.

## Unreleased
## RustChan 1.6.0 — 2026-10-01

This release includes every integrated change since the published v1.5.0 release:
the container deployment workflow, correctness fixes, terminal and web administration
upgrades, public posting and reading improvements, and verified native software updates.

### Upgrade notes

- Back up the complete data directory before upgrading. Existing database structures are verified and stamped for the running 1.6.0 package; Rust 1.91 remains the minimum supported compiler.
- Native self-updates require one-time operator setup on Linux GNU x86_64 or ARM64: separate service identities, the supplied systemd/polkit configuration, a protected updater executable and independently provisioned trusted public key. The updater is operator-managed and is not replaced by application updates. See [Software Updates](docs/software-updates.md).
- Containers, macOS, Windows and custom installations use their deployment tools to install releases. Their admin panel can check releases but does not offer native installation. Keep container data volumes when recreating an instance.
- `/readyz` additionally reports the running package version. It does not expose release availability, updater transactions or retained backup metadata.

### Added

- Added the administrator-only Software Updates task, stable-release availability notice and persisted progress/results, integrated into the existing admin workspace and all nine themes. Installation requires the current administrator password, CSRF/same-origin validation, an expiring one-use approval and explicit restart confirmation.
- Added a separate native updater with bounded, fixed-operation local IPC and official-repository release discovery. Ed25519-signed manifests bind release identity, target, compatibility, sizes and hashes; strict archive and ELF checks reject unsafe or mismatched payloads before activation.
- Added mandatory verified pre-upgrade snapshots of the database, configuration, media and private runtime state. Failed activation, migration or readiness restores the previous executable and persistent state together; durable recovery gates startup after interruptions and retains evidence when operator intervention is required. Snapshot metadata appears in the existing Backups task.
- Added deterministic Linux update packaging, signed manifests/public-key assets and signing tests to the release workflow while retaining the four existing platform ZIPs and checksums. Stable publication requires the repository signing secret and verifies both native targets before publishing.
- Added a production container image, persistent-volume Compose setup and GHCR publishing for Linux amd64/arm64. Main and stable tags publish `latest`, version tags and immutable commit tags; Compose supports pinning a chosen image.

### Improved

- Expanded the terminal administration console with task-oriented browsing, contextual forms and dialogs, logs, history, telemetry, and responsive operator views.
- Reorganized the web admin panel into 21 task-focused sections with searchable configuration controls, account management, and separate active, saved, overridden, and next-restart settings states.
- Standardized admin spacing, typography, controls, tables, status treatments, and expandable details across all nine existing themes, including loading, error, empty, and disabled states.
- Improved responsive admin forms and moderation layouts down to 320px. Dense wide tables retain horizontal scrolling within named, keyboard-focusable containers, with accessible keyboard navigation and reachable actions.
- Improved current-section navigation, visible focus indicators, contrast, status announcements, and backup progress semantics. Backup dialogs now contain keyboard focus and restore focus when closed, while retaining no-JavaScript fallbacks and reduced-motion support.
- Expanded the terminal administration console with task-oriented browsing, contextual forms and dialogs, logs, history, telemetry, help and responsive operator views.
- Reorganized web administration into 22 task sections with searchable configuration controls and account management. Settings distinguish active, saved, environment-overridden and next-restart values; validated persistence retains operator comments and applies live settings only after successful saves.
- Standardized admin spacing, typography, controls, tables, state treatments and expandable details across all nine themes. Responsive forms work down to 320px; dense tables scroll within named keyboard-focusable regions, with reachable actions and visible focus.
- Improved current-section navigation, contrast, status announcements and backup progress semantics, while preserving native forms, no-JavaScript fallbacks and reduced-motion support.
- Refreshed the README, setup and container guides, demonstration screenshots, and operator documentation for the terminal/admin workflows and native update boundary.

### Fixed

- Rejected network and runtime configuration forms now retain the administrator's selected theme and submitted values.
- Admin health polling no longer starts requests from an unloading page: polling schedules after completion, cancels and aborts on navigation, ignores late updates, and resumes after back/forward-cache restoration. This fixes intermittent WebKit access-control diagnostics without increasing timeouts or enabling retries.
- Firefox launches automatically through the normal browser harness on macOS 27 despite Playwright's upstream shared-application-identity defect ([Playwright #42768](https://github.com/microsoft/playwright/issues/42768)). The isolated identity uses the installed browser binary, requires no manual shell setup, and is protected by launch regressions and a version guard.
- Browser fixture teardown now closes pages before stopping their server, preventing background polling from racing shutdown. Navigation assertions wait for the actual submission response and audits inspect the selected task's visible controls.
- Completed native updates clear the availability notice; stale discovery records cannot offer installation of the running or an older version. Rollbacks retain notice of a newer release.

- Banned identities can no longer submit reports or poll votes; those mutations share the existing posting ban gate and enforce CSRF before authorization results are disclosed. Long ban reasons wrap on narrow screens.
- NSFW consent and mobile preferences dialogs move and contain keyboard focus, support dismissal/reopening and restore focus to their triggers. Mobile preferences also restore visible trigger focus after returning to desktop.
- Rejected public posts retain submitted drafts and poll settings, explain attachment reselection and provide copyable recovery after parsed errors and bounded oversized uploads. Enhanced CAPTCHA refresh preserves the composer and files.
- Interrupted uploads and unconfirmed responses retain input and explain check-before-retry recovery. Pending uploads cannot fall through into duplicate native submissions, and server-rendered recovery text takes precedence over stale autosave.
- Local quotes and backlinks preserve browser history; dense previews keep their triggers reachable. Search links open complete thread context and retain modified-click behavior; poll result anchors resolve correctly.
- Filename sanitization preserves bounded extensions and Unicode stems. Failed audio/video playback provides readable recovery advice and an original-file download action; explicit scrolling honors reduced motion.
- Catalog “last reply” sorting includes sage replies independently of bump order, preserving normal bump sorting and saved preferences.
- Rejected network and runtime configuration forms retain the administrator's selected theme and submitted values without partially changing saved settings.
- Admin health polling aborts on navigation, ignores late responses, schedules after completion and resumes after back/forward-cache restoration, preventing unloading-page WebKit diagnostics.

### Testing and maintenance

- Restored version-controlled Playwright tests, configurations, npm manifest and lockfile, helper scripts, and browser CI. Browser downloads, profiles, runtime databases, caches, reports, screenshots, traces, videos, and disposable audit evidence remain ignored.
- Coordinated CLI integration-test subprocess spawning to prevent Linux fork/exec descriptor inheritance from making freshly copied executables fail with `ETXTBSY`, while keeping test execution parallel. Kept configuration documentation compatible with Rust 1.91's strict Clippy checks.
- Added deterministic polling lifecycle, Firefox launch and no-JavaScript, fixture disposal, and narrow-table accessibility regressions. Completed validation passed 2,430 Rust tests and the seven-project browser matrix (1,077 passed, 883 explicit skips, zero unexpected failures or retries); WebKit stress testing passed 120/120 with two workers.
- Restored version-controlled Playwright infrastructure and browser CI, with normal regression and weekly/manual deep modes. Runtime data, browser profiles, screenshots, reports, traces and disposable audit evidence remain ignored.
- Added administrator authorization, updater signing/archive/snapshot/rollback/recovery, readiness privacy, all-theme/narrow-screen, keyboard focus and no-JavaScript regressions. Release candidates receive strict Rust lint, locked dependency policy, packaging and browser validation.
- Stabilized fixture teardown, backup completion and completed-redirect submission assertions and session-revocation fixtures that retain and verify the old session without unrelated polling, preserving test budgets and all-theme coverage. Linux browser CI provides a virtual audio output for genuine native playback checks. Firefox uses an isolated application identity for the macOS 27 upstream Playwright launch defect, with launch regressions and a version guard.
- Coordinated CLI test subprocess spawning to prevent Linux fork/exec descriptor inheritance from causing `ETXTBSY`, while preserving parallel test execution. Guarded native updater helpers with Unix configuration so check-only Windows builds remain warning-free. Kept configuration documentation compatible with Rust 1.91 strict lint and refreshed the yanked `yoke-derive` lockfile entry.

## RustChan 1.5.0 — 2026-09-28

Expand Down
24 changes: 20 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 12 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rustchan"
version = "1.5.0"
version = "1.6.0"
edition = "2021"
# Keep the package MSRV and cargo-msrv metadata aligned with Arti's Rust 1.91
# requirement.
Expand Down Expand Up @@ -32,6 +32,10 @@ panic = "abort"
name = "rustchan-cli"
path = "src/main.rs"

[[bin]]
name = "rustchan-updater"
path = "src/bin/rustchan-updater.rs"

[lib]
name = "chan"
path = "src/lib.rs"
Expand Down Expand Up @@ -60,6 +64,13 @@ toml = "1.1.5"

argon2 = "0.5.3"
sha2 = "0.11.0"
# Signed update verification reuses the TLS provider and existing compression/semver crates.
ring = "0.17.14"
semver = "1.0.28"
flate2 = "1.1.9"
tar = { version = "0.4.46", default-features = false }
reqwest = { version = "0.13.4", default-features = false, features = ["rustls-no-provider", "blocking"] }

subtle = "2.6.1"
hex = "0.4.3"
# rand_core 0.10 updates RustChan's direct RNG trait dependency, but the
Expand Down Expand Up @@ -161,12 +172,6 @@ ratatui = { version = "0.30.2", default-features = false, features = ["crossterm
# Safe in-process filesystem-capacity queries for upload disk-space preflights.
rustix = { version = "1.1.4", features = ["fs"] }

[dev-dependencies]
# The CLI runtime integration test needs a TLS-capable HTTP client to drive the
# built binary over HTTPS. Production targets no longer link an HTTP client.
# default-features = false prevents the native-tls backend from being compiled
# in; rustls-no-provider keeps the test build on the pure-Rust TLS stack.
reqwest = { version = "0.13.4", default-features = false, features = ["rustls-no-provider"] }

[features]
default = ["tls-self-signed"]
Expand Down
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \

COPY --from=builder /out/rustchan-cli /usr/local/bin/rustchan-cli

ENV CHAN_HOST=0.0.0.0 \
ENV RUSTCHAN_CONTAINER=1 \
CHAN_HOST=0.0.0.0 \
CHAN_PORT=8080 \
CHAN_TOR_SUPPORT=false

Expand Down
Loading
Loading