Skip to content

feat(agent): implement package broker#1847

Merged
Vladyslav Nikonov (vnikonov-devolutions) merged 4 commits into
masterfrom
feat/unigetui-package-broker-integration
Jul 22, 2026
Merged

feat(agent): implement package broker#1847
Vladyslav Nikonov (vnikonov-devolutions) merged 4 commits into
masterfrom
feat/unigetui-package-broker-integration

Conversation

@vnikonov-devolutions

@vnikonov-devolutions Vladyslav Nikonov (vnikonov-devolutions) commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Implements now package broker (Also see UniGetUI WIP branch Devolutions/UniGetUI#4826)

Implemented

  • Policy parsing (both json and yaml), evaluation, JSON scheme generation.
  • Dry-run executor
  • Windows executor/command builder for winget & PowerShell; needs refining but implements most of the functionality
  • Elevated/non elevated execution with user impersonation.
  • Pipe auth - validate effective user & calling executable signature
  • Made some updated to initial PoC schema - cleaned up, renamed and restructured some fields, added new request/response for polling operation status

Not implemented yet

  • Other managers except winget and powershell

Steps to run this branch:

  • Compile and replace DevolutionsAgent.exe from this branch.
  • Add new agent config fields:
    • "PackageBroker":{"Enabled":true, "PolicyPath": "C:/path/to/policy.json"}
    • If testing with non-MSI UniGetUI isntallation, disable signature validation:
      "__debug__": { "skip_broker_signature_validation": true }
  • Create empty UseAgentBroker file at %LOCALAPPDATA%/UniGetUI/Configuration (UniGetUI uses file-based config)
  • Start Agent
  • Start UniGetUI, test

Do not merge yet, testing in progress

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

Let maintainers know that an action is required on their side

  • Add the label release-required Please cut a new release (Devolutions Gateway, Devolutions Agent, Jetsocat, PowerShell module) when you request a maintainer to cut a new release (Devolutions Gateway, Devolutions Agent, Jetsocat, PowerShell module)

  • Add the label release-blocker Follow-up is required before cutting a new release if a follow-up is required before cutting a new release

  • Add the label publish-required Please publish libraries (`Devolutions.Gateway.Utils`, OpenAPI clients, etc) when you request a maintainer to publish libraries (Devolutions.Gateway.Utils, OpenAPI clients, etc.)

  • Add the label publish-blocker Follow-up is required before publishing libraries if a follow-up is required before publishing libraries

@vnikonov-devolutions
Vladyslav Nikonov (vnikonov-devolutions) force-pushed the feat/unigetui-package-broker-integration branch from 2158cf9 to 83fa0b1 Compare July 14, 2026 14:40
@vnikonov-devolutions Vladyslav Nikonov (vnikonov-devolutions) changed the title [DRAFT] feat(agent): uniget ui broker implementation feat(agent): implement package broker Jul 14, 2026
@vnikonov-devolutions
Vladyslav Nikonov (vnikonov-devolutions) marked this pull request as ready for review July 14, 2026 14:44
Copilot AI review requested due to automatic review settings July 14, 2026 14:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements an early Windows package broker for Devolutions Agent.

Changes:

  • Adds policy loading, evaluation, live reload, and sample scenarios.
  • Adds authenticated named-pipe APIs, operation tracking, and status polling.
  • Adds WinGet/PowerShell command builders and elevated execution.

Reviewed changes

Copilot reviewed 47 out of 48 changed files in this pull request and generated 24 comments.

Show a summary per file
File Description
.gitattributes Adds asset and YAML attributes.
Cargo.lock Locks new broker dependencies.
devolutions-agent/Cargo.toml Adds broker dependencies and Windows features.
devolutions-agent/src/lib.rs Exposes Windows broker modules.
devolutions-agent/src/service.rs Starts the configured broker task.
devolutions-agent/src/config.rs Adds broker and debug configuration.
devolutions-agent/src/code_signing.rs Centralizes signature validation.
devolutions-agent/src/updater/package.rs Reuses signature helpers.
devolutions-agent/src/broker/mod.rs Defines broker modules.
devolutions-agent/src/broker/auth.rs Authenticates pipe clients.
devolutions-agent/src/broker/task.rs Coordinates broker subsystems.
devolutions-agent/src/broker/pipe.rs Implements named-pipe transport.
devolutions-agent/src/broker/policy_loader.rs Loads JSON/YAML policies.
devolutions-agent/src/broker/policy_watcher.rs Implements policy hot reload.
devolutions-agent/src/broker/operation_tracker.rs Tracks asynchronous operations.
devolutions-agent/src/broker/server/mod.rs Implements broker API behavior.
devolutions-agent/src/broker/server/connection.rs Serves HTTP pipe connections.
devolutions-agent/src/broker/server/execution.rs Runs background operations.
devolutions-agent/src/broker/server/responses.rs Builds API responses.
devolutions-agent/src/broker/executor/mod.rs Defines execution abstractions.
devolutions-agent/src/broker/executor/output.rs Handles output and exit codes.
devolutions-agent/src/broker/executor/windows/mod.rs Implements Windows execution plans.
devolutions-agent/src/broker/executor/windows/process.rs Creates user-token processes.
devolutions-agent/src/broker/executor/windows/token.rs Resolves sessions and tokens.
devolutions-agent/src/broker/command_builder/mod.rs Dispatches command construction.
devolutions-agent/src/broker/command_builder/winget.rs Builds WinGet commands.
devolutions-agent/src/broker/command_builder/powershell.rs Builds PowerShell commands.
devolutions-agent/src/broker/evaluator/mod.rs Implements policy precedence.
devolutions-agent/src/broker/evaluator/matching.rs Matches request attributes.
devolutions-agent/src/broker/evaluator/constraints.rs Enforces rule constraints.
devolutions-agent/src/broker/evaluator/version.rs Matches version ranges.
devolutions-agent/src/broker/evaluator/wildcard.rs Implements wildcard matching.
devolutions-agent/src/broker/evaluator/tests.rs Tests policy evaluation.
devolutions-agent/src/broker/scenario_tests.rs Runs sample scenarios.
devolutions-agent/src/broker/assets/samples/corporate-allowlist.policy.json Adds allowlist policy sample.
devolutions-agent/src/broker/assets/samples/deny-risky-options.policy.json Adds deny-list policy sample.
devolutions-agent/src/broker/assets/samples/scenarios/baseline.scenarios.json Defines baseline scenarios.
devolutions-agent/src/broker/assets/samples/requests/winget-vscode-install.request.json Adds allowed request sample.
devolutions-agent/src/broker/assets/samples/requests/winget-vscode-skiphash.request.json Adds risky request sample.
devolutions-agent/src/broker/assets/samples/requests/winget-unknown-install.request.json Adds denied request sample.
crates/devolutions-agent-shared/Cargo.toml Adds temporary-file dependencies.
crates/devolutions-agent-shared/src/lib.rs Exposes temporary-file utilities.
crates/devolutions-agent-shared/src/temp_file.rs Adds shared temporary-script handling.
devolutions-session/Cargo.toml Removes direct tempfile dependency.
devolutions-session/src/main.rs Removes obsolete dependency import.
devolutions-session/src/dvc/fs.rs Re-exports the shared guard.
devolutions-session/src/dvc/task.rs Uses shared script utilities.
devolutions-session/src/dvc/process.rs Converts I/O execution errors.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread devolutions-agent/src/broker/task.rs Outdated
Comment thread devolutions-agent/src/broker/task.rs Outdated
Comment thread devolutions-agent/src/broker/server/mod.rs
Comment thread devolutions-agent/src/broker/server/mod.rs Outdated
Comment thread devolutions-agent/src/broker/server/mod.rs
Comment thread devolutions-agent/src/broker/policy_watcher.rs
Comment thread devolutions-agent/src/broker/policy_watcher.rs
Comment thread devolutions-agent/src/broker/command_builder/powershell.rs
Comment thread devolutions-agent/src/broker/task.rs Outdated
Comment thread devolutions-agent/src/broker/command_builder/powershell.rs
@vnikonov-devolutions

Copy link
Copy Markdown
Contributor Author

Current improvements list (generated by Copilot):

Priority Workstream Missing / improvement
P0 Secure process execution Timeout currently terminates only the root process. Add Windows Job Object / process-tree cleanup so installers, PowerShell children, and nested processes cannot survive timeout.
P0 Secure temp scripts Broker scripts are no longer in user TEMP, but the helper still creates/keeps files by path and then applies ACL after creation. Build a Windows secure temp-script helper: broker-controlled directory, create with DACL up front, grant only target user read/execute, keep safe handle/sharing semantics until launch.
P0 WinGet trust model Current trusted WinGet resolution still accepts %LOCALAPPDATA%\Microsoft\WindowsApps\winget.exe. Validate Microsoft/AppInstaller identity or resolve via a safer AppX/app execution alias path, not just path prefix.
P0 Default policy discovery If default policy is absent, the watcher pins package-broker-policy.json; creating .yaml / .yml later will not resume. Default watcher should track all supported candidate extensions.
P0 Config schema/docs config_schema.json and user docs do not mention PackageBroker or SkipBrokerSignatureValidation. This blocks reliable deployment/configuration.
P0 PowerShell capability mismatch Capabilities advertise PowerShell custom parameters as supported, but command builder now rejects them. Either advertise supports_custom_parameters: false for PowerShell managers or implement safe structured parameter parsing.
P0 Effective user identity Unqualified effective_user is still accepted and can be ambiguous across domains/local accounts. Prefer requiring domain-qualified identity or, better, bind to SID.
P1 Source binding WinGet uses --source  only; policy can include source.url, but execution does not verify name-to-URL mapping. Add source URL/type verification or reject URL-bearing WinGet requests until supported.
P1 PowerShell command correctness Verify real PowerShellGet/PSResourceGet behavior for -Repository on uninstall and version flags on update through integration tests. Current builders are unit-tested syntactically only.
P1 Policy defaults PolicyConstraints::default() is permissive for risky options: custom params, pre/post commands, kill-before-operation, skip hash, interactive. Decide whether broker should enforce safer defaults or ship hardened policy templates.
P1 Version matching API says package versions are lenient/non-SemVer, but range matching now requires SemVer. Need manager-specific comparators or clear policy documentation that ranges only apply to SemVer-compatible versions.
P1 Package name matching package_names criteria currently fail closed because requests do not carry display names. Either implement package-name resolution or remove/diagnose unsupported criteria.
P1 Operation lifecycle Cancellation is only timeout-driven. Add explicit cancel endpoint/task control if clients need to stop long-running installs.
P1 Operation ownership/idempotency Owner key is built from request strings after validation. Store owner identity from authenticated/canonical pipe client data instead, and define whether retry fingerprints should include volatile fields like created_at / diagnostics flags.
P1 Shutdown cleanup Broker now uses CancellationToken, but only the pipe server task is joined. Join watcher, relay, and eviction tasks to guarantee clean service shutdown.
P1 Concurrency control No per-user/global operation limits or queueing. Add throttling to avoid parallel installers fighting over MSI/WinGet/PowerShell state.
P1 Output truncation marker Bounded reader drops old bytes but no longer tracks whether truncation occurred before tail_utf8. Add a real tail buffer with a truncated flag.
P1 Auditability Add structured audit logs for evaluate/execute/status: authenticated user, client exe thumbprint, policy id/revision, rule id, operation id, result. Keep command args redacted.
P2 Structured status details details is always None. Either keep unsupported, or add useful details: reboot required, known WinGet/MSI reason, package manager, policy rule, timeout cause.
P2 Health diagnostics Health only says ready/paused and policy id. Include last policy load error/revision/time for supportability.
P2 Client packaging story Server validates a signed client executable path, but this branch does not define/distribute the expected broker client binary or installation path contract.
P2 Integration tests Add Windows integration tests for named-pipe auth, status auth, signature-skip mode, real process launch, UTF-8 capture, timeout kill, policy reload, default missing policy recovery.
P2 Shared Windows helpers Move DACL/temp-script/process-tree helpers into shared/win wrapper crates once stable; current broker-local Win32 ACL code duplicates updater patterns.
P2 Performance cleanup Wildcard matching compiles regexes on every evaluation. Cache compiled patterns or use a glob matcher if policies grow.

Suggested parallel split:

Track Can own independently
Execution/security Job-object timeout, secure temp scripts, WinGet trust, SID/domain identity.
Manager semantics WinGet source verification, PowerShell parameter/capability fixes, real manager integration tests.
Policy engine Default policy multi-extension watching, version comparators, package-name support, safer constraints.
API/lifecycle Operation cancellation, ownership/idempotency cleanup, concurrency limits, structured status.
Ops/docs Config schema, docs, sample policies, audit/health improvements.
Test harness Windows end-to-end broker tests and fixtures that other tracks can reuse.

@CBenoit

Benoît Cortier (CBenoit) commented Jul 15, 2026

Copy link
Copy Markdown
Member

Given the diff size, I’ll honestly not be able to review as well as I wish I could. For the sake of merging quickly, do you think you could flag me some areas that you think are worth spending extra time reviewing on my end? Can you double check that nothing that is stable today is affected by this? The idea is that it’s okay to merge a POC in a somewhat unfinished state for improved velocity, so it’s easier to iterate together with follow up PRs against master, but it should not impact negatively any other stable component. Anything that is affecting other stable modules should be extracted in a dedicated PR so I can perform the usual hardened review. Otherwise, I’ll just conduct a minimal review and approve possibly with follow up suggestions. How does that sound?

@vnikonov-devolutions

Copy link
Copy Markdown
Contributor Author

do you think you could flag me some areas that you think are worth spending extra time reviewing on my end

I think mostly windows executor need some review for critical security issues, and the code from devolutions-agent/updater which was moved to shared code but other than that, I think everything stable is untouched

@CBenoit Benoît Cortier (CBenoit) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, I double checked the overall architecture + the security sensitive code (to the best of my knowledge + using a AI-based rubber duck), and here are my findings.

Extra low/hardening issues found by Claude, in no specific order:

Temp script DACL is applied after creation (executor/windows/mod.rs:406-475). The file is created with the inherited ACL, content written, then SetNamedSecurityInfoW tightens it. In SYSTEM's C:\Windows\Temp this is mostly safe (new files there aren't Users-readable during the window, and tempfile uses CREATE_NEW random names so no pre-creation hijack), but creating the file atomically with the target SDDL (SECURITY_ATTRIBUTES at creation) removes the window entirely and is cleaner. Also worth an integration test confirming a non-admin user token can actually read the script from the broker's temp dir in real SYSTEM mode.

winget batch escaping (executor/windows/mod.rs:552-593) correctly follows CommandLineToArgvW + %-doubling + /V:OFF conventions, but the cmd.exe-layer handling of embedded " in a batch line is subtle and only unit-tested at the string level. An execution-level test (actually invoking through cmd) would harden this, especially for allow-rules that permit custom parameters.

skip_broker_signature_validation (config.rs, auth.rs:80-84) fully disables client auth. It's debug-scoped and logged at warn! (good) — just ensure it can never be set in shipped configs.

connection setup is unauthenticated work (pipe.rs:40-56): every connection does OpenProcess/exe_path/lookup_account before any signature gate; a connection flood is cheap-ish DoS. Low impact; note only.

The new code is gated and does not impact existing stable features. As discussed on Slack, I think at this point we should merge, and iterate from there 🙂

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: This is very close to tempfile::TempPath:

A path to a named temporary file without an open file handle... When dropped, the temporary file is deleted.

Instead of keep() + storing a PathBuf + a hand-rolled Drop, we could hold the TempPath from NamedTempFile::into_temp_path() and get deletion-on-drop for free. If we want to keep logging cleanup failures, we can retain a small Drop that calls TempPath::close() and logs the error.

Not a big win either way. Happy to leave as-is if you prefer the explicit version.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (non-blocking, but worth deciding now): Placement of the TmpFileGuard + the script preamble constants are generic "write a temp script and clean it up" helpers. Nothing here is agent-specific. Landing them in devolutions-agent-shared reads as convenience placement (it's just the common dep) rather than a domain fit.

We already favor small focused utility crates (http-client-proxy, win-api-wrappers, secure-memory, …), a tiny dedicated crate (e.g. tmp-script-file or tmp-file) where we wrap tempfile with higher level functionalities feels like a better home. Maybe change that in a follow-up.

notify = { version = "7", default-features = false, features = ["macos_kqueue"] }
now-policy = "0.1"
now-policy-api = { version = "0.1", features = ["policy-compat"] }
now-policy-server-template = { version = "0.1", features = ["policy-compat"] }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: I suppose we’ll remove the policy-compat feature in the feature? What is the purpose of enabling it today, we never had such a feature until now?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is feature for gating conversion code between now-policy & now-policy-api types as now-policy-api deliberately was designed in such way, that direct use of now-policy-api crate is not required unless explicitly requested via policy-compat feature.

Comment on lines +90 to +97
fn recognizes_devolutions_certificate_thumbprints() {
for thumbprint in DEVOLUTIONS_CERT_THUMBPRINTS {
let mut thumbprint_bytes = [0u8; 20];
hex::decode_to_slice(thumbprint, &mut thumbprint_bytes)
.expect("test thumbprint should be valid hexadecimal");
assert!(is_devolutions_certificate_thumbprint(&thumbprint_bytes));
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: Exhaustive testing for a small N (= number of test cases), is a good practice.

#[cfg(windows)]
pub mod broker;
#[cfg(windows)]
pub(crate) mod code_signing;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Extract the package broker into its own crate.

The broker is shaping up nicely, but it's landing as a large, self-contained subsystem inside the devolutions-agent crate. I suggest pulling it out into a dedicated crate (e.g. crates/dwa-package-broker or crates/now-package-broker; to decide) as part of this work, before it grows further. This aligns with the repo's existing philosophy of splitting concerns into focused sub-crates.

rationale:

  • Size and cohesion: the broker already spans ~15 modules with a clear boundary: auth, command_builder/{winget,powershell}, evaluator/*, executor/*, etc. It's a typical leaf-crate candidate.
  • Dependency surface: This PR adds a large, broker-only dependency set directly to the devolutions-agent binary: axum, hyper, hyper-util, tower-service, notify, now-policy, now-policy-api, now-policy-server-template, serde_yaml, semver, regex, chrono, tokio-util. Moving these behind a crate keeps the agent binary's manifest lean.
  • Feature-gating: A separate crate also gives a natural place to hang a Cargo feature so the whole subsystem and its dep tree can be compiled out entirely when not needed (typically during development, when working on a separate module).

On the shared code_signing module: as part of this migration, I think we should also extract code_signing (currently moved into devolutions-agent/src/code_signing.rs) so it can be shared between the updater (updater/package.rs, which historically owned DEVOLUTIONS_CERT_THUMBPRINTS) and the broker (broker/auth.rs, which now consumes validate_devolutions_authenticode_signature). Right now the broker reaches back into the agent crate for it via use crate::code_signing::..., which won't work once the broker is its own crate.

I’ll be happy to help with the mechanical move. Not blocking, good follow up.

};

expected_domain.eq_ignore_ascii_case(&actual.domain) && expected_name.eq_ignore_ascii_case(&actual.name)
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: We are matching identity by by display name instead of SID.

This compares DOMAIN\name strings, and when the request's effective_user is unqualified ("alice") it ignores the domain entirely. find_user_session() likewise matches by username only when unqualified. On a host with both MACHINE\alice and DOMAIN\alice, the authenticated caller and the session actually launched into can differ, and the broker can run the operation in the wrong user's session. The token-side domain fix is only helping partially.

suggestion: Capture the caller's SID at connect (we already have read sid_and_attributes()), then thread that SID through ExecutionContext, and both authenticate and select the WTS session by SID rather than by name string.

Comment on lines +166 to +168
fn canonicalize_for_comparison(path: &Path) -> anyhow::Result<PathBuf> {
Ok(std::fs::canonicalize(path)?)
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: Arguably non necessary helper function on top of canonicalize.

Comment on lines +170 to +174
fn same_windows_path(left: &Path, right: &Path) -> bool {
left.as_os_str()
.to_string_lossy()
.eq_ignore_ascii_case(&right.as_os_str().to_string_lossy())
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: to_string_lossy() looking wrong to me as a security-relevant identity check.

suggestion: Use GetFileInformationByHandleEx with FILE_ID_INFO via win-api-wrappers / windows, and compare the file identity directly. This is immune to all the edge cases I can think of. (Unicode casing, invalid UTF-8, etc)

/// The main package-manager command line as separate arguments (exe + args).
pub command: Vec<String>,
/// Optional shell command to run after the main command (`cmd.exe /S /C`).
pub post_command: Option<String>,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: pre_operation_command/post_operation_command are raw strings written to a .bat and run via cmd.exe with the possibly elevated token.

Claude’s analysis:

The only gate is the boolean allow_pre_post_commands, and constraints are permissive when absent (let Some(c) = constraints else { return true }). So any allow-rule that omits constraints, or sets that one flag, yields arbitrary elevated code execution regardless of the package allowlist, the packages allowlist becomes moot. The command content is never restricted or previewed to the policy.

Recommendation: at minimum document this loudly and make it opt-in per rule with a content allowlist; consider defaulting constraints to deny-all when a rule has none, so constraints: None isn't a silent "allow every risky option."

Comment on lines +29 to +50
/// Load a policy document from a file path.
///
/// The file format is detected from the extension:
/// - `.json` — parsed as JSON
/// - `.yaml` or `.yml` — parsed as YAML
///
/// Deserialization performs all validation (structure, types, length constraints, patterns).
pub fn load_policy(path: &Path) -> anyhow::Result<PolicyDocument> {
let content = std::fs::read_to_string(path)
.map_err(|e| anyhow::anyhow!("failed to read policy file at {}: {e}", path.display()))?;

let policy = deserialize_policy(&content, path)?;

info!(
policy_id = %policy.metadata.id,
revision = policy.metadata.revision,
rules_count = policy.rules.len(),
"Loaded policy"
);

Ok(policy)
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Policy file integrity is trusted without an ownership/ACL check.

Claude’s analysis:

The policy is the entire authorization control, but it's read from %PROGRAMDATA%\Devolutions\Agent\ (or a configured path) with no check that the file/directory is writable only by Administrators/SYSTEM. C:\ProgramData subtrees are a known spot for over-permissive inherited ACEs; if a standard user can write the policy, they self-authorize arbitrary elevated installs.

Recommendation: before trusting a loaded policy, verify the file's owner and DACL restrict write to SYSTEM/Administrators (fail-closed to Paused otherwise). Defense-in-depth even if the installer sets ACLs correctly.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@vnikonov-devolutions
Vladyslav Nikonov (vnikonov-devolutions) force-pushed the feat/unigetui-package-broker-integration branch from 87d48ab to 26094bd Compare July 22, 2026 12:50
@vnikonov-devolutions
Vladyslav Nikonov (vnikonov-devolutions) merged commit 66c8377 into master Jul 22, 2026
42 checks passed
@vnikonov-devolutions
Vladyslav Nikonov (vnikonov-devolutions) deleted the feat/unigetui-package-broker-integration branch July 22, 2026 13:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants