Skip to content

Lock & Sync

Every NSX app is reproducible through two files:

File Purpose
nsx.yml Intent — what modules the app needs and where they come from
nsx.lock Receipt — the exact commit + content hash of every vendored copy

nsx lock writes (or refreshes) nsx.lock. nsx sync makes the on-disk modules/ tree match nsx.lock exactly. Use nsx sync --frozen in CI to fail on drift instead of correcting it.

Stable and development dependency modes

The packaged stable registry is the release default. Its project and module revisions are expected to move to immutable version tags or full commit SHAs. The SDK defaults are pinned to immutable releases (nsx-ambiq-sdk@v5.2.23 and nsx-pmu-armv8m@v0.2.0). The packaged neuralspotx@main self-reference remains the single narrow temporary exception; adding another floating stable ref fails the registry policy test.

Internal target bring-up remains explicit and app-local. Use ordered registry.layers (workspace or inline) for shared overlays, then module_registry for the highest-precedence app override:

registry:
  layers:
    - packaged
    - workspace: ../bringup-registry.yaml

module_registry:
  projects:
    nsx-ambiq-sdk:
      local_path: ../nsx-ambiq-sdk

Relative local_path values in inline layers and module_registry are resolved from the app directory. In a workspace layer, they are resolved from the workspace overlay file's directory, so shared overlays remain portable.

An explicit local_path overrides the project's packaged git URL and produces a local lock entry with the source tree's content hash. For a branch or SHA override, set the project revision and each affected module revision so the module's requested constraint is unambiguous:

module_registry:
  projects:
    nsx-ambiq-sdk:
      revision: bringup/customer-board
  modules:
    nsx-ambiqsuite:
      project: nsx-ambiq-sdk
      revision: bringup/customer-board

Regardless of mode, nsx.lock records exact resolved content: git refs resolve to a commit SHA plus content hash, while local projects record a content hash. SBOM output preserves the lock's project, kind, requested constraint/tag, resolved commit (for git), and neuralspotx tool version. This makes branch and local development inputs visible in provenance without changing stable registry defaults.

The source: field

Each entry under modules: in nsx.yml may carry an optional source: field that tells NSX where the module's contents come from.

Form Meaning
omitted Registry default (git or packaged)
source: { path: <path> } Linked from an external directory; mirrored on each sync
source: { vendored: true } Committed inside this app; sync never touches it

source: omitted (registry default)

modules:
  - name: nsx-uart

NSX resolves the module against the packaged registry (or any module_registry.* override in nsx.yml). If the project entry has a url, it is git-cloned at the locked commit. If it points into the neuralspotx package, it is copied. Both cases are gitignored under modules/.gitignore because nsx sync re-acquires them.

source: { path: <p> } — linked module

modules:
  - name: my-driver
    source:
      path: ../../shared/my-driver

NSX treats the external path as the source of truth. nsx sync mirrors its contents into modules/my-driver/ (excluding .git/__pycache__), hashes the result, and stores the hash in nsx.lock. The vendored copy is gitignored; the source lives wherever you already source-control it.

source: { vendored: true } — committed in this app

modules:
  - name: my-aot-stub
    source:
      vendored: true

The directory under modules/my-aot-stub/ is owned by you and committed with the app. nsx sync will never write to it; the only thing recorded in nsx.lock is the content hash, so drift is still detectable.

The fastest way to add one is:

nsx module add my-aot-stub --vendored

That scaffolds a minimal nsx-module.yaml + CMakeLists.txt, appends the source: { vendored: true } entry to nsx.yml, regenerates modules/.gitignore (so the directory is kept in git), and refreshes nsx.lock.

Typical use cases:

  • AOT-generated modules
  • In-house drivers
  • Customer-private modules that should not be re-fetched
  • Snapshot of a third-party drop frozen for a release

Lock kinds

nsx.lock records each module under one of five kinds:

Kind Source Sync behaviour
git Registry git project Re-clone at the locked commit
packaged Shipped inside neuralspotx Re-copy from the package
local source: { path: } (linked) Mirror from external path, hash-verify
vendored source: { vendored: true } Hands-off; verify content hash only
unresolved Registry git, upstream offline Hash-verify only; cannot re-fetch

nsx outdated only operates on git modules — the others have no upstream constraint to compare against.

CI recipe

nsx lock --check            # fail if nsx.lock is stale relative to nsx.yml
nsx sync --frozen           # fail if modules/ drifts from nsx.lock
nsx outdated --exit-code    # fail if a git constraint has moved upstream

Together these guarantee that what was committed builds bit-for-bit identically to what CI builds.

nsx lock --check is read-only: it resolves the lock as nsx lock would and prints a structured diff (+ added, - removed, ~ changed) against the on-disk nsx.lock without writing anything. Exit code is non-zero on drift.

For machine-readable output, nsx outdated --json emits:

{
  "checked": [
    {"module": "...", "constraint": "main", "locked": "...", "upstream": "...", "status": "up-to-date", "url": "..."}
  ],
  "skipped": [{"module": "...", "reason": "..."}],
  "outdated_count": 0
}

Pipe through jq for status dashboards or PR-comment bots.