Skip to content
SHC Docs

HTTP app repositories

An http(s) repo is a static site serving an index manifest plus one .app archive (tar.gz of the app package directory) per app — the SHC equivalent of a helm chart repository. Any web host works: GitLab Pages, S3, nginx. The shc repo’s own CI publishes the full apps/ catalog this way on every default-branch push and release tag (apps:index job → GitLab Pages, next to the terraform provider mirror).

Terminal window
shc repo add shc https://<pages-domain>/apps/index.yaml
shc repo sync shc
shc install shc/plane

Private hosts work too: shc repo add … --token <t> sends Authorization: Bearer <t> on both the index fetch and every archive download (terraform: the shc_repo resource’s token attribute).

Sync parses the index and replaces the repo’s catalog rows; each row’s source is the resolved archive URL with the digest pinned as a #sha256=<hex> fragment. On install, the daemon downloads the archive node-locally, verifies the digest (mismatch fails closed with X500902REPMAT), and extracts it into the repo’s cache dir — so unlike git+file:// repos, http repos install from any node.

index.yaml (JSON also parses — YAML is a superset):

api_version: v1
apps:
- name: plane
version: "1.0.0"
description: Project management
category: productivity
tags: [projects]
archive: plane-1.0.0.app # relative to the index URL, or absolute
digest: sha256:<hex> # over the archive bytes

Entries missing name or archive fail the sync loudly. parent_app / vapp / homepage / icon carry through to the catalog like the git crawl does.

go run scripts/build-apps-archives.go -apps <tree> -out <dir> packages one app per subdirectory (app.yaml required) into byte-reproducible archives — sorted walk, zeroed times, fixed ownership — so digests only change when package content changes. It writes <name>.app plus a <name>.meta.json metadata slice per app; build index.yaml entries from the meta slices (the shc catalog’s own publisher does exactly that via scripts/publish-apps-registry.sh + scripts/gen-apps-registry-index.go). Serve the archives and the index anywhere; register the index.yaml URL.

Versions are immutable. Consumers pin digests at sync time; never republish different bytes under the same archive name — bump the app’s version instead (the archive name embeds it).

An archive is the app package dir’s contents at the tar root (app.yaml, compose.yaml, …). Extraction rejects path traversal, absolute names, and link entries.