Skip to content
Closed
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ cargo build --release

See the [full build guide](https://opennow.zortos.me/guides/getting-started/) for platform-specific requirements.

For the new rewrite bootstrap, see [`opennow-rewrite/README.md`](opennow-rewrite/README.md).

---

## Documentation
Expand All @@ -164,6 +166,7 @@ Full documentation available at **[opennow.zortos.me](https://opennow.zortos.me)
- [Architecture Overview](https://opennow.zortos.me/architecture/overview/)
- [Configuration Reference](https://opennow.zortos.me/reference/configuration/)
- [WebRTC Protocol](https://opennow.zortos.me/reference/webrtc/)
- [Cross-Platform Rebuild Strategy](docs/cross-platform-roadmap.md)

---

Expand Down
132 changes: 132 additions & 0 deletions docs/cross-platform-roadmap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Cross-Platform Rebuild Roadmap (Windows + macOS + Linux + Raspberry Pi)

This roadmap is for a **full rebuild** focused on one goal:

> Ship one lightweight app architecture that runs everywhere with minimal per-platform patching.

## Reality check (important)

There is no desktop stack with **zero** platform-specific work forever. GPU drivers, input APIs, packaging/signing, and media backends differ by OS.

What we *can* do is design a stack where platform-specific code is tiny, isolated, and rarely changed.

## Recommended full-rewrite stack

If you are willing to fully rewrite and want broad compatibility + Raspberry Pi support, use:

- **Language:** C++20
- **UI + Windowing:** Qt 6 (Qt Quick/QML)
- **Rendering/Input (stream surface):** SDL2 (or native Qt scene integration)
- **Media pipeline:** GStreamer (single pipeline model across Linux/macOS/Windows)
- **Networking/WebRTC:** libdatachannel or Google WebRTC native
- **Build system:** CMake + Conan/vcpkg
- **Packaging:**
- Windows: MSI (WiX)
- macOS: signed `.app` + DMG
- Linux desktop: AppImage + Flatpak
- Raspberry Pi: `.deb` + AppImage fallback

### Why this stack

- Qt is one of the most proven desktop cross-platform toolkits.
- C++ keeps runtime overhead lower than Electron/Chromium-based stacks.
- GStreamer already has strong Linux/Pi support and solid Windows/macOS paths.
- You can keep one UX and one media architecture instead of shell-swapping.

## Alternatives (and why not first choice here)

| Option | Why teams choose it | Why it is weaker for your stated goal |
|---|---|---|
| Flutter desktop | Great UI velocity, strong cross-platform UI consistency | Heavier runtime than a tuned C++ app; Pi support is workable but less ideal for video-heavy low-latency workloads |
| Electron | Fastest development and plugin ecosystem | Too heavy for Raspberry Pi + cloud-streaming decode scenarios |
| Go desktop rewrite | Simple language, good tooling | Desktop UI ecosystem is less mature for a long-lived media-heavy app |
| Keep Rust + new shell | Reuses existing work | You explicitly said you are okay with full rewrite and want to avoid ongoing platform-fix complexity from current direction |

## Architecture to minimize platform-specific fixes

Use a strict layered design:

1. **Core domain layer (100% portable C++)**
- Session state machine
- Auth/session token orchestration
- Input abstraction model
- Settings/config model

2. **Media layer (mostly portable)**
- Unified GStreamer graph builder
- Codec selection strategy
- Adaptive bitrate and latency controls

3. **Platform adapters (small boundary only)**
- GPU decode capability probe
- Window handle plumbing
- Gamepad/raw input adapter
- Installer/update hooks

4. **UI layer (Qt/QML)**
- No direct platform calls
- Talks only to core interfaces

Hard rule: platform code must stay in `platform/<os>/` and never leak into domain/UI layers.

## Raspberry Pi requirements (first-class target)

- Target Raspberry Pi 4/5 (64-bit OS only).
- Prefer H.264 decode path first; AV1 optional later.
- Use low-power defaults:
- 720p / 60 FPS preset
- conservative bitrate auto mode
- reduced UI effects
- Add a Pi-specific “Performance Mode” profile.
- Validate on both X11 and Wayland sessions.

## Delivery plan (rewrite)

### Phase 0 — Foundation (2-3 weeks)
- Create new repo structure and CI matrix.
- Bring up Qt app shell and settings persistence.
- Set up package builds for all target OSes.

### Phase 1 — Streaming MVP (4-6 weeks)
- Implement login/session flow.
- Build basic video/audio stream + input forwarding.
- Deliver playable session on Windows/macOS/Linux.

### Phase 2 — Raspberry Pi stabilization (3-4 weeks)
- Tune decode paths and defaults for Pi.
- Add thermal/performance telemetry panel.
- Publish Pi build artifacts and install docs.

### Phase 3 — Parity + hardening (4-8 weeks)
- Add gamepad/racing wheel mapping.
- Add crash reporting + startup diagnostics export.
- Freeze API boundaries and optimize startup/memory.

## CI and quality gates (must-have)

Run for every PR:

- Build: Windows, macOS, Ubuntu, Raspberry Pi (arm64 cross or native runner)
- Smoke test: login screen launch, session start, input event, audio output, clean shutdown
- Packaging check: install/uninstall
- Performance budget checks:
- startup time
- memory ceiling
- dropped-frame threshold

Reject merges that fail any target platform gate.

## Maintenance model

To keep long-term maintenance easy:

- One owner per layer (core/media/platform/ui).
- Monthly dependency update window.
- Platform bug triage labels (`win`, `mac`, `linux`, `pi`).
- Never merge emergency OS hacks without adding a cross-platform abstraction follow-up task.

## Final recommendation

Given your goals (all major desktop OSes + Raspberry Pi + lightweight + fewer platform-specific surprises), a **C++ + Qt + GStreamer** full rewrite is the most practical fit.

It is more upfront work, but it gives the best chance of stable long-term cross-platform behavior with low runtime overhead.
45 changes: 45 additions & 0 deletions opennow-rewrite/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
cmake_minimum_required(VERSION 3.20)
project(OpenNOWRewrite VERSION 0.1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

option(OPENNOW_REWRITE_BUILD_DESKTOP "Build desktop bootstrap app" ON)
option(OPENNOW_REWRITE_BUILD_TESTS "Build rewrite tests" ON)

add_library(opennow_core
src/core/app.cpp
src/auth/login.cpp
)

target_include_directories(opennow_core
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
)

if(OPENNOW_REWRITE_BUILD_DESKTOP)
add_executable(opennow_desktop_bootstrap
apps/desktop/main.cpp
)

target_link_libraries(opennow_desktop_bootstrap
PRIVATE
opennow_core
)
endif()

if(OPENNOW_REWRITE_BUILD_TESTS)
enable_testing()

add_executable(opennow_login_smoke
tests/login_smoke.cpp
)

target_link_libraries(opennow_login_smoke
PRIVATE
opennow_core
)

add_test(NAME opennow_login_smoke COMMAND opennow_login_smoke)
endif()
40 changes: 40 additions & 0 deletions opennow-rewrite/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# OpenNOW Rewrite (Bootstrap)

This folder contains the new rewrite scaffold requested for a cross-platform, lightweight architecture.

## Current status

- ✅ Separate rewrite project folder created
- ✅ CMake + C++20 bootstrap target compiles
- ✅ Core library boundary started (`opennow_core`)
- ✅ Login base scaffolding started (provider model, PKCE bootstrap, auth URL builder)
- ✅ Desktop bootstrap app target started (`opennow_desktop_bootstrap`)
- 🚧 Qt 6 integration (next)
- 🚧 GStreamer/WebRTC integration (next)

## Build

```bash
cd opennow-rewrite
cmake -S . -B build
cmake --build build
ctest --test-dir build --output-on-failure
./build/opennow_desktop_bootstrap
```

## Initial structure

- `include/opennow/core/` - public core interfaces
- `src/core/` - core implementation
- `apps/desktop/` - desktop executable entrypoint
- `platform/<os>/` - platform adapter implementation boundaries
- `docs/` - rewrite architecture/planning docs

## Next milestones

1. Add Qt 6 app shell target and startup window.
2. Add OAuth callback listener abstraction in platform layer (current version is synchronous socket-based).
3. Add token exchange + refresh (`/token`) and secure persistence.
4. Add media abstraction interfaces (`IMediaPipeline`, `ISessionClient`).
5. Add first Linux adapter and capability probe.
6. Add CI pipeline for Linux/macOS/Windows + arm64 runner.
11 changes: 11 additions & 0 deletions opennow-rewrite/apps/desktop/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <iostream>

#include "opennow/core/app.hpp"

int main() {
opennow::core::App app;
std::cout << app.banner() << '\n';
std::cout << app.login_bootstrap_preview() << '\n';
std::cout << "Next step: wire Qt6 shell + GStreamer pipeline." << '\n';
return 0;
}
23 changes: 23 additions & 0 deletions opennow-rewrite/docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Rewrite Architecture (Initial)

The rewrite is split into strict layers to keep platform-specific code isolated.

## Layers

1. `core/` (portable)
- business logic
- session state machine
- configuration and feature flags
2. `media/` (mostly portable)
- GStreamer/WebRTC abstractions
3. `platform/<os>/` (OS-specific)
- input hooks
- decode/backend probing
- packaging hooks
4. `apps/desktop/` (UI shell)
- app startup
- shell window and settings UX

## Rule

Only `platform/<os>/` may contain direct OS API calls.
26 changes: 26 additions & 0 deletions opennow-rewrite/docs/LOGIN_REFERENCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Login Reference Notes (from current Rust implementation)

This file tracks what has been mirrored from `opennow-streamer/src/auth/mod.rs` into the rewrite bootstrap.

## Mirrored into rewrite base

- `LoginProvider` model + NVIDIA default provider
- PKCE generation with SHA-256 + base64url challenge (`make_pkce_challenge`)
- Callback port probing by binding localhost against known redirect ports
- OAuth authorize URL construction for `login.nvidia.com/authorize`
- Authorization-code extraction from callback query (`code=`), including URL decoding

## Intentionally simplified for bootstrap

- callback listener currently uses synchronous sockets in core auth service
- move this into a platform/network adapter for cleaner separation
- no token exchange endpoint client yet (`/token`)
- no userinfo decode/fetch yet

## Next auth milestones

1. Add HTTP client abstraction and implement `/token` exchange.
2. Add refresh-token flow and expiry handling.
3. Add secure token persistence abstraction.
4. Add JWT decode for `sub`, `preferred_username`, `email`, `gfn_tier`.
5. Add `/userinfo` fallback.
54 changes: 54 additions & 0 deletions opennow-rewrite/include/opennow/auth/login.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#pragma once

#include <cstdint>
#include <optional>
#include <string>

namespace opennow::auth {

struct LoginProvider {
std::string idp_id;
std::string login_provider_code;
std::string login_provider_display_name;
std::string login_provider;
std::string streaming_service_url;
int32_t login_provider_priority = 0;

[[nodiscard]] static LoginProvider nvidia_default();
[[nodiscard]] bool is_alliance_partner() const;
};

struct AuthTokens {
std::string access_token;
std::optional<std::string> refresh_token;
std::optional<std::string> id_token;
int64_t expires_at = 0;
};

struct PkceChallenge {
std::string verifier;
std::string challenge;
};

class LoginService {
public:
[[nodiscard]] PkceChallenge make_pkce_challenge() const;
[[nodiscard]] std::string build_auth_url(
const PkceChallenge& pkce,
uint16_t callback_port,
const LoginProvider& provider
) const;

[[nodiscard]] std::optional<uint16_t> pick_callback_port() const;
[[nodiscard]] std::optional<std::string> extract_code_from_callback_target(
const std::string& callback_target
) const;

[[nodiscard]] std::optional<std::string> wait_for_callback_code(uint16_t callback_port) const;

private:
[[nodiscard]] std::string generate_nonce() const;
[[nodiscard]] std::string device_id() const;
};

} // namespace opennow::auth
13 changes: 13 additions & 0 deletions opennow-rewrite/include/opennow/core/app.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

#include <string>

namespace opennow::core {

class App {
public:
[[nodiscard]] std::string banner() const;
[[nodiscard]] std::string login_bootstrap_preview() const;
};

} // namespace opennow::core
8 changes: 8 additions & 0 deletions opennow-rewrite/platform/linux/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Linux Adapter

Platform-specific code for linux belongs here.

Planned responsibilities:
- GPU/backend capability detection
- OS-specific input bridge
- Packaging/install/update hooks
8 changes: 8 additions & 0 deletions opennow-rewrite/platform/macos/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Macos Adapter

Platform-specific code for macos belongs here.

Planned responsibilities:
- GPU/backend capability detection
- OS-specific input bridge
- Packaging/install/update hooks
8 changes: 8 additions & 0 deletions opennow-rewrite/platform/raspberrypi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Raspberrypi Adapter

Platform-specific code for raspberrypi belongs here.

Planned responsibilities:
- GPU/backend capability detection
- OS-specific input bridge
- Packaging/install/update hooks
Loading