From 5a662b61660edf3386f3b2b6be8d42f9059fd224 Mon Sep 17 00:00:00 2001 From: viruslox <12681439+viruslox@users.noreply.github.com> Date: Fri, 21 Aug 2026 11:51:26 +0000 Subject: [PATCH] chore: update documentation and installation scripts - Updated README.md and ARCHITECTURE.md with missing configurations (`FRAMEFLOW_ROLE`, `peers.yaml`). - Added Apache Reverse Proxy setup instructions in README.md. - Modified `internal/sysutils/install.go` to explicitly copy `peers.yaml.template` and `frontend.settings.template` for both Client and Server roles. --- ARCHITECTURE.md | 10 +++++++++- README.md | 35 ++++++++++++++++++++++++----------- internal/sysutils/install.go | 31 ++++++++++++++++++++----------- 3 files changed, 53 insertions(+), 23 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index caec7dc..f733396 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -77,9 +77,17 @@ To secure the connection between the remote `vlx_frontend` and the SBC's `VLX_Fr 2. The Client provisions a signed `.p12` or `.pem` Client Certificate for authorized remote UI instances. 3. The core API uses `tls.RequireAndVerifyClientCert`, refusing any connection not signed by the local CA. +### Multi-Client Deterministic Slots and Configuration Roles + +The suite supports multi-client MLVPN tunneling utilizing a deterministic slot model configured via `/opt/VLX_FrameFlow/etc/peers.yaml` on the server. +- Each client is assigned an integer slot in `peers.yaml`. This slot deterministically derives the tunnel interface (`mlvpn{slot}`), UDP port (`5080+slot`), and the inner tunnel IP subnet (`10.1.{10+slot}.x`). +- To enforce routing, peer names defined in `peers.yaml` must be lowercase and DNS-label safe. + +The daemon resolves architecture nuances (e.g. AMD64 vs ARM64) automatically. Furthermore, the role configuration (`FRAMEFLOW_ROLE` in settings, empty for Client, `SERVER` for Server) dynamically instructs the daemon on which privilege scoping and systemd management mechanisms (e.g., system vs user) should be utilized. + ### "Build as User, Run as Root" -The suite enforces a strict dichotomy between Client and Server roles regarding execution privilege and systemd architecture: +The suite enforces a strict dichotomy between Client and Server roles regarding execution privilege and systemd architecture (guided by the `FRAMEFLOW_ROLE` variable): 1. **Build:** Unprivileged compilation via `build.sh`. 2. **Install:** Executing the compiled binary as root triggers `internal/sysutils.InstallBinary()`. diff --git a/README.md b/README.md index 14ccb67..33c083e 100644 --- a/README.md +++ b/README.md @@ -214,20 +214,20 @@ This file contains **all runtime environment variables**. It is generated during ### Apache Reverse Proxy -To serve the Control Panel Frontend behind an Apache Reverse Proxy (for example, under the `/vlx/` path), ensure that the `proxy`, `proxy_http`, and `proxy_wstunnel` modules are enabled. Use the following configuration block: +To serve the Control Panel Frontend behind an Apache Reverse Proxy (for example, under the `/frameflow/` path), ensure that the `proxy`, `proxy_http`, and `proxy_wstunnel` modules are enabled. Use the following configuration block: ```apache - - ProxyPass http://127.0.0.1:8080/ - ProxyPassReverse http://127.0.0.1:8080/ - - - - ProxyPass ws://127.0.0.1:8080/ws - ProxyPassReverse ws://127.0.0.1:8080/ws - +# ===== FrameFlow peer (frontend : — telemetry WS at /ws) ===== +RedirectMatch ^/frameflow$ /frameflow/ + +ProxyPass /frameflow/ws ws://127.0.0.1:/ws +ProxyPass /frameflow/ http://127.0.0.1:/ +ProxyPassReverse /frameflow/ http://127.0.0.1:/ ``` -Note: Adjust the port (`8080` by default) to match your `vlx_frontend` bind port. + +And very important: frameflow peers GUI are at address: `http(s)://:/frameflow/peer/` + +Note: Adjust the `` (`8080` by default) to match your `vlx_frontend` bind port. ### General Paths | Variable | Description | @@ -239,6 +239,7 @@ Note: Adjust the port (`8080` by default) to match your `vlx_frontend` bind port | Variable | Description | | :--- | :--- | +| `FRAMEFLOW_ROLE` | Specifies role-aware privilege scoping (`SERVER` or empty for client). | | `FRAMEFLOW_USER` | The dedicated, unprivileged user that runs the background services (default: `frameflow`). | | `MLVPN_SERVER_IP` | The remote server IP for MLVPN (UDP traffic). | | `MLVPN_SLOT` | MLVPN tunnel identity (client). Must match this client's peer slot on the server. | @@ -269,6 +270,18 @@ Note: Adjust the port (`8080` by default) to match your `vlx_frontend` bind port **Note on Server Configuration:** The Server role configuration (`frameflow_srv.settings.template`) intentionally omits these IP variables. The Server is a destination node that securely binds to `0.0.0.0` and `::` locally, and therefore only requires the corresponding authentication keys (`MLVPN_KEY`, `MPTCP_PROXY_PASS`). +### `/opt/VLX_FrameFlow/etc/peers.yaml` (Server Only) + +This file is used to configure multi-client MLVPN tunneling using a deterministic slot model. Each client is assigned an integer slot that dictates its tunnel interface (`mlvpn{slot}`), UDP port (`5080+slot`), and tunnel IP subnet (`10.1.{10+slot}.x`). Peer names must be lowercase and DNS-label safe. + +```yaml +peers: + # Example peer + - name: client01 + slot: 1 + key: "YOUR-SECRET-KEY-1" +``` + ### Streaming Endpoints Both endpoints undergo **Strict URL Validation** before they are actively bound to the ingest pipeline. A malformed URL configuration (e.g., missing scheme) will proactively prevent the FFmpeg streaming unit from attempting to start, and instead will yield a direct descriptive error in the log. diff --git a/internal/sysutils/install.go b/internal/sysutils/install.go index 4811bf4..765dac5 100644 --- a/internal/sysutils/install.go +++ b/internal/sysutils/install.go @@ -219,17 +219,15 @@ func InstallBinary(isServer bool) error { } } - if !isServer { - // Copy frontend template user settings if it exists - frontendTemplate := filepath.Join(searchPath, "frontend.settings.template") - frontendTarget := filepath.Join(installConfigDir, "frontend.settings") - if _, err := os.Stat(frontendTemplate); err == nil { - if _, err := os.Stat(frontendTarget); os.IsNotExist(err) { - copyFile(frontendTemplate, frontendTarget, 0644) - Info("Copied frontend.settings.template to etc") - } else { - mergeConfigs(frontendTemplate, frontendTarget) - } + // Copy frontend template user settings if it exists + frontendTemplate := filepath.Join(searchPath, "frontend.settings.template") + frontendTarget := filepath.Join(installConfigDir, "frontend.settings") + if _, err := os.Stat(frontendTemplate); err == nil { + if _, err := os.Stat(frontendTarget); os.IsNotExist(err) { + copyFile(frontendTemplate, frontendTarget, 0644) + Info("Copied frontend.settings.template to etc") + } else { + mergeConfigs(frontendTemplate, frontendTarget) } } @@ -249,6 +247,17 @@ func InstallBinary(isServer bool) error { mergeConfigs(mtxConfig, mtxTarget) } } + + // Copy peers.yaml.template to etc/peers.yaml if it exists + peersTemplate := filepath.Join(searchPath, "peers.yaml.template") + peersTarget := filepath.Join(installConfigDir, "peers.yaml") + if _, err := os.Stat(peersTemplate); err == nil { + if _, err := os.Stat(peersTarget); os.IsNotExist(err) { + copyFile(peersTemplate, peersTarget, 0644) + Info("Copied peers.yaml.template to etc") + } + } + break } }