2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00
2026-10-06 18:14:31 +02:00

local-registry 0.14.0

Authentication review and fixes. Credential JSON and Authorization headers are parsed without ambiguous alternatives. Token rotation is serialized across processes sharing the same auth file; losing an observed file fails closed. Public OCI HEAD remains available when credentials are damaged. Access logs use the actual socket peer and the verified identity. See iteration 0.14.0 and the current verification report.

Gateway administrator login. The server consumes Remote-Email and the comma-separated Remote-Groups from explicitly trusted gateway peers. Members of registry_admin enter the console automatically. Present headers have priority; existing Basic/Bearer client authentication applies only when both identity headers are absent. Configure the direct peer with -auth-proxy IP. See gateway setup and the exact contract, iteration 0.13.0 and the 0.13.0 verification report.

Manifest-optional publishing CLI. Feed a finished binary or installation tree to package, or use publish -input to package, sign and publish in one call. Set metadata with flags or use the explicit interactive mode; complete manifests remain supported. Both inputs use the same native packaging and snapshot publication contracts. See the publishing guide, iteration notes and 0.12.0 verification report.

./local-registry publish -interactive -input ./rootfs -channel testing

Run this on the trusted publishing host with local registry storage and your operator-provisioned signing key. The build stays separate; the HTTP server needs no changes. For packaging alone, use package instead of publish.

Recent image tags and one-call multiarch packages. The Images screen shows the three most recently published tag bindings per repository, with durable publication times that also work when retagging older images. A schema 2 package manifest maps architectures to binaries or installation trees; one command builds all DEB/RPM targets into an atomic bundle. See tag publication semantics, the packaging example and the 0.11.0 test report.

Complete multiarch image imports. Published pull jobs copy the entire OCI index or Docker manifest list, every platform manifest, config and layer directly into the registry. The original digests are preserved, shared blobs are reused, and the destination tag changes only after the complete graph is verified and durable. Imports work without a container engine or backing database. See operation and upgrade notes and the 0.10.0 test report.

Upstream freshness through REST. Your existing scheduler can check a source, import its exact digest and follow the durable run ID. Checks and synchronization have separate timestamps. Older single-platform receipts now request a complete import. See the REST contract and examples.

Podman by default, Docker as fallback. Automatic engine selection prefers Podman. If it is unavailable, Docker is selected and a persistent notice appears on every authenticated console page. Explicit engine settings remain binding; selecting Docker yourself does not show a fallback warning. Helpers follow the same preference and report fallback in their output. See engine configuration and the 0.8.2 test report.

SQLite snapshots from the source host. The new sqlite-backup.py helper creates and checks consistent online copies, including committed WAL data. The registry deduplicates 64 KiB blocks before compression and retains complete, independent snapshots. Recovery verifies the recorded digest and SQLite integrity before publishing a new file. The registry server needs only its binary and data directory for this feature; Python/SQLite run on the source and recovery hosts. See setup and recovery, measurements and the 0.8.0 test report.

Native MySQL and MariaDB page backups. Full, differential and incremental backups now share the database console and durable job system. Dedicated repositories retain complete chains, verify archive checksums, and prepare isolated recovery copies through local-registry database-restore. MariaDB uses a checked checkpoint procedure to reduce repeated page copies after bulk loads. See configuration and recovery, measurements and the 0.7.0 test report.

Native PostgreSQL block backups. The Backups → Databases view runs pgBackRest full, differential and incremental backups, checks WAL archiving, verifies stored backups, and applies dependency-aware retention. Jobs have durable intent, bounded logs and explicit interruption/error states. The acceptance suite restores real PostgreSQL data, tablespaces and a point in time before an intentional deletion. See setup and recovery, database measurements and the 0.6.0 test report.

Private deduplicated backups, locally validated on 2026-09-30. New backups share content-defined chunks and store a complete manifest for each snapshot. Downloads reproduce the exact uploaded bytes; old raw/gzip backups remain readable. The console separates restore size from stored size and can verify an entire backup. See backup operation and format, the measured comparison and the 0.5.0 test report.

Matt's Go registry now accepts built programs, produces DEB/RPM packages and publishes signed APT/DNF repositories. The existing OCI image, managed pull/build and backup paths remain part of the same executable. Go code uses the standard library plus one pinned, vendored BSD-licensed Rabin chunker from restic. Native packaging and signing use the distribution tools. No runtime dependency downloads are required.

The larger Patronage Systems logo remains anchored to the center of the whole page, accounting for the sidebar. It overlaps the header separator with a clear cutout and small corner accents. Stable scrollbar space prevents page-height changes from shifting the layout. The logo is embedded in the HTML; displaying it needs no external requests.

Version 0.4.0 hardened concurrent uploads, OCI publication, cleanup, backups, managed jobs and credential replacement against interrupted operations and storage errors. The package-first console shows interrupted jobs and uncertain credential commits explicitly. See transaction and recovery guarantees and the console guide. Local browser automation now covers desktop and mobile layouts, navigation, credentials, downloads and errors; the full manual accessibility checklist remains separate.

Current verification: 621 Go race-test results passed, no failures; three GPG integration tests were skipped because this environment denies Unix sockets. Go vet, the static Linux amd64 build, UI logic and 43 checks through a real HTTP test gateway passed. Production idgate and a real browser were not exercised. Historical packaging/database/browser reports remain in docs/ and are not presented as fresh validation. See the iteration record.

Build and run

Linux, Go 1.24 or newer (the vendored dependency permits offline builds):

CGO_ENABLED=0 go build -trimpath -o local-registry .
./local-registry -listen 127.0.0.1:5000 -root ./registry-data
# For builds and local-only pulls, select the engine installed on this host:
./local-registry -engine podman -root ./registry-data
# Or: REGISTRY_ENGINE=docker ./local-registry -root ./registry-data

./run.sh builds from source and forwards its arguments to the executable. The included local-registry executable is Linux amd64; rebuild on other Linux architectures. On Windows use WSL2 with a Linux distribution and keep the data root in its Linux filesystem. Native Windows builds are not supported because the process locks use Linux/Unix flock. Only one HTTP server may own a data root; startup refuses a second writer. Stop the previous server and back up the complete root before upgrading. The included Linux binary is statically linked. Registry metadata and backup manifests are files; the registry requires no backing database. SQLite backup production runs on a separate source host using the included helper. The server itself needs no package tools. Optional native database profiles additionally require pgBackRest for PostgreSQL, or Python 3.10+ and the matching native backup/client tools for MySQL/MariaDB. Scheduled API jobs can use the included Python helper. Build/publish hosts need:

  • DEB: dpkg-deb; OpenSnitch source builds also use dpkg-shlibdeps.
  • RPM: rpmbuild, rpm, rpmsign, rpmkeys, createrepo_c.
  • Publishing: gpg, an explicitly chosen signing key and its GPG home.

For example, on Ubuntu/Debian the tool packages are dpkg-dev rpm createrepo-c gnupg. On Fedora/Rocky they are rpm-build rpm-sign createrepo_c gnupg2; install Debian packaging tools separately if that host must also build DEBs. Target-native builders are appropriate for programs with shared-library needs.

Remote deployments use HTTPS at a reverse proxy. For gateway identity, configure its exact direct peer with -auth-proxy IP and have it overwrite Remote-Email and Remote-Groups from the authenticated session. registry_admin grants administrator access; absent headers select the existing client token. See the gateway contract for trust, forwarding and session details.

For header-free client access, put {"user":"admin","token":"<secret>"} in <root>/auth.json with mode 0600; -auth-file can select another file. Without a gateway or token, administration is restricted to a direct loopback peer, a loopback Host and same-origin browser requests. A configured gateway disables tokenless bootstrap. X-Forwarded-For never grants access. Unsafe browser requests must be same-origin; preserve Host, Origin and Sec-Fetch-Site at the proxy. Header-free CLI clients retain their token behavior. OCI image pulls remain public.

Credential files require the exact token key; user is optional. Duplicate, unknown, differently cased or null fields are rejected. Usernames are at most 254 UTF-8 bytes, printable, without a colon or surrounding whitespace. Tokens are at most 4096 visible ASCII bytes without whitespace. Bearer credentials cannot contain commas; such custom tokens require Basic authentication. Generate a random token with POST /v2/_auth and {"generate":true} or supply {"token":"..."}; supplying both is rejected. A missing file permits initial local bootstrap only until that process has observed an auth file. To clear a token intentionally, use authenticated direct-loopback DELETE /v2/_auth, which writes an explicit empty token. Forwarding headers disable bootstrap and token clearing. Restore an accidentally removed file atomically; after a process restart, a missing file is indistinguishable from a fresh installation. Configure -auth-proxy before proxying an installation without a token.

Rotations use a persistent <auth-file>.lock file. Instances sharing credentials must use the same file path on a filesystem supporting flock; do not delete the lock while any instance runs. External editors should replace the credential file atomically and coordinate on that same lock when rotating concurrently.

Container clients negotiate a Bearer token at /v2/, which returns HTTP 401 with a challenge when administrator credentials are configured. The local /v2/_token exchange issues tokens valid for five minutes: anonymous clients receive pull access; valid administrator credentials can request repository- scoped push/delete access. OCI tokens cannot administer the server or mint broader tokens. Administrator credential rotation immediately invalidates them. Direct administrator Basic/Bearer API requests remain supported. A health check requiring HTTP 200 from /v2/ must supply credentials when authentication is on. An HTTPS-terminating proxy must preserve Host and set X-Forwarded-Proto to https so the challenge advertises the correct token endpoint. See OCI authentication for client examples and deployment details.

The embedded console at / puts package repositories first, with separate views for container images, managed image jobs, backups, enterprise access and settings. Gateway administrators are recognized automatically. Header-free users can sign in with the administrator button; their token stays in page memory and is forgotten on reload. No CDN, font service or JavaScript build step is required. See the console guide. Managed Docker/Podman pushes receive credentials through an isolated temporary config removed at completion; remote source credentials are not changed. The publisher is a local operator command, not an HTTP endpoint that accepts arbitrary filesystem paths or runs uploaded programs.

1. Describe the program

A manifest is optional. The publishing CLI accepts direct flags and offers an opt-in interactive mode. For a complete reusable manifest, copy examples/program.json for one architecture, or examples/program-multiarch.json for several, and replace the metadata and target declarations. A raw executable is installed as /usr/bin/<name>; a directory is an installation root containing, for example, usr/bin/program, usr/share/... and etc/program.conf.

{
  "schema_version": 1,
  "name": "example-program",
  "version": "1.0.0",
  "release": "1",
  "architecture": "amd64",
  "maintainer": "Your Organization <packages@example.invalid>",
  "description": "Program description",
  "license": "MIT",
  "source_date_epoch": 1750000000,
  "targets": [
    {"format":"deb","distribution":"ubuntu","suite":"noble","dependencies":[]},
    {"format":"rpm","distribution":"fedora","suite":"44","dependencies":[]}
  ]
}

This dual-target example fits a binary whose compatibility with both targets has been established, such as a suitably tested static program. Target labels do not prove ABI compatibility. Dynamic programs require their target libraries, correct dependencies and target-specific execution checks.

Manifest architecture DEB RPM
amd64 amd64 x86_64
arm64 arm64 aarch64
riscv64 riscv64 riscv64
386 i386 i686

ELF architecture is checked against the declaration. This command does not cross-compile. source_date_epoch must be positive and not in the future; it normalizes payload timestamps. With fixed inputs and native tool versions, the DEB and RPM build tests check byte-identical repeatability.

In a supplied manifest, dependencies are required explicitly as an array, including an empty array when appropriate. Without a manifest, -depends sets them; omitted declarations produce an empty array and are shown in the CLI plan. RPM also derives ELF library requirements. Regular files under /etc become Debian conffiles or RPM %config(noreplace) entries. Special files, setuid/setgid payloads, unsafe names and escaping symlinks are rejected. Unsupported inputs fail; there is no unsigned publication mode.

Optional source_archive is a clean path relative to the manifest. The archive is copied into the bundle and bound by its full SHA-256. Optional scripts contains pre_install, post_install, pre_remove, post_remove, each a relative local file beginning with #!/bin/sh. These are trusted package lifecycle scripts that run with the package manager's privileges. Their bodies are hashed with the input; they are not executed while packaging. Supply format-appropriate scripts: Debian and RPM lifecycle arguments differ.

2. Build the packages

For an interactive start without a manifest:

./local-registry package -interactive -input ./rootfs

For scripted calls, metadata flags replace the manual JSON step; see flags, defaults and examples. These existing manifest commands also remain supported:

./local-registry package -manifest program.json -input ./program -output ./bundle
# Or use an installation directory:
./local-registry package -manifest program.json -input ./rootfs -output ./bundle

For several architectures, use schema 2 with inputs instead of architecture:

{
  "schema_version": 2,
  "name": "example-program",
  "version": "1.0.0",
  "release": "1",
  "inputs": {
    "amd64": "dist/amd64/program",
    "arm64": "dist/arm64/rootfs",
    "riscv64": "dist/riscv64/program",
    "386": "dist/386/program"
  },
  "maintainer": "Your Organization <packages@example.invalid>",
  "description": "Program description",
  "license": "MIT",
  "source_date_epoch": 1750000000,
  "targets": [
    {"format":"deb","distribution":"ubuntu","suite":"noble","dependencies":[]},
    {"format":"rpm","distribution":"fedora","suite":"44","dependencies":[]}
  ]
}
./local-registry package -manifest program-multiarch.json -output ./bundle

This builds eight native packages with one call. Include only the architectures you need (one to four); all declared targets apply to every input. Each input is either an already-built executable or an installation directory. Paths are clean, relative to the manifest directory, and must stay within it even through parent symlinks. Input roots themselves must be regular files or directories. The current working directory does not change their resolution. Schema 2 rejects -input and the single architecture field; schema 1 continues to require both. Dependencies, metadata, lifecycle scripts and the optional source archive are shared. The packager does not compile or run foreign binaries and needs no emulator.

RPM architecture is selected with rpmbuild --target; the spec does not impose a host-compatible BuildArch constraint. See the RPM build-target documentation. The installed native packaging tools must support the requested target architecture.

The output directory must be new and outside every input tree. All targets must succeed before it appears. It contains native packages, package-manifest.json, package-receipt.json and the optional source archive. The receipt binds the exact staged file paths/modes/bytes and scripts, the manifest, and every output package. Schema 2 adds input_sha256, one content hash per architecture; its source_sha256 binds those sorted architecture/hash pairs using the framed multi-architecture-inputs-v2 domain. Publication verifies the complete architecture/target matrix and rejects missing, duplicate or unexpected entries. One publish call publishes all architectures in the same signed snapshot. Tool versions and target execution evidence should accompany a release.

A signed ZIP is a later authenticated transport for this bundle. ZIP ingestion and handoff-signature verification are not implemented in this iteration; a ZIP is not silently treated as a program or a trusted directory. Payload packing and repository signing already have separate, explicit contracts.

3. Publish atomically

Provision a signing key through your key-management process. The publisher requires the complete primary fingerprint and a dedicated GPG home. Its agent must be able to sign non-interactively (for example, an unlocked key on the isolated publishing host). Private keys and passphrases never enter the bundle, repository or subscriber configuration.

./local-registry publish -root ./registry-data -bundle ./bundle \
  -gnupghome /secure/repository-keys -key FULL_PRIMARY_FINGERPRINT \
  -channel testing

Repeat the same command with community or enterprise to promote the same artifacts. Publication checks native metadata against the receipt, hashes, source provenance and the selected key. A version/release/architecture/target identity cannot change bytes in another channel. Promotions reuse signed RPM bytes. To change package content, increment the version or release.

Publication holds an interprocess writer lock, prepares a complete snapshot, signs and verifies native metadata, syncs files and atomically switches packages/current. Failed preparation leaves the active snapshot unchanged. DNF fetches metadata and its detached signature separately; a publication between those requests can require a client refresh. Atomicity applies to the filesystem commit, not an entire multi-request client transaction. Package files, APT by-hash indexes and DNF metadata from earlier publications remain available to clients already downloading. Do not edit snapshots in place.

URL Content
/apt/<channel>/<distribution>/dists/<suite>/... APT Release/InRelease and indexes
/apt/<channel>/<distribution>/pool/... DEB payloads
/dnf/<channel>/<distribution>/<suite>/<rpm-arch>/... Signed RPMs and repodata
/keys/repository.asc Repository public key
/sources/index.json and .asc Signed binary-to-source mapping
/sources/<archive> and .asc Corresponding source and signature

APT uses SHA-256 indexes, Acquire-By-Hash, signed InRelease/Release.gpg and a 14-day Valid-Until. Re-publish an existing bundle at least weekly to refresh its metadata if no new version is released. Scheduling belongs to the operator's existing CI/scheduler. DNF uses signed RPMs and signed repomd.xml; its ordinary metadata refresh setting is not an equivalent cryptographic freshness deadline.

The signing key is pinned on first publication. Key rollover and snapshot retention/garbage collection are not implemented. Disk use grows with retained releases; old files must not be removed while clients may still reference them. The existing OCI GC does not touch package snapshots. Back up the whole storage root and signing-key material separately.

Old manually populated <root>/apt and <root>/dnf mirrors are no longer served. They are not deleted or silently imported: generate and publish bundles through the new contract. Existing OCI image paths and legacy backup objects remain readable. New backup data uses the separate private chunk layout described in BACKUPS.md; copy both manifests and chunks when backing up the registry.

4. Configure clients once

The examples below use an operator-owned HTTPS endpoint repo.example.org. Download /keys/repository.asc, check its full fingerprint through an independently trusted channel, and install it as /etc/apt/keyrings/registry.asc or /etc/pki/rpm-gpg/REGISTRY.asc (mode 0644). A key fetched from the same repository without independent verification does not establish initial trust.

Ubuntu 24.04 example, /etc/apt/sources.list.d/registry.sources:

Types: deb
URIs: https://repo.example.org/apt/community/ubuntu
Suites: noble
Components: main
Signed-By: /etc/apt/keyrings/registry.asc
sudo apt-get update
sudo apt-get install example-program
sudo apt-get upgrade

Fedora 44 example, /etc/yum.repos.d/registry.repo:

[registry]
name=Registry community
baseurl=https://repo.example.org/dnf/community/fedora/44/$basearch
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/REGISTRY.asc
metadata_expire=1h
sudo dnf install example-program
sudo dnf update

Keep the target distribution's normal repositories enabled to resolve library and daemon dependencies. The registry does not mirror the base operating system.

Subscriptions and commercial model

testing and community are public. enterprise requires an unexpired, per-customer Basic credential on every GET and HEAD, including localhost. Credentials grant downloads only. Administrator tokens cannot substitute for subscriber credentials. Source archives and their index remain public after expiry; installed programs do not perform license checks.

./local-registry subscriber -root ./registry-data -user customer-001 \
  -expires 2027-09-28T00:00:00Z
# Rotate: repeat the command. Revoke:
./local-registry subscriber -root ./registry-data -user customer-001 -revoke

Issuance prints the new random token once. Only its SHA-256 and expiry are stored in packages/subscriptions.json (0600); HTTP reads use a username index and a cached configuration. Atomic replacement applies rotations without restart. A COMMIT_UNCERTAIN failure can mean a new token is already active; the issuing response still provides that token once. Preserve it and inspect current state before retrying. The CLI prints that response and exits nonzero. This is local provisioning, not billing, host enrollment or a payment check.

For APT enterprise access, change the URI channel and create a root-readable /etc/apt/auth.conf.d/registry.conf (0600):

machine https://repo.example.org/apt/enterprise
login customer-001
password <issued-token>

For DNF, change community to enterprise and add username=customer-001 and password=<issued-token> to the repo file; make that file 0600. Use HTTPS; credentials never belong in repository URLs or CI logs.

COMMERCIAL.md explains Proxmox's separation of open-source licenses, repository channels and paid maintenance/support, and proposes the corresponding model here. The supplied registry did not declare a software license; this iteration does not invent one for Matt's code.

OpenSnitch and checks

examples/opensnitch/README.md contains the exact CLI source, original Fedora CLI RPM, build/repack procedures, licenses and provenance. deliverables/ contains the generated unpublished, unsigned package bundles for Ubuntu 24.04 amd64 and Fedora 44 x86_64, ready for publish. Fedora execution, daemon interaction and other architecture runtime behavior remain unverified; see the evidence before declaring a supported target.

go test -race -v ./...
go vet ./...
python3 test/crash_recovery.py --binary ./local-registry
python3 test/backup_chunks.py --binary ./local-registry --output /tmp/registry-backup-checks
python3 test/backup_chunks.py --binary ./local-registry --output /tmp/registry-backup-benchmark --benchmark
node test/ui_logic.mjs
python3 test/publishing_cli.py --binary ./local-registry --results /tmp/registry-cli-checks
bash test/e2e_pulls.sh
python3 test/oci_clients.py --binary ./local-registry --engine podman
python3 test/oci_clients.py --binary ./local-registry --engine docker
# Strict native gate on a disposable Linux host with APT, DNF and signing tools:
sudo test/packages.sh /tmp/registry-test-results

The native gate builds a second unrelated program at two versions, signs and publishes both, installs/upgrades it with real APT/DNF, and checks tamper rejection. A missing prerequisite or failed step is a nonzero result. Some optional native Go integration tests skip when a tool is absent or GPG cannot run; their skip status must not be counted as passing the strict release gate. New tests clean up their keys, processes and temporary roots. Legacy shell tests are retained for the original OCI scenarios; focused OCI regression coverage is also in Go. The local 0.5.0 run completed the full native gate and all Go tests with zero skips. The previous 0.4.3 validation remains in its historical evidence directory. Docker requires a running daemon for its real-client test; the test builds a scratch image without downloading a base.

S
Description
No description provided
Readme
14 MiB
0 Stars 1 Watchers 0 Forks
Languages
Go 65.8%
Python 24%
Shell 7.4%
JavaScript 2.4%
HTML 0.4%