Connection lost
Trying to reconnect…
Server didn't respond
Recovering…
Host your own pack registry
An emisar pack registry is not a service — it's a static file layout over HTTPS. Anything that serves files can be one: a GCS or S3 bucket, MinIO, plain nginx, an artifact store behind your VPN. Author packs for your own tooling, publish them to infrastructure you control, and install them across your fleet with the same hash-pinned flow the public registry uses. This isn't a side door — the public registry is built and published by the exact tool documented here, from our CI, on every packs change.
Distribution and trust are deliberately separate: trust never
rests on the registry. Installs pin a
sha256
content hash, the runner re-hashes every pack at load time, and the portal requires an
operator to trust each pack@hash
per account before its actions can run. A registry — ours or yours — only moves bytes.
-
1 Author and validate your packs
Packs are directories of YAML — the authoring guide walks the format, and the YAML reference covers every field. Validation runs the same loader and hash path the runner enforces:
emisar pack validate ./billing-tools
-
2 Get packctl
packctlbuilds and publishes registries. It's deliberately a separate binary fromemisar: your fleet hosts never carry publish code or publisher credentials — only the workstation or CI job that builds the registry does. It shares the runner's pack loader, so published content hashes matchemisar pack validatebyte-for-byte.go install github.com/andrewdryga/emisar/runner/cmd/packctl@latest
-
3 Build the registry tree
--base-urlis wherever you'll host it; tarball URLs in the catalog join onto it:packctl catalog build --packs ./packs --out ./dist \ --base-url https://packs.acme.internal
The output is the whole registry — immutable, content-addressed artifacts plus two mutable pointers:
v1/catalog.json latest catalog (mutable pointer) v1/catalog/<sha256>.json immutable catalog snapshot v1/suggest.json lean suggest index (mutable pointer) v1/schemas/*.json catalog + authoring schemas v1/packs/<id>/<version>/<sha256>/pack.tar.gz immutable pack tarball
-
4 Host it anywhere static
GCS is native — immutable objects upload precondition-protected so existing bytes are never overwritten, and the pointers flip last:
GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token) \ packctl catalog publish --dir ./dist --bucket acme-pack-registry
S3, MinIO, nginx, or anything else: the tree is plain files — sync it with any tool. Upload the immutable objects first, then the two pointers:
aws s3 sync ./dist s3://acme-pack-registry \ --exclude "v1/catalog.json" --exclude "v1/suggest.json" aws s3 cp ./dist/v1/suggest.json s3://acme-pack-registry/v1/suggest.json aws s3 cp ./dist/v1/catalog.json s3://acme-pack-registry/v1/catalog.json
-
5 Install from it, fleet-wide
Per install with
--registry, or set the default for a host once withEMISAR_PACKS_REGISTRY. Pin the hash you reviewed — a tampered or mismatched copy is rejected before it reaches the runner:sudo emisar pack install billing-tools \ --registry https://packs.acme.internal --hash sha256:<reviewed> sudo systemctl reload emisar # or fleet-wide via the environment: export EMISAR_PACKS_REGISTRY=https://packs.acme.internal emisar pack update --dry-run
Then trust the pack once on the dashboard's Packs page — actions gate on that per-account trust, not on where the bytes came from. Edit the pack and its hash changes, which re-marks it pending until you re-trust.
-
6 Publish the next version safely
Always build against the currently-published catalog so history carries forward.
--previousenforces the registry's core guarantee: a pack that changed bytes for an already-publishedid@versionfails the build — bump the version instead. On a critical fix,--retire-olderretires everything older than the version you're publishing in one operation.curl -fsS https://packs.acme.internal/v1/catalog.json -o current.json packctl catalog build --packs ./packs --out ./dist \ --base-url https://packs.acme.internal --previous current.json
What your registry does — and doesn't — change
- The cloud works unchanged. Runners advertise installed packs regardless of source; trust, policy, approvals, and audit apply identically to private packs.
-
Air-gap friendly.
Mirror the public registry's tree — or build your own from a vendored
packs/checkout — onto an internal host. Nothing phones home. -
Discovery stays public-registry-only.
The dashboard's catalog browser and
emisar pack suggestlist the public index; your private packs appear in the portal the moment a runner advertises them, just not in those browse surfaces. -
No CRL-style revocation.
Retire versions with
--retire-olderand republish; hash pinning means an already-reviewed install never changes underneath you.