Skip to content

feat: implement nono sideload#1190

Open
intentionally-left-nil wants to merge 3 commits into
nolabs-ai:mainfrom
intentionally-left-nil:feat/sideload
Open

feat: implement nono sideload#1190
intentionally-left-nil wants to merge 3 commits into
nolabs-ai:mainfrom
intentionally-left-nil:feat/sideload

Conversation

@intentionally-left-nil

@intentionally-left-nil intentionally-left-nil commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

When developing a registry pack, it's not easy to test the behavior of the pack, because nono requires the pack to have proper lockfiles and attestation.

To solve for this developer case, without harming the security of production nono users, this commit introduces a new compile-time build flag called sideload, which is disabled for production builds.

When nono is compiled with sideload, it introduces a new CLI command, nono sideload. This command copies the path into the correct registry location and updates the lockfile like normal. Additionally, other commands like nono run disable attestation

Linked Issue

Closes #1188

Summary

Test Plan

make build-sideload 2>&1

# Show that the warning gets generated for every message

target/debug/nono list --installed 2>&1
warning: nono was compiled with --features sideload, which means integrity protections are DISABLED. Do NOT use this binary on production systems.

# Sideload a local pack
"$NONO" sideload test-pack 2>&1

# See that it's visible with listing
"$NONO" list --installed
warning: nono was compiled with --features sideload, which means integrity protections are DISABLED. Do NOT use this binary on production systems.
always-further/opencode 0.0.5   2026-05-30 17:49
example/test-pack       0.1.0 [sideload]        2026-06-17 15:58
intentionally-left-nil/npm      0.0.8   2026-06-03 16:35

# See that the sandbox works
 NONO=/home/anil/code/nono/target/debug/nono && echo "=== ls nono-allowed (should succeed) ===" && "$NONO" run --profile example-test-pack -- ls /home/anil/code/nono/tmp/nono-allowed 2>&1 && echo && echo "=== ls nono-denied (should be blocked) ===" && "$NONO" run --profile example-test-pack -- ls /home/anil/code/nono/tmp/nono-denied 2>&1; echo "exit: $?"
=== ls nono-allowed (should succeed) ===
warning: nono was compiled with --features sideload, which means integrity protections are DISABLED. Do NOT use this binary on production systems.

  nono v0.63.0
  Verified 1 pack(s)
  Capabilities:
  ────────────────────────────────────────────────────
   r+w  /home/anil/code/nono/tmp/nono-allowed (dir)
    r   /home/anil/.config/nono/packages/example/test-pack (dir)
       + 44 system/group paths (-v to show)
   net  outbound allowed
  ────────────────────────────────────────────────────

  Applying sandbox...

hello.txt

=== ls nono-denied (should be blocked) ===
warning: nono was compiled with --features sideload, which means integrity protections are DISABLED. Do NOT use this binary on production systems.

  nono v0.63.0
  Verified 1 pack(s)
  Capabilities:
  ────────────────────────────────────────────────────
   r+w  /home/anil/code/nono/tmp/nono-allowed (dir)
    r   /home/anil/.config/nono/packages/example/test-pack (dir)
       + 44 system/group paths (-v to show)
   net  outbound allowed
  ────────────────────────────────────────────────────

  Applying sandbox...

ls: cannot open directory '/home/anil/code/nono/tmp/nono-denied': Permission denied

Command exited with code 2.

No path denials were observed during this session.
The failure may be unrelated to sandbox restrictions.

To grant additional access, re-run with:
  --allow <path>     read+write access to directory
  --read <path>      read-only access to directory
  --write <path>     write-only access to directory

Next steps:
  Add permissions: nono run --allow <path> -- ls /home/anil/code/nono/tmp/nono-denied
  Query policy: nono why --path <path> --op <read|write|readwrite>
exit: 2

# See that the production build fails to run the pack

  nono v0.63.0
nono: Package verification failed for example/test-pack: pack 'example/test-pack' is missing .nono-trust.bundle - reinstall with: nono pull example/test-pack --force
exit: 1

Checklist

  • An issue exists and is linked above
  • All commits are signed-off, using DCO
  • All new code follows the project's coding standards (CLAUDE.md) and is covered by tests
  • Public-facing changes are paired with documentation updates
  • Release note has been added to CHANGELOG.md if needed

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

PR Review Summary

Size

Metric Value
Lines added +1121
Lines removed -17
Total changed 1138
Classification Large (> 300 lines)

Affected crates

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

Blast radius — Moderate

This PR touches: source code,CI / build tooling


Updated automatically on each push to this PR.

@intentionally-left-nil intentionally-left-nil changed the title cli: Implement nono sideload feat: implement nono sideload Jun 17, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new sideload feature to the nono-cli tool, enabling the installation of packs from a local directory without registry attestation or cryptographic verification for development and testing. It adds a sideload subcommand, a Cargo feature flag, integration tests, and documentation, along with safety guards like compile-time errors for release builds and lockfile validation to prevent production binaries from loading sideloaded packs. A critical security review comment was kept, which identifies a potential path traversal vulnerability in build_local_downloads where artifact paths are joined without validation, potentially allowing arbitrary file reads.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread crates/nono-cli/src/package_cmd.rs
@lukehinds

Copy link
Copy Markdown
Contributor

would this be useful for you @intentionally-left-nil , its script that installs the package locally and prints out the profile name to use?

https://github.com/always-further/nono-packs/blob/main/scripts/dev-install.sh

@intentionally-left-nil

Copy link
Copy Markdown
Contributor Author

@lukehinds the script is mentioned in the original bug report, but to expand:

Even if the files are put in the correct place, nono's runtime still treats registry packs different than local packs for certain cases. For example, '$PACK_DIR' doesn't get expanded unless it's a registry pack

@vjeantet

Copy link
Copy Markdown

Hello, this overlaps with a distribution-side PR I have open (#1389, for #1388), and I think the two sit at different points on the same dial rather than competing, so let me share how I ended up covering a similar need.

This PR #1190 optimises the tight inner loop: edit the pack, test it immediately, no publish step, attestation off, kept safe by confining it to a non-production compile-time build. That is the right shape for pack authoring.

For my case I got a live loop without a dev-only build. #1389 lets nono pull packs from a static (nginx-servable) tree with signatures off and integrity kept via per-artifact SHA-256. On top of that I run a file watcher (watch-exec) on the pack sources that re-runs nono pack publish-static into a directory served by a plain local HTTP server, then nono pull ns/pack --force. --> Edit, re-publish, re-pull.

One detail that might be useful to you: because the pack goes through the normal nono pull path, it lands in the pack store and is treated as a real registry pack at runtime.
The unsigned sentinel only gates the Sigstore bundle re-verification (the guard in verify_profile_packs); it does not touch $PACK_DIR expansion or source_pack, which key purely on the pack living in the store (resolve_store_pack_session_hooks). So the local-vs-registry divergence you hit ($PACK_DIR not expanding for local packs) does not appear on this path.

To be clear, this is not a replacement for your PR, it has more moving parts (watcher + HTTP server + publish/pull round-trip) than editing files in place, and yours is the better fit for pure authoring iteration.

Just sharing a second route in case it helps. 😀

Nice work on this, by the way. Gating the whole thing behind a compile-time flag with a loud runtime warning, so a production binary simply cannot load sideloaded packs, is a clean way to keep the dev ergonomics without weakening the default posture.

@intentionally-left-nil

Copy link
Copy Markdown
Contributor Author

@vjeantet As long as nono is comfortable shipping a config like yours, I think that's a useful path (and also it would remove downsides like needing to recompile).

As long as there's a dev flow to install from a local directory I'm happy!

intentionally-left-nil and others added 3 commits July 15, 2026 07:10
When developing a registry pack, it's not easy to test the behavior of
the pack, because nono requires the pack to have proper lockfiles and
attestation.

To solve for this developer case, without harming the security of
production nono users, this commit introduces a new compile-time build
flag called sideload, which is disabled for production builds.

When nono is compiled with sideload, it introduces a new CLI command,
nono sideload. This command copies the path into the correct registry
location and updates the lockfile like normal. Additionally, other
commands like nono run disable attestation

Signed-off-by: Anil Kulkarni <anil@terminal.space>
Signed-off-by: Anil Kulkarni <anil@terminal.space>
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.

Implement a local test mechanism for packs

3 participants