Publishing to a catalog
A catalog is a Git repository that lists apps for the Store. Pithyx has two project catalogs, pithyx/apps-official and pithyx/apps-community, and anyone can run their own, made from the template repository pithyx/catalog-template. A box clones the catalogs it uses, verifies them and never builds anything.
Catalogs
Section titled “Catalogs”- Official (
pithyx/apps-official) is the only source oforg.pithyx.*apps besides the system image. Community (pithyx/apps-community) is on by default too; an administrator can switch it off. An administrator can add Own catalogs. - Source. Each app lives in
apps/<id>/on themasterbranch. CI builds the frontend and the images, pushes the images toghcr.io/<owner>/apps/<id>/<service>:<version>(never overwritten, linked by digest), and writes two CI-only branches:catalog-next(the beta channel) andcatalog(the stable channel). The stable branch is updated only after a manual approval. - Files.
catalog.jsonat the root names the catalog. Each branch holdsindex.jsonand, for project catalogs,index.json.sig. Per version, the index lists hashes and sizes of the package, manifest, icon and screenshots, the images, the architectures, thepithyxrange, thedataVersion, the license and the source commit. The files are atapps/<id>/<version>/:<id>-<version>.pithyxapp,pithyx.json,icon.svgandscreenshots/. - Sequence. Each publish raises the index
sequenceby one. The box refuses a lower number (store.catalog.sequence_old). - Signature. Ed25519 over the exact bytes of
index.json. A box accepts a project catalog if at least one signature comes from a key it trusts for the channel and every signature by a trusted key verifies. Unknown key ids are ignored, which allows key rotation. A failed check isstore.catalog.signature_invalid. Custom catalogs are not signed and the Store marks their apps as not verified. - On the box. Catalogs are shallow clones in
/var/lib/pithyx/catalogs/<id>/, fetched with git over https or ssh (3 minute timeout, no hooks, no submodules). A failed fetch keeps the last verified copy. Every file is checked against the index hash. - Channels and updates. The Store settings have a channel (stable reads
catalog, beta readscatalog-next), an update policy (automatic, notify or none; default notify), a daily check time (default 03:00 with a random delay of up to 60 minutes) and a per-app override. An update that needs new permissions, root or elevated rights is never installed automatically.
The review
Section titled “The review”Every pull request to a project catalog is reviewed by the project: the app id follows the reversed-domain rule, no foreign brands, no org.pithyx.* in the community catalog, and the app has to be worth having. How long a review takes, and the exact process for outside contributors, is not documented.
Machine rules run on every pull request (pithyx catalog check, see below):
- Each app has a
maintainers.jsonwith 1 to 20 GitHub logins. A pull request that changes an app must come from one of the maintainers as the file stood before the change. A new app’s file must name its author. - Workflows:
check.yaml(pull requests, read only),publish.yaml(pushes tomasterwritecatalog-next) andpromote.yaml(manual, writescatalogafter approval in the environmentcatalog-release). Runs from outside contributors wait for approval.
From sdk/schema/catalog.v1.rules.json:
- One app folder per pull request, under
apps/. At most 5 MiB per app folder and 1000 characters per line. - Required files:
pithyx.json,icon.svg,maintainers.jsonand one ofLICENSE,LICENSE.md,LICENSE.txt. The manifest needslicenseandpithyx. - Up to 8 screenshots in
screenshots/, each at most 1 MiB, named[a-z0-9][a-z0-9_-]{0,62}with.png,.jpgor.webp. - Files are UTF-8 text or listed media (gif, jpeg, png, webp, woff2) whose content matches the extension. Minified files (
.min.jsand similar) and unlisted files are refused. - Every
FROMandCOPY --fromin a Dockerfile is pinned by@sha256or isscratch. - The license is an SPDX expression of OSI-approved licenses;
LicenseRef-is refused. - Every image in the index has every architecture it lists (
store.catalog.architecture_missing), anddataVersionnever decreases. - Builds are as reproducible as possible: BuildKit is pinned by digest in the workflows and
SOURCE_DATE_EPOCHcomes from the last commit.
An own catalog
Section titled “An own catalog”- Create a repository from
pithyx/catalog-template. The publish job refuses the template’s placeholder id. - Put your apps in
apps/<id>/. The CLI commands run in the repository root:pithyx catalog check,pending,index,signandverify(see @pithyx/cli). - In the Store, open the Own tab and add the repository by URL.
- A public
https://host/pathURL is fetched at once. - A private repository uses an SSH URL such as
git@host:owner/repo.git. The box creates an SSH deploy key and shows its public half; add it to the repository as a read-only key, then fetch. - Images in a private registry need a read token (
user:token). The Store has no field for it; set it through the API withPATCH /api/store/catalogs/{key}andregistryToken.
- A public
- URLs use https without credentials, or ssh forms (
user@host:path,ssh://user@host[:port]/path). Anything else isstore.catalog.url_invalid.
Own catalogs may link images from any registry, by digest, and follow the global update policy. Signing is optional: the publish workflow signs only if the repository defines SIGNING_KEY. The box verifies signatures only for the project catalogs.
Packages that a workflow pushes to GitHub’s container registry start as private, even from a public repository; make each package public in the registry’s web interface or boxes cannot pull the images anonymously.
Removing an own catalog leaves its installed apps in place without updates. The project catalogs can be switched off but not removed.