Skip to content

Detect, validate and select managed or system dependencies #8494

Open

Description

@vitormattos

Context

LibreSign already has setup checks for Java, JSignPdf and PDFtk, and these checks already report useful information such as the configured path and detected version.

The missing part is that the current checks start from a path already stored in LibreSign and assume that the dependency is a LibreSign-managed artifact.

For example:

  • JavaSetupCheck reads the configured Java path, verifies LibreSign-managed integrity, then runs java -version;
  • JSignPdfSetupCheck reads jsignpdf_path, verifies managed integrity, then checks the JSignPdf version;
  • PDFtkSetupCheck reads pdftk_path, verifies managed integrity, then currently executes it as a JAR through Java.

This means we already have part of the version/capability validation, but we do not yet have a clean way to discover compatible system dependencies or distinguish them from LibreSign-managed files.

Goal

Provide one backend dependency model that can answer, for the current LibreSign configuration:

  • which dependencies are required;
  • whether a compatible dependency is available from the operating system;
  • which version/path was detected;
  • whether LibreSign should use the managed or system-provided dependency;
  • whether the selected dependency is valid;
  • whether LibreSign needs to download anything.

The frontend should receive this information ready to render. It must not probe the operating system itself.

Separate discovery from selection

System discovery and the dependency selected for use are different things.

For example, the server may have a compatible Java at /usr/bin/java, while LibreSign is still configured to use its managed Java.

The backend should therefore keep these concepts separate:

Detected system dependency

Read-only information discovered from the current runtime environment:

  • found / not found;
  • detected path;
  • detected version;
  • compatibility status;
  • reason when incompatible.

Selected dependency source

The administrator's choice:

  • managed;
  • system.

Do not automatically switch an existing installation to a system dependency only because one is discovered.

Dependency inventory

Introduce a small backend service responsible for producing a dependency inventory/plan for the current configuration.

The exact class names are implementation details, but conceptually the result should contain information like:

{
  "java": {
    "required": true,
    "selectedSource": "managed",
    "managed": {
      "configured": true,
      "path": "...",
      "version": "...",
      "status": "valid"
    },
    "system": {
      "found": true,
      "path": "/usr/bin/java",
      "version": "...",
      "status": "compatible"
    }
  }
}

The frontend issue uses this contract to decide what choices/actions to present.

Determine which dependencies are required

The required dependency set depends on the selected engines.

Keep this decision in one backend place.

The current code already contains parts of these rules in AdminController::installAndValidate() and the individual installer methods. Consolidate them so setup checks, installation and the frontend all use the same dependency plan.

Do not duplicate the dependency matrix in Vue.

System discovery

Discovery should be read-only and safe.

For executable dependencies, prefer a dedicated executable finder instead of shelling out to user-controlled commands. Symfony Process is already available and provides Symfony\Component\Process\ExecutableFinder.

Examples that may be discovered this way include Java and executable forms of PDFtk.

After finding a candidate, run the dependency-specific version/capability probe.

Do not build shell commands from user input.

JSignPdf

JSignPdf is different from a normal executable dependency because LibreSign currently works with its directory/JAR layout.

Do not assume that every dependency can be discovered with the same algorithm.

Each dependency should have its own detector/inspector behind a common contract.

If JSignPdf cannot be reliably discovered from the system environment, the backend may report it as not automatically detected and still allow an explicitly configured system path.

Compatibility is not the same as managed artifact version

The current managed setup is tied to the exact artifacts LibreSign downloads and verifies.

System-provided dependencies need an explicit compatibility policy.

Do not automatically require an exact managed-artifact build/version unless that exact version is technically required.

For each dependency, centralize the compatibility rule so discovery, setup validation and runtime configuration use the same decision.

The result should distinguish at least:

  • compatible;
  • incompatible version;
  • not found;
  • cannot execute/read;
  • unknown/invalid response.

Important: PDFtk has two execution forms

The current managed PDFtk path points to a JAR and PDFtkSetupCheck executes:

java -jar <pdftk_path> --version

A system installation may instead expose an executable such as:

/usr/bin/pdftk

So simply replacing pdftk_path with /usr/bin/pdftk is not sufficient if the rest of LibreSign still assumes the path is a JAR.

The dependency abstraction must know how the selected provider is invoked.

For PDFtk this may mean distinguishing:

  • managed JAR executed through Java;
  • system executable invoked directly.

Keep this difference behind the dependency/provider contract so signing code does not spread managed vs system conditionals.

Persisted configuration

Once an administrator selects a system dependency, LibreSign can continue to store the effective path in the existing configuration keys where that is compatible with current consumers:

  • java_path;
  • jsignpdf_path;
  • pdftk_path.

But the path alone is not enough to describe trust/source.

Persist an explicit source for each dependency as well.

This allows LibreSign to know whether it should:

  • apply managed-artifact integrity verification;
  • use system compatibility validation;
  • offer download/reinstall;
  • preserve the configured external path.

Existing installations with no source configured must keep the current managed behavior.

When discovery runs

Do not probe the operating system continuously and do not add discovery to signing/runtime hot paths.

Discovery is needed when configuration information can actually change the setup UI:

  • when the admin settings/setup page is loaded;
  • after an engine selection changes the required dependency plan;
  • when the administrator explicitly validates/refreshes the setup;
  • after installing or changing a dependency source/path.

The current settings page already runs setup checks on load and after engine changes. Refactor this flow so dependency discovery and validation are performed once for the dependency inventory and reused by the setup response, instead of running duplicate probes.

At minimum, memoize results during one request/inventory build.

Only add a longer-lived cache if profiling shows that the probes are expensive enough to justify it. If a cache is added, it must be short-lived or explicitly invalidated when dependency configuration changes so OS package updates are not hidden for a long time.

Reuse setup-check logic

Do not create a second unrelated version-check implementation.

Extract/reuse the useful parts of the current setup checks so the same dependency-specific inspector can validate:

  • a detected system candidate;
  • a configured system dependency;
  • a configured managed dependency where applicable.

Managed integrity verification remains a separate concern and applies only to LibreSign-managed artifacts.

The setup checks can then render the result of this shared inspection instead of owning all probing logic themselves.

Installation behavior

Installation should consume the same dependency plan/inventory.

For each required dependency:

Source = managed

  • if a valid managed artifact already exists, keep it;
  • otherwise allow LibreSign to download/install it;
  • use the managed integrity verification.

Source = system

  • never download or overwrite it;
  • validate the selected system candidate/path;
  • use it only when compatible.

After the operation, return an updated inventory/setup result to the frontend.

CLI behavior

The CLI should use the same backend contracts.

It should be possible to inspect the detected/selected dependencies without triggering installation.

The existing install command should not silently replace a selected system dependency with a managed one.

Exact CLI UX can be decided during implementation, but discovery/inspection must not exist only for the web UI.

Tests

Prefer small dependency-specific detector/inspector tests plus tests for the aggregate inventory.

Cover at least:

Discovery

  • executable dependency found in PATH;
  • dependency not found;
  • command exists but cannot execute;
  • version can be detected;
  • detected version is compatible;
  • detected version is incompatible;
  • JSignPdf explicit-path fallback when automatic discovery is not available.

Selection

  • detected system dependency does not automatically become selected;
  • default source for existing installations remains managed;
  • selecting system persists both source and effective path;
  • switching back to managed restores managed behavior.

PDFtk

  • managed JAR is invoked through Java;
  • system executable is invoked directly;
  • both produce a normalized version/capability result.

Plan/inventory

  • engine configuration changes the required dependency set;
  • only required dependencies are included as required;
  • inventory exposes managed and detected system status;
  • repeated consumers in the same request reuse the same inspection result.

Installation

  • only missing required managed dependencies are downloaded;
  • selected system dependencies are never downloaded or overwritten;
  • managed integrity verification is not applied to system files;
  • incompatible system dependency prevents setup from becoming valid.

Out of scope

  • frontend rendering/configuration;
  • automatic package installation through apt/dnf/apk/etc.;
  • changing OS permissions or SELinux policy;
  • FreeBSD support itself;
  • background polling of operating-system dependencies.

Done when

  • One backend dependency plan determines what is required by the selected engines.
  • System dependencies can be discovered/inspected without changing the current configuration.
  • Discovery returns path, version and compatibility information.
  • Managed/system selection is stored explicitly.
  • Existing path configuration is reused where appropriate without using path alone as the source/trust marker.
  • Dependency-specific compatibility logic is shared by discovery and setup validation.
  • PDFtk supports the difference between managed JAR and system executable invocation.
  • Discovery runs only in setup/configuration flows, not signing hot paths.
  • The frontend can get all information required to render the dependency setup from one backend contract.
  • Existing installations remain managed by default.
  • Tests cover discovery, compatibility, selection and managed/system execution differences.

Activity

  1. changed the title [-]Add explicit managed/system dependency model and validation[/-] [+]Detect, validate and select managed or system dependencies[/+] on Sep 21, 2026
  2. added theissue type on Sep 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    backendBackend workphpWork involving PHP code

    Type

    Fields

    Priority

    None yet

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions