This guide describes how to package a SpecFact module for registry publishing: validate structure, create a tarball and checksum, optionally sign the manifest, and automate publishing in the dedicated modules repository.
Modules docs handoff: this page remains in the core docs set as release-line overview content. Canonical bundle-specific deep guidance now lives in the canonical modules docs site, currently published at
https://modules.specfact.io/.
Repository ownership
specfact-cliowns the lean runtime, bundled modules undersrc/specfact_cli/modules/, shared contracts, and CI that re-signs those bundles.specfact-cli-modulesowns official workflow bundle payloads, the marketplaceregistry/index.json, and publish automation for bundles shipped from that repo.
If you are publishing an official marketplace bundle, work from nold-ai/specfact-cli-modules.
Module structure
Your module must have:
- A directory containing
module-package.yaml(the manifest). - Manifest fields:
name,version,commands(required). For marketplace distribution, usenamespace/name(e.g.acme-corp/backlog-pro) and optionalpublisher,tier.
Recommended layout:
<module-dir>/
module-package.yaml
src/
__init__.py
commands.py # or app.py, etc.
Exclude from the package: .git, __pycache__, tests, .pytest_cache, *.pyc, *.pyo.
Script: publish-module.py
Use scripts/publish-module.py to validate, package, and optionally sign a module.
# Basic: create tarball and SHA-256 checksum
python scripts/publish-module.py path/to/module -o dist
# Write a registry index fragment (for merging into your registry index)
python scripts/publish-module.py path/to/module -o dist \
--index-fragment dist/entry.yaml \
--download-base-url https://registry.example.com/packages/
# Sign the manifest after packaging (requires key)
python scripts/publish-module.py path/to/module -o dist --sign
python scripts/publish-module.py path/to/module -o dist --sign --key-file /path/to/private.pem
Options:
module_path: Path to the module directory or tomodule-package.yaml.-o/--output-dir: Directory for<name>-<version>.tar.gzand<name>-<version>.tar.gz.sha256.--sign: Runscripts/sign-modules.pyon the manifest (usesSPECFACT_MODULE_PRIVATE_SIGN_KEYor--key-file).--key-file: Path to PEM private key when using--sign.--index-fragment: Write a single-module index entry (id, latest_version, download_url, checksum_sha256) to the given path.--download-base-url: Base URL fordownload_urlin the index fragment.
Namespace and marketplace: If the manifest has publisher or tier, the script requires name in namespace/name form and validates format (^[a-z][a-z0-9-]*/[a-z][a-z0-9-]+$).
Signing (optional)
For runtime verification, sign the manifest so the tarball includes integrity metadata:
- Environment: Set
SPECFACT_MODULE_PRIVATE_SIGN_KEY(inline PEM) andSPECFACT_MODULE_PRIVATE_SIGN_KEY_PASSPHRASEif the key is encrypted. Or use--key-fileand optionally--passphrase/--passphrase-stdin. - Re-sign after changes: Run
scripts/sign-modules.pyon the manifest (and bump version if content changed). See Module signing and key rotation.
GitHub Actions workflow
nold-ai/specfact-cli ships .github/workflows/publish-modules.yml for bundled
modules only (re-sign + package + in-repo snapshot). It does not open PRs
against specfact-cli-modules or update the marketplace registry/index.json.
- Triggers:
workflow_runafter Module Signature Hardening ondev/main,workflow_dispatch(single module path), and push of tags*-v*. - Bundled snapshot:
resources/bundled-module-registry/index.jsonrecords published versions for CI comparison. When versions advance, the workflow opens a PR in this repository (GITHUB_TOKEN) updating that file and uploads build artifacts. - Marketplace registry: Official bundle publishing and
registry/index.jsoninnold-ai/specfact-cli-modulesstay in that repository’s own automation.
Optional signing in CI: add repository secrets such as
SPECFACT_MODULE_PRIVATE_SIGN_KEY and SPECFACT_MODULE_PRIVATE_SIGN_KEY_PASSPHRASE.
Signing must happen before publish so the generated registry artifacts contain current integrity
metadata.
Release flow summary (bundled modules in specfact-cli)
- Bump the bundled module
versioninmodule-package.yaml. - Re-sign the changed manifest(s) (for example via
sign-modules.ymlondev/main). - Verify signatures locally and in CI.
- Push to
devandmain(or merge via PR to those branches) in specfact-cli; when signing completes,publish-modules.ymlmay open a PR updatingresources/bundled-module-registry/index.json.
For marketplace bundles maintained in specfact-cli-modules, follow that
repository’s publishing guide and registry PR flow.
Best practices
- Bump module
versioninmodule-package.yamlwhenever payload or manifest content changes; keep versions immutable for published artifacts. - Use
namespace/namefor any module you publish to a registry. - Before merging to a protected branch such as
main, run strict verification (same bundle asmainpre-commit andsign-modules.ymlpush verify), e.g.hatch run verify-modules-signatureorpython scripts/verify-modules-signature.pywith the flags inscripts/module-verify-policy.sh(VERIFY_MODULES_STRICT). On feature ordevbranches, local pre-commit and PR CI use the relaxedVERIFY_MODULES_PRbundle (--skip-checksum-verification); see Module signing and key rotation. Follow your registry’s policy if stricter. - Prefer
--download-base-urland--index-fragmentwhen integrating with a custom registry index.
See also
- Module marketplace – Discovery, trust, and security.
- Module signing and key rotation – Keys, signing, and verification.
- Custom registries – Adding and configuring registries for install/search.