Skip to content

feat(packs): support unsigned packs from a static internal registry#1389

Draft
vjeantet wants to merge 2 commits into
nolabs-ai:mainfrom
vjeantet:feat/static-registry
Draft

feat(packs): support unsigned packs from a static internal registry#1389
vjeantet wants to merge 2 commits into
nolabs-ai:mainfrom
vjeantet:feat/static-registry

Conversation

@vjeantet

@vjeantet vjeantet commented Jul 8, 2026

Copy link
Copy Markdown

feat(packs): support unsigned packs from a static internal registry

Closes #1388. Related to #857.

What

Enables air-gapped macOS fleets to distribute and update nono profiles,
Claude Code skills/plugins/hooks, and arbitrary enterprise files from an
internal static (nginx) registry, with signature verification disabled
and integrity guaranteed by per-artifact SHA-256.

This is the lower-friction, integrity-only transport discussed in
#1388 (a complement to the OCI direction of #857, not a replacement).
The keyed ECDSA trust mode is a separate follow-up PR layered on the same
[registry] config.

Changes

  • Config: [registry] section (url + verify, fail-secure verify = true by default) and a resolve_registry helper with precedence
    --registry > NONO_REGISTRY > config > default. Verification is turned
    off only via explicit --insecure, NONO_REGISTRY_INSECURE, or
    [registry].verify = false.
  • Pull (unsigned tier): when verify is off, skip the Sigstore bundle
    download/verify, namespace assertion, and subject-membership check; keep
    the SHA-256 check. Record an unsigned:internal-registry signer sentinel
    in the lockfile and write no .nono-trust.bundle.
  • Runtime guard: verify_profile_packs skips bundle re-verification
    only for packs whose lockfile signer is the sentinel, keyed on the
    lockfile so signed packs always get full re-verification. SHA-256 stays
    enforced.
  • Resilience: PullResponse tolerates the minimal static JSON (optional
    provenance; default bundle_url / scan_passed / size_bytes).
  • Zero-network posture: suppress registry phone-home (update hints +
    migration provider lookup) when verification is disabled.
  • Publish: nono pack publish-static emits the static registry tree
    (versions/<v>/pull, versions/latest/pull, status, files/…) reusing
    the serde types, with latest tracking the highest published semver.
  • Docs: air-gapped/internal-registry workflow + GitLab CI example.

Security notes

  • The unsigned tier is integrity-only, no provenance, strictly opt-in,
    never the default.
  • Fail-secure everywhere: default verify = true; SHA-256 enforced at
    install and at runtime; the lockfile-keyed sentinel guard prevents a
    signed pack from being silently downgraded to integrity-only.
  • No change to public-registry or existing keyless signing semantics.

Tests

Unit tests: registry precedence, fail-secure verify resolution, minimal
static JSON deserialization, publish-static generator round-trip, latest
tracking, sentinel runtime guard + tamper detection.

cargo check -p nono-cli and the crate test suite pass locally on macOS.

Stacking

Base branch: feat/static-registry off upstream/main. The keyed-trust PR
is stacked on top of this one and should be reviewed/merged after it.


Agent compliance check (AGENTS.md)

  • I am not prohibited from contributing under this policy
  • An issue already exists — new issue Static internal pack registry (nginx-servable, unsigned integrity tier) #1388 filed; awaiting
    maintainer guidance (see risks section of the issue) before merge
  • I described my intent and approach in the issue discussion
  • I reviewed repository coding and security rules for the affected area
  • I provided required attribution for reused or adapted code (builds on
    existing nono::trust / registry serde types; no external code copied)
  • I did not use forbidden patterns such as unwrap/expect
  • I used NonoError where required
  • I validated and canonicalized all relevant paths
  • This PR matches the approved or disclosed issue scope — disclosed;
    not yet approved (issue is a proposal pending maintainer direction)

Note: this contribution is authored with AI assistance. Two checklist
items above are intentionally left unchecked because #857/#1388 is
still a question-labelled proposal. Per AGENTS.md §5/§7 I am not
requesting merge yet — filing for maintainer guidance first.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

PR Review Summary

Size

Metric Value
Lines added +876
Lines removed -69
Total changed 945
Classification Large (> 300 lines)

Affected crates

  • crates/nono-cli — CLI changes. Verify argument parsing, flag documentation, and UX behaviour across supported platforms.

Blast radius — Contained

This PR touches: source code


Updated automatically on each push to this PR.

vjeantet added 2 commits July 14, 2026 16:36
Enable air-gapped macOS fleets to distribute and update nono profiles,
Claude Code skills/plugins/hooks, and arbitrary enterprise files from an
internal static (nginx) registry, with signature verification disabled
and integrity guaranteed by per-artifact SHA-256.

- Add a `[registry]` config.toml section (url + verify, fail-secure true)
  and a `resolve_registry` helper with precedence
  --registry > NONO_REGISTRY > config > default; verify off via
  --insecure flag, NONO_REGISTRY_INSECURE, or [registry].verify=false.
- Pull path: when verify is off, skip Sigstore bundle download/verify,
  namespace assertion, and subject-membership; keep the SHA-256 check.
  Record an `unsigned:internal-registry` signer sentinel in the lockfile
  and write no `.nono-trust.bundle`.
- Run-time guard: `verify_profile_packs` skips bundle re-verification for
  packs whose lockfile signer is the sentinel, keyed on the lockfile so
  signed packs always get full re-verification. SHA-256 still enforced.
- Make PullResponse tolerant of the minimal static JSON (optional
  provenance, default bundle_url/scan_passed/size_bytes).
- Suppress registry phone-home (update hints + migration provider lookup)
  when verification is disabled.
- Add `nono pack publish-static` to emit the static registry tree
  (versions/<v>/pull, versions/latest/pull, status, files/...) reusing
  the serde types, with `latest` tracking the highest published semver.
- Document the air-gapped/internal-registry workflow.

Adds unit tests (registry precedence, fail-secure verify, minimal
deserialization, generator round-trip, latest tracking, sentinel guard +
tamper detection).

Signed-off-by: vjeantet <valere.jeantet@gmail.com>
Show an air-gapped GitLab pipeline that seeds the docroot from the nginx
host, runs nono pack publish-static to add the current version, then
rsyncs back. Seeding keeps latest/status at the true highest semver and
makes rsync --delete safe.

Signed-off-by: vjeantet <valere.jeantet@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Static internal pack registry (nginx-servable, unsigned integrity tier)

1 participant