Software

OS package updates with a dry-run and live log, repositories and holds, PHP versions and extensions, language runtimes with EOL tracking and pinned global tools.

In preview software version 0.1.0

Actions

39 actions, callable from the panel, the command palette and the API as POST /api/v1/a/<id>. Internal actions used between modules are not listed.

ActionWhat it doesRiskPreview
software.os.list List OS packages, updates, holds and reboot state (read) low No dry run
software.os.refresh Refresh the package index low No dry run
software.os.plan Dry-run a package change (read) low No dry run
software.os.apply Apply a package change high
software.os.hold Hold packages at their current version medium
software.os.unhold Release package holds medium
software.os.allow.get Show the package allow-list (read) low No dry run
software.os.allow Edit the package allow-list high
software.repo.list List package repositories (read) low No dry run
software.repo.preview Show a repository signing key and its fingerprint (read) low No dry run
software.repo.add Add a third-party repository critical No dry run
software.repo.toggle Enable or disable a repository high No dry run
software.repo.remove Remove a third-party repository critical No dry run
software.updates.get Show the update policy (read) low No dry run
software.updates.configure Set the update policy high No dry run
software.inventory.export Export the package and PHP inventory (read) low No dry run
software.php.list PHP versions on a node (read) low No dry run
software.php.install Install a PHP version high
software.php.remove Remove a PHP version high
software.php.ext.list PHP extensions of a version, per scope (read) low No dry run
software.php.ext.set Enable or disable a PHP extension for a scope medium
software.php.ext.install Install or remove a PHP extension package high No dry run
software.php.ext.pecl Build a PECL extension from the allow-list high No dry run
software.php.default.get Default PHP version (read) low No dry run
software.php.default.set Set the default PHP version medium
software.php.ini.get PHP settings form of an account or site (read) low No dry run
software.php.ini.set Change PHP settings of an account or site medium
software.php.status PHP-FPM and opcache status (read) low No dry run
software.reconcile Re-apply the saved extension switches low No dry run
software.runtime.list Runtimes, versions and EOL state (read) low No dry run
software.runtime.install Install a runtime version (shared store) medium
software.runtime.remove Remove a runtime version medium
software.runtime.detect Suggest versions from an app's files (read) low No dry run
software.runtime.select Select a version for an app or the account default low
software.runtime.policy.set Offered runtimes, versions and on-demand cap medium
software.runtime.usage Who uses which runtime (EOL report) (read) low No dry run
software.runtime.request.resolve Close a version request low No dry run
software.tool.pin Pin a global tool version medium
software.tool.list Managed tools and pins (read) low No dry run

Permissions and limits

Permissions

  • software.os.view View packages and repositories
  • software.os.manage Apply package changes, holds and update policy
  • software.os.admin Edit the package allow-list
  • software.repo.manage Add and change package repositories
  • software.php.view View PHP versions and settings
  • software.php.manage Change PHP extensions, version and settings of own sites
  • software.php.admin Install PHP versions and extensions server-wide
  • software.runtime.view See runtimes and their EOL state
  • software.runtime.use Choose runtime versions for own apps and account
  • software.runtime.manage Install or remove runtime versions in the shared store
  • software.runtime.admin Set which runtimes and versions are offered
  • software.tool.manage Pin global tool versions

Engineering notes

Generated from modules/software/docs.md at build d90e9e2. These are the notes the engineers keep next to the code: precise, technical, and honest about what is not done yet.

Spec: docs/specs/software.md AC-software-01..17. Card: docs/tasks/w9/w9-14-software-os-php.md. Runtimes and tools: card w9-18, section below. Panel extensions are the built-in extensions.* module (w9-16).

OS packages

  • software.os.list (installed / upgradable / security / held / managed, search, reboot-required, lock state, index age), software.os.refresh (job) and an hourly software.scheduled.refresh that caches the counts per node and emits software.updates.available / software.reboot.required / software.php.eol_soon.
  • Allow-list. Install / upgrade need the package to be on the allow-list: the panel-managed set (every module's AllowPackages in the agent) plus the administrator's additions (software.os.allow, stored in m_software.allow_list and sent with every request). Removal is further limited: a package owned by another module (nginx, mariadb, postfix ...) or essential to the OS is never removable here, directly or as a dependency of something else (the simulation is checked), and the refusal names the module that needs it. No API field carries raw apt/dnf arguments: names must match ^[a-z0-9][a-z0-9+.-]*[a-z0-9]$ (no option or name-/name+ suffix lookalikes) and every command line is built by the agent.
  • Plan / apply. software.os.plan simulates with the package manager itself (apt-get -s, dnf --assumeno) and returns steps (install/upgrade/remove with versions, dependency flag, installed size), download and disk deltas, services affected (active units owned by upgraded or removed packages), refused reasons and a hash over the steps. software.os.apply (long: true, risk high => step-up) re-plans, compares plan_hash when given, takes the node queue, waits (up to 2 min, "package manager busy" in the job log, then busy) while another process holds the dpkg/dnf lock, runs the groups (upgrade, install, remove) streaming the output, then calls security.updates.health_watch and records history + software.updates.applied. dry_run returns a module.Plan; refusals are errors (precondition_failed). upgrade_class: security|all upgrades every upgradable package of that class (held ones are skipped and listed).
  • Holds. software.os.hold / unhold (apt-mark / dnf versionlock). A held package cannot be named in an upgrade.
  • Update policy lives in security: software.updates.get/configure only call security.updates.status/configure.
  • Repositories. software.repo.list (os / vendor / third_party, enabled, signed, key fingerprints), software.repo.preview (downloads a key over https and returns the fingerprint of every primary key, changes nothing), software.repo.add (risk critical => step-up; needs the fingerprint the administrator confirmed; the agent downloads the key again, requires that fingerprint, keeps ONLY that key in the repository's keyring, writes a deb822 source with Signed-By (apt) or a .repo with gpgcheck=1 (dnf), runs apt-get update/dnf makecache and removes everything again when that fails). toggle works on rc-* repositories only (#rc-off# for .list files, Enabled: no for .sources), remove on repositories added here only. OS repositories are shown read-only.

PHP

  • Versions come from the version catalog (EOL date and label included): software.php.list. install adds the PHP repository when needed (Debian family) and installs the standard extension set; remove is refused while sites use the version unless migrate_to names an installed version (sites are moved through web.sites.update first). After install/remove/default the module calls web.server.ensure with the installed set, because web only offers versions it was told about; the server default is web's default_php for new sites.
  • Extensions per scope. software.php.ext.list (installed/available, description, size, dependencies, conflicts, warnings, server state on/off/absent, account state inherit/on/off and the effective state). software.php.ext.set scope server (admin; the extension's ini linked in or out of the version's FPM conf.d) or account/site. PHP only loads extensions from the ini scan directories of the master process, never from a pool section, and web runs one master per account and version. So an account (site) switch is a private scan directory plus a systemd drop-in rc-fpm-<ver>-<user>.service.d/50-rc-software-ext.conf (PHP_INI_SCAN_DIR) and a restart of THAT master only (measured 0.2-0.5 s; other accounts' masters are not touched). The new directory is checked first (the extension must load, php-fpm -t must accept the pool) and everything is rolled back on failure. A site-scope switch is therefore an account-wide switch for that PHP version (the response says scope: account).
  • software.php.ext.install installs/removes an extension package (admin scope; disabled: true leaves it off server-wide).
  • software.php.ext.pecl: vetted list apcu, igbinary, imagick, memcached, mongodb, redis, swoole, xdebug. The agent installs the build tools and php<ver>-dev, finds the stable release on pecl.php.net, downloads over https, unpacks with a path-checked extractor into a temp directory owned by nobody, and runs phpize / configure / make as nobody with no-new-privs, an empty environment and no network (unshare -n). Only the finished module is copied (by root) to /var/lib/respirecloud/software/pecl/<php>/<ext>-<release>/<ext>.so, proven to load, and only then is its ini written. Failure or cancel removes the temp directory and changes nothing. It starts off server-wide. RHEL family: not built; the Remi package of the same extension is installed instead (method: package).
  • Conflicts / warnings (AC-14) come back in the extension list and in the ext.set dry-run plan (xdebug with pcov, swoole, the opcache JIT; "slows every request").
  • php.ini forms. software.php.ini.get/set for a site or an account: 22 known keys with type, minimum and upper bound (memory_limit and upload sizes 2G, execution time 3600 s ...), custom directives only as name/value pairs from the same list, upload_max_filesize <= post_max_size. Bad values are rejected before anything is called; the change itself goes through web.layers.set (web validates again, renders, runs php-fpm -t and reverts on failure).
  • software.php.default.set scope site/account uses web.sites.update (web's own php-fpm -t + rollback); scope server stores the default and syncs web's default_php.
  • software.php.status: master state, workers, memory, restarts, overrides, error-log path, configured opcache settings.

Runtimes and global tools (w9-18, AC-software-18..22)

  • Handlers are thin (cp/runtimes.go); logic is runtimes/ (catalog lines, EOL, detection, Decide, Select, Report), work is the agent ops in sdk/agentop/software.go (software.mise.*, software.tool.pin, software.runtime.default|detect).
  • Catalog. core.versions.list through Host.Call (retried as the platform when the actor lacks core.nodes.view, embedded catalog as last resort), cached 5 min.
  • State (m_software, migration 0002): runtime_policy (offered lines, cap, on-demand; no row = every non-EOL line, cap 4, on-demand on), runtime_defaults (per account), tool_pins, runtime_requests.
  • runtime.select: app scope with no version takes what the app's version files say; Decide then installs on demand (streamed job) below the cap, else records a request (status: requested, event software.runtime.requested) and an administrator's runtime.install of that line closes it. App scope calls apps.app.update {runtime, version}; account scope rewrites /opt/respirecloud/path.d/<user>.sh.
  • runtime.remove is refused while an app or account default uses the version, and also when the apps cannot be listed.
  • runtime.usage lists apps + defaults with EOL state (EOL first); the daily software.scheduled.runtime_eol emits software.runtime.eol_soon once per line and state.
  • Cron units of the apps module carry PATH=/opt/respirecloud/tools/bin:... (apps.go, delimited w9-18 block).

Not done / limits

See docs/tasks/w9/w9-14-software-os-php.handover.md.