RavensPort

Building

dotnet restore RavensPort.slnx --locked-mode
dotnet build RavensPort.slnx --no-restore

Since 5.0.0 the UI is Avalonia rather than WPF, and every project targets two frameworks: net10.0, the portable build that Linux runs, and net10.0-windows10.0.19041.0, the Windows build with Windows Hello and the themed tray menu. Restore in locked mode: the packages.lock.json files record both frameworks, and a restore that rewrites them fails CI with NU1004.

The 1Password desktop-app integration also needs the native library built from src/OnePasswordNative with Go and cgo (onepassword.dll on Windows, libonepassword.so on Linux). clean-build.bat and packaging/build-deb.sh build it for you.

Tests#

dotnet test tests/RavensPort.Core.Tests/RavensPort.Core.Tests.csproj
dotnet test tests/RavensPort.UI.Tests/RavensPort.UI.Tests.csproj

RavensPort.Core.Tests is over a thousand tests (more on the Windows framework, which also covers the Windows-only code), covering the OAuth and storage layers, the full HTTP method × credential placement matrix against a real upstream, multi-credential routes (several headers, header + body together, the same credential in two slots at once, and routes attaching nothing), static API keys (forwarded in every permitted placement, mixed with an OAuth token on one request, and a key with a line break refused before it reaches the wire), credential testing against a real endpoint that checks what it was sent, the refusal of query-string credentials and of a proxy key sent in a URL, the wipe of the pre-2.0 store, and end-to-end funnel behaviour — including that two funnels over one upstream stay isolated, run in parallel, and never cross-deliver a response.

RavensPort.UI.Tests walks the approval suite's ground through the actual Avalonia views, on both frameworks. It starts the app in single use, so it needs no password manager and no secret and is safe to run anywhere, including on a fork's pull request. It cannot cover mTLS, which restarts the app and so ends a single-use session; that stays the approval suite's job.

Coverage#

./coverage.ps1 -SkipSystemTests -Open

Runs the unit suite with coverage collection, merges the result, and opens an HTML report under TestResults/coverage-report/. SonarCloud currently reports around 84% of lines.

The figure worth quoting is the union of both suites, because they reach the same assembly from opposite ends — RavensPort.Core.Tests through an InMemoryVault, RavensPort.SystemTests through a real 1Password account. Drop -SkipSystemTests to get it, with the system suite's environment set up first:

$env:OP_SERVICE_ACCOUNT_TOKEN  = '<token for a throwaway account>'
$env:RAVENSPORT_SYSTEM_TEST_ACK = 'i-understand-this-erases-the-ravensport-vault'
./coverage.ps1

Read tests/RavensPort.SystemTests/README.md before doing that — the system suite erases every RavensPort item in the vault the token reaches. Without both variables the script skips it and says so, rather than reporting half a figure as the whole one.

RavensPort.Core, RavensPort.UI and the app itself are measured, now that the UI suite drives the real views. Generated XAML code, the tray, dialogs and window code-behind are left out, because no suite can reach them; the reasoning is written out in coverlet.runsettings, which both runs share so the two halves can legitimately be merged.

In CI, .github/workflows/coverage.yml does the same thing across two workflows: it runs the unit suite itself, waits for system-approval.yml to finish on the same commit, merges both, and publishes the percentage as a job summary and to SonarCloud. The summary always says which halves went into the number — a pull request from a fork gets no vault credentials, so there the system half is honestly absent rather than quietly counted as zero.

Analysis needs a SONAR_TOKEN repository secret; without it the coverage still runs and publishes, only the upload is skipped. SONAR_ORGANIZATION is a repository variable, defaulting to the organisation implied by the project key.

Publishing a standalone exe#

dotnet publish src/RavensPort.App/RavensPort.App.csproj -p:PublishProfile=win-x64-selfcontained -p:TargetFramework=net10.0-windows10.0.19041.0 -c Release

The project targets two frameworks, so publish has to be told which one: without -p:TargetFramework it stops with NETSDK1129. A publish profile cannot answer that for it.

Produces a self-contained RavensPort.exe (~180 MB, runtime bundled) under src/RavensPort.App/bin/Release/net10.0-windows/publish/win-x64/. See THIRD-PARTY-NOTICES.md before redistributing — it bundles components whose licenses require their notices travel along.

Building the Store variant#

dotnet build RavensPort.slnx -p:StoreBuild=true

Defines STORE_BUILD, which drops Proton Pass and mTLS — see BuildProfile and docs/STORE-MSIX.md. It must be a command-line property, not a publish-profile one: profile properties do not cross a ProjectReference, so setting it there would build RavensPort.App with the features removed and RavensPort.Core with them still in. The packaging scripts check the built product name and refuse the wrong build in either direction.

Every other command on this page builds the full app, unchanged.

Project layout#

src/RavensPort.Core/            OAuth flows, password-manager storage, YARP proxy config, MCP funnel,
                                API to MCP bridges, activity log — no UI dependency, just the engine
src/RavensPort.UI/              view models, with no reference to any UI framework
src/RavensPort.App/             Avalonia tray app: hosts Kestrel + YARP in-process, tray icon, views
src/OnePasswordNative/          Go wrapper over the 1Password SDK, built as a native library
templates/api-mcp/              manifest starting points, and the format contract for writing one
tools/RavensPort.ManifestCheck/ command-line manifest validator — the same rules the app applies
tests/RavensPort.Core.Tests/    xunit tests for Core — InMemoryVault, no side effects
tests/RavensPort.UI.Tests/      the real views, driven in single use
tests/RavensPort.SystemTests/   the approval suite: one real 1Password vault, end to end
packaging/                      winget manifests, the MSIX and .deb build scripts
installer/                      the Inno Setup installer

RavensPort.App owns the process. It starts the Kestrel/YARP host on a thread-pool task rather than the UI thread, avoiding a sync-over-async deadlock, then initializes the tray icon. The proxy and the UI share one DI container. RavensPort.UI exists so the view models cannot depend on a UI framework: that is what made the port from WPF to Avalonia possible, and it keeps them testable.

Releases#

Pushing a version tag (v*) runs the test suite and, only on success, builds and publishes a release with a provenance attestation. Nothing is released off a failing build.

A release carries the installer (RavensPort-Setup-<version>.exe), the MSIX package, and the amd64 and arm64 Linux packages. The tag also opens the winget manifest update, and deb.yml publishes the packages to the apt repository. The bare self-contained exe used to ship beside the installer, but running it installed nothing (no Start menu entry, no way back in after the tray menu's Exit), which is what Microsoft Store certification rejected. Building from source still produces that exe if you want it.


Edit this page on the wiki Request support · Report vulnerabilities privately