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)¶
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¶
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¶
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:
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.