Files
Hydra-OS/docs/updates.md
T

7.9 KiB

Signed Hydra runtime updates

The new ISO includes an independent updater and an independently pinned Ed25519 public key. Discovery uses the public Gitea repository at https://git.shanaty.ru/Emil_Shanaty/Hydra-OS; discovery is not a trust source. An empty releases list is normal and reports no update.

hydra update check
hydra update status
sudo hydra update apply
sudo hydra update rollback TRANSACTION
sudo hydra-update-recover

check and status run without sudo. Apply prints the transaction identifier. After applying or rolling back, log out of the graphical session and log in again to load the changed runtime. Existing guest disks and profiles remain unchanged. If the normal hydra command is broken, use the independent hydra-update command or hydra-update-recover directly. A prepared/interrupted transaction blocks the next apply until recovery succeeds. Recovery may be repeated after interruption.

Only the latest current transaction can be explicitly rolled back. Rollback first checks that its installed files still match their recorded content, ownership and permissions, and refuses local edits. Recovery restores the originals of an interrupted transaction without requiring mixed runtime files to match the new release. Corrupt or missing backups stop restoration before any destination changes; keep the complete transaction directory for inspection. No automatic cleanup removes failed transaction evidence.

Runtime updates contain only Python runtime modules, usr/lib/hydra/bin/hydra, etc/motd, the Fastfetch configuration and its text logo. They cannot contain update*.py, the independent updater, release keys, /home, guest profiles/disks, credentials, network configuration, services, Portage, the kernel or boot files. Runtime rollback does not roll back Gentoo or its kernel. Whole-system recovery is a separate facility with separate acceptance requirements.

Trust bootstrap and installation

These files are installed by the trusted image build, outside the runtime payload:

Source Installed path Owner/mode
hydra/update_engine.py /usr/lib/hydra-update/engine.py root:root 0644
bin/hydra-update /usr/bin/hydra-update root:root 0755
bin/hydra-update-recover /usr/bin/hydra-update-recover root:root 0755
Independently supplied public key /etc/hydra/update.pub root:root 0644

The launchers use isolated Python (/usr/bin/python3 -I) and never import the mutable hydra package. Install Python and OpenSSL in the image. Protect all parent directories with root ownership and no group/other write permissions. The engine and its key cannot update themselves through this protocol; their maintenance is an independently verified image/bootstrap operation.

An old ISO without these files cannot safely bootstrap by executing a downloaded zipapp. Reinstall from the newly verified ISO, or independently verify and install the exact engine, launchers and public key from a trusted source as administrator. Do not fetch the trust key from the same release discovery response being verified. Verify its fingerprint through the independently supplied release information.

Production mutation requires UID 0, x86_64, the Hydra build marker and Hydra's ID=hydra-os OS release. Production always uses /etc/hydra/update.pub; a key override is refused. A --root DIRECTORY support/testing argument targets an explicit isolated root containing the build marker and existing payload parent directories. Only these staging roots allow --public-key FILE. No environment variable silently redirects an installed update to another root or key.

Release creation

Keep the Ed25519 private key outside Git, the source/build context and the Nix store, in a user-owned private file (0600). Never upload it as a release asset. Provision this key through the release operator's separate key-management process. The builder does not generate keys, publish assets or overwrite an existing output.

python3 dev/release.py \
  --source /path/to/verified-checkout \
  --output /path/to/new-release-directory \
  --private-key /private/outside-git/release.pem \
  --version 1.0.0 --sequence 1

The three release assets are manifest.json, manifest.sig, and runtime.zip. Publish them under the immutable Gitea tag 1.0.0, exactly matching the signed version. Each new release needs a strictly increasing integer sequence; never reuse a sequence or mutate assets at an existing version. Rollback retains the highest accepted sequence independently of the current installed version. The builder is deterministic for identical source, version, sequence, key and compression implementation. It records Git HEAD; build from a reviewed clean checkout so that commit identifies the bytes actually being released.

The signature is Ed25519 over the exact bytes of manifest.json, using OpenSSL pkeyutl -sign -rawin -inkey PRIVATE -in manifest.json -out manifest.sig. Verification uses pkeyutl -verify -pubin -rawin with the pinned key. No downloaded Python is executed. runtime.zip is passive file data with reproducible timestamps.

Manifest format 1 has exact top-level fields format, product (Hydra OS), scope (runtime), channel (stable), architecture (x86_64), sequence, version, source_commit, archive and files. The archive entry contains name, size and sha256; each allowed file has size, sha256 and mode (0644 or 0755). Duplicate keys, unknown fields, unsafe paths, links/special archive members, mismatched hashes/sizes and unsupported formats are rejected. The maximum manifest is 256 KiB, archive/expanded payload 16 MiB, individual file 4 MiB and file count 256. Downloads have a 15-second socket timeout and 60-second per-artifact deadline; redirects remain on the fixed HTTPS origin. Discovery checks up to the latest 20 published release records, skipping draft/prerelease records. Publish runtime releases regularly enough that the current stable release remains in that window.

For offline/local verification and application:

hydra update check --artifacts /trusted/local/release
sudo hydra update apply --artifacts /trusted/local/release

These use the same pinned key and validation as online releases. For developer staging, add --root /isolated/hydra-root --public-key /test/public.pem. The Python API is Updater(root, public_key).apply(artifacts), .status(), .rollback(transaction) and .recover(); staging roots may run as their owner.

Durability and limits

A nonblocking flock serializes mutations. The private state directory is /var/lib/hydra-update (0700). Each transaction saves original regular-file bytes, hashes, sizes, permissions and ownership; symlink targets/ownership and previously absent files are recorded explicitly. Unsupported destination types and unsafe parent directories are refused. Payload parent directories must already exist.

All destination paths and payload bytes are checked before creating update state. All backups and a prepared journal are durable before the first runtime replacement. Writes use adjacent temporary files, file fsync, atomic replacement and directory fsync. Recovery is independent of the runtime package. The public read-only status summary is /usr/share/hydra/update-status.json; private journals are authoritative, and an abrupt loss of power may leave the summary briefly older than those journals. Run recovery as administrator when an update was interrupted.

This is a per-file recoverable transaction, not a simultaneous filesystem snapshot. A running process can observe a mixture during apply. Do not launch new Hydra runtime operations until apply/recovery finishes. Root administrators and processes that can already modify protected root-owned directories are outside the updater's concurrent-write threat model. Filesystem/device failures can require manual repair; recovery retains all evidence and never promises recovery from lost backup storage.