Skip to content
Open
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
139 changes: 139 additions & 0 deletions .github/workflows/openwrt.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
name: Test OpenWrt package

on:
pull_request:
paths:
- "public/**"
- "packaging/openwrt/**"
- "scripts/prepare-openwrt-feed.sh"
- "scripts/ci/openwrt-qemu-smoke.sh"
- "tests/**"
- "package.json"
- ".github/workflows/openwrt.yml"
push:
branches: ["main"]
paths:
- "public/**"
- "packaging/openwrt/**"
- "scripts/prepare-openwrt-feed.sh"
- "scripts/ci/openwrt-qemu-smoke.sh"
- "tests/**"
- "package.json"
- ".github/workflows/openwrt.yml"
schedule:
- cron: "23 4 * * 1"
workflow_dispatch:

permissions:
contents: read

concurrency:
group: openwrt-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22

- name: Install validation tools
run: |
sudo apt-get update
sudo apt-get install --yes jq shellcheck

- name: Run unit and contract tests
run: npm test

- name: Validate shell files
run: |
sh -n scripts/prepare-openwrt-feed.sh
sh -n scripts/ci/openwrt-qemu-smoke.sh
sh -n packaging/openwrt/package/files/etc/uci-defaults/90-firewall-visualiser
sh -n packaging/openwrt/package/files/usr/sbin/firewall-visualiser-setup
shellcheck -s sh scripts/prepare-openwrt-feed.sh
shellcheck -s sh scripts/ci/openwrt-qemu-smoke.sh
shellcheck -s sh packaging/openwrt/package/files/etc/uci-defaults/90-firewall-visualiser
shellcheck -s sh packaging/openwrt/package/files/usr/sbin/firewall-visualiser-setup

- name: Validate ACL JSON
run: jq --exit-status . packaging/openwrt/package/files/usr/share/rpcd/acl.d/firewall-visualiser.json >/dev/null

- name: Stage OpenWrt feed
run: ./scripts/prepare-openwrt-feed.sh

sdk-build:
needs: validate
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
sdk:
- x86_64-25.12.4
- x86_64-24.10.7

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Stage OpenWrt feed
run: ./scripts/prepare-openwrt-feed.sh

- name: Create artifact directory
run: mkdir -p artifacts

- name: Build with official OpenWrt SDK container
uses: openwrt/gh-action-sdk@797d0e3d0eb13b355c3447f60d0179d4b43089e2
env:
ARCH: ${{ matrix.sdk }}
FEED_DIR: ${{ github.workspace }}/.build/openwrt-feed
FEEDNAME: firewall_visualiser
PACKAGES: openwrt-firewall-visualiser
ARTIFACTS_DIR: ${{ github.workspace }}/artifacts
BUILD_LOG: 1
V: s

- name: Upload packages and build logs
uses: actions/upload-artifact@v4
with:
name: openwrt-firewall-visualiser-${{ matrix.sdk }}
if-no-files-found: error
path: artifacts/

qemu-smoke:
if: github.event_name == 'workflow_dispatch' || github.event_name == 'schedule'
needs: sdk-build
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Download x86_64 IPK artifact
uses: actions/download-artifact@v4
with:
pattern: openwrt-firewall-visualiser-x86_64-24.10.7
path: runtime-artifacts
merge-multiple: true

- name: Install smoke-test tools
run: |
sudo apt-get update
sudo apt-get install --yes curl jq openssh-client qemu-system-x86

- name: Run OpenWrt QEMU smoke test
run: ./scripts/ci/openwrt-qemu-smoke.sh runtime-artifacts

- name: Upload runtime diagnostics
if: always()
uses: actions/upload-artifact@v4
with:
name: openwrt-firewall-visualiser-qemu-diagnostics
if-no-files-found: warn
path: runtime-artifacts/qemu-diagnostics/
1 change: 1 addition & 0 deletions .github/workflows/static.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ jobs:
run: |
sh -n scripts/openwrt_export_hosts.sh
sh -n scripts/openwrt_export_subnet_mappings.sh
sh -n scripts/prepare-openwrt-feed.sh

# Single deploy job since we're just deploying
deploy:
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.build/

74 changes: 46 additions & 28 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -696,10 +696,19 @@ Implemented details:

## Phase N: Installable OpenWrt Edition

Status: planned.
Status: implemented; SDK and QEMU release verification is pending the first successful GitHub Actions run.

Objective: add an OpenWrt-native, installable edition without replacing or forking the existing static site under `public/`.

Implemented details:

- `public/` remains the canonical frontend and defaults to offline mode.
- `platform.js` and `openwrt-api.js` provide authenticated, read-only live mode without persisting credentials or session tokens.
- `packaging/openwrt/package/` contains the package definition, rpcd ucode service, ACL, UCI defaults, and listener setup command.
- `scripts/prepare-openwrt-feed.sh` creates a reproducible package feed under `.build/` and overlays installed-mode runtime configuration.
- `tests/openwrt/` covers the API contract, ACL, package layout, runtime modes, setup command, and fixed backend provider surface.
- `.github/workflows/openwrt.yml` validates and builds both APK and IPK targets, with a scheduled/manual x86_64 QEMU runtime smoke test.

The installable edition must remain an additive distribution target:

- `public/` remains the canonical frontend and continues to work as a local file, on GitHub Pages, or on any static web server.
Expand Down Expand Up @@ -750,7 +759,7 @@ Responsibilities:

A separate Node.js, Python, PHP-FPM, or database service is not required.

### Planned Repository Layout
### Repository Layout

```text
openwrt-firewall-visualiser/
Expand Down Expand Up @@ -781,8 +790,8 @@ openwrt-firewall-visualiser/
| | | | `-- firewall-visualiser.json
| | | `-- ucode/
| | | `-- firewall-visualiser.uc
| | `-- overlay/
| | `-- runtime-config.js # Installed-mode override
| |-- overlay/
| | `-- runtime-config.js # Installed-mode override
| `-- README.md # Package development notes
|-- scripts/
| |-- prepare-openwrt-feed.sh # Creates a generated SDK feed
Expand All @@ -795,10 +804,10 @@ openwrt-firewall-visualiser/
| |-- package-layout.test.js
| |-- platform.test.js
| |-- rpc-contract.test.js
| |-- setup-script.test.js
| |-- ucode-contract.test.js
| `-- fixtures/
| |-- snapshot.json
| |-- firewall.json
| `-- devices.json
| `-- snapshot.json
`-- .github/workflows/
|-- static.yml
`-- openwrt.yml # Package and optional runtime tests
Expand All @@ -808,7 +817,7 @@ Generated package payloads must be written under `.build/` and excluded from Git

### Package Staging Model

`prepare-openwrt-feed.sh` should build a disposable local feed such as:
`prepare-openwrt-feed.sh` builds a disposable local feed such as:

```text
.build/openwrt-feed/
Expand All @@ -822,7 +831,7 @@ Generated package payloads must be written under `.build/` and excluded from Git
`-- assets/...
```

The staging script should:
The staging script:

1. Remove the previous generated feed directory.
2. Copy `packaging/openwrt/package/Makefile` and the package-owned backend/configuration files.
Expand All @@ -835,15 +844,15 @@ This avoids fragile package Makefile references outside the feed and guarantees

### Package Definition

The first package should be named:
The package is named:

```text
openwrt-firewall-visualiser
```

It contains architecture-independent HTML, CSS, JavaScript, JSON, shell, and ucode files, so the package should use `PKGARCH:=all` unless compiled components are introduced later.

Expected dependencies:
Package dependencies:

```makefile
DEPENDS:= \
Expand Down Expand Up @@ -1116,7 +1125,7 @@ Security-focused assertions should include:

### OpenWrt SDK Build Workflow

Add `.github/workflows/openwrt.yml` after the package directory and staging script exist.
`.github/workflows/openwrt.yml` implements the package validation and build gate.

The mandatory CI gate should:

Expand All @@ -1128,7 +1137,7 @@ The mandatory CI gate should:
6. Upload package artefacts and build logs.
7. Test at least one current APK-based release and one maintained IPK-based release while both are supported.

Proposed workflow:
Implemented workflow outline:

```yaml
name: Test OpenWrt package
Expand Down Expand Up @@ -1165,10 +1174,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v7
uses: actions/setup-node@v4
with:
node-version: 22

Expand Down Expand Up @@ -1204,13 +1213,12 @@ jobs:
fail-fast: false
matrix:
sdk:
- x86_64-25.12.5
- x86_64-25.12.4
- x86_64-24.10.7
- mips_24kc-25.12.5

steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v4

- name: Stage OpenWrt feed
run: ./scripts/prepare-openwrt-feed.sh
Expand All @@ -1219,18 +1227,18 @@ jobs:
run: mkdir -p artifacts

- name: Build with OpenWrt SDK
uses: openwrt/gh-action-sdk@v11
uses: openwrt/gh-action-sdk@797d0e3d0eb13b355c3447f60d0179d4b43089e2
env:
ARCH: ${{ matrix.sdk }}
FEED_DIR: ${{ github.workspace }}/.build/openwrt-feed
FEEDNAME: firewall-visualiser
FEEDNAME: firewall_visualiser
PACKAGES: openwrt-firewall-visualiser
ARTIFACTS_DIR: ${{ github.workspace }}/artifacts
BUILD_LOG: 1
V: s

- name: Upload packages and logs
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: openwrt-firewall-visualiser-${{ matrix.sdk }}
if-no-files-found: error
Expand All @@ -1244,16 +1252,16 @@ jobs:

The release numbers in the matrix are intentionally pinned. They must be reviewed when OpenWrt support status changes. Third-party and official actions should be pinned to immutable commit SHAs in the implemented workflow where repository policy requires stronger supply-chain controls.

### Optional OpenWrt Runtime Smoke Test
### OpenWrt Runtime Smoke Test

An SDK build proves that the package description and dependencies are valid, but it does not prove that rpcd registers the object or that uHTTPd can serve the installed application.

Add an x86_64 QEMU smoke test once the package builds reliably. It may initially run on `workflow_dispatch` and a weekly schedule before becoming a pull-request gate.
The x86_64 QEMU smoke test initially runs on `workflow_dispatch` and a weekly schedule. It can become a pull-request gate after it has proved stable.

`scripts/ci/openwrt-qemu-smoke.sh` should:
`scripts/ci/openwrt-qemu-smoke.sh`:

1. Download a pinned official OpenWrt x86_64 image and its checksum file.
2. Verify the image checksum before booting it.
1. Download a pinned official OpenWrt x86_64 image.
2. Verify it against the release checksum pinned beside the image URL before booting it.
3. Start QEMU with an isolated user-mode network and forwarded SSH/HTTPS ports.
4. Wait for SSH with a fixed timeout.
5. Install the built package and dependencies using the package manager provided by that release.
Expand Down Expand Up @@ -1285,39 +1293,49 @@ The QEMU job should use the x86_64 package artefact from the SDK matrix and shou

#### Phase N1: Package Skeleton

Status: implemented.

- Add package staging, Makefile, file layout, and deterministic manifest.
- Install the unmodified static application under `/www/openwrt-firewall-visualiser/`.
- Add SDK build CI for supported OpenWrt releases.
- Keep runtime mode offline.

#### Phase N2: Read-Only rpcd API

Status: implemented.

- Add `firewall.visualiser` with `capabilities`, `firewall`, `devices`, `interfaces`, and `health`.
- Add the read-only ACL.
- Add contract fixtures and backend parser tests.
- Do not expose counters, conntrack, or write operations.

#### Phase N3: Live Frontend Adapter

Status: implemented.

- Add authenticated OpenWrt mode and manual login/session handling.
- Add snapshot loading, bounded polling, error states, and offline fallback.
- Update the graph incrementally rather than replacing the Cytoscape instance for every refresh.

#### Phase N4: Dedicated Listener Setup

Status: implemented.

- Add the explicit setup command for management address and port.
- Add idempotence and wildcard-address refusal tests.
- Document the management-zone input rule without applying it automatically.

#### Phase N5: Runtime CI

Status: implemented as a scheduled/manual gate; promotion to pull-request gating remains pending runtime stability.

- Add the pinned x86_64 QEMU smoke test.
- Verify installation, authentication, RPC registration, static serving, package removal, and resource use.
- Promote the smoke job from scheduled/manual to a required pull-request check after it is stable.

### Phase N Acceptance Criteria

The phase is complete when:
Implementation is complete. Release readiness additionally requires the SDK matrix and QEMU smoke job to pass in GitHub Actions:

- The GitHub Pages/static distribution behaves as it did before the package work.
- `public/` is the only maintained frontend source tree.
Expand Down Expand Up @@ -1381,7 +1399,7 @@ Use this workflow after changes:
4. Add, edit, and remove a device mapping.
5. Test a specific source-to-destination path.
6. Try each graph layout and filter.
7. Upload a local firewall config and verify that no data leaves the browser except the Cytoscape CDN request.
7. Upload a local firewall config and verify that no data leaves the browser.

## Last Updated

Expand Down
Loading
Loading