From your license key to your first audit report

A clean, linear getting-started path for new NSAuditor AI Enterprise customers: install → activate → configure → scan → read the results. Ten steps, plus an optional Pro add-on and the Claude Desktop / MCP workflow. Everything runs on your machine, and every outbound path the product can take is enumerated in its published egress register, so the claim is one you can audit rather than one you have to trust.

Written for the current shipped product, EE 0.40.1, which requires CE ≥ 0.2.43 — install the trio in Step 2 and you get the matching pair. New in 0.34.0 and set up between Steps 6 and 7: exploit intelligence — CISA KEV + FIRST EPSS, which reorders the finding queue so a known-exploited MEDIUM outranks an unexploited CRITICAL. RFC 3161 trusted timestamping is opt-in via NSAUDITOR_TSA_URL — never a default — and each compliance artifact then carries a .tsr sidecar your auditor verifies offline with stock openssl, against a Time-Stamp Authority you choose, with none of our software in the path. New in 0.36.0: your report checks the suppression signatures it renders against recorded key material, for approvers whose registry entry carries the public key beside its fingerprint. A missing verdict means not checked, never failed — set the approvals up here, and Step 9 shows you how to read the verdict.

EE 0.40.1 · needs CE ≥ 0.2.45 28 enterprise plugins SOC 2 · HIPAA · NIST CSF 2.0 · PCI DSS · ISO 27001 · CIS v8 · GDPR Article 32 · NIST SP 800-171 Zero data exfiltration
WHAT YOU RECEIVED IN YOUR PURCHASE EMAIL

After buying NSAuditor AI Enterprise from nsauditor.com/ai/enterprise, your confirmation email contains two things you'll need below:

• your Enterprise license keyenterprise_eyJhbGciOiJFUzI1NiIs...
• your npm access tokennpm_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (read-only; @nsasoft/nsauditor-ai-ee is a private, restricted npm package).

BOUGHT THROUGH AWS MARKETPLACE?

First, register to receive your license key. AWS bills you, but it doesn't hand us your email — so after subscribing on the AWS Marketplace listing, go to nsauditor.com/ai/marketplace/register and enter your email, your AWS account ID, and your Agreement ID (agmt-…, shown in AWS Console → AWS Marketplace → Manage subscriptions). Your ES256-signed license key is then emailed to you. (The Usage Instructions on the listing carry the same steps.)

Your delivery channel is the Docker container image, not npm — the emailed key comes with the docker login / docker pull / docker run commands instead of an npm token, and the image already bundles the full platform (CE + Enterprise package).

Skip steps 0–3 and 5 below — run the container with your key instead: docker run --rm -e NSAUDITOR_LICENSE_KEY="enterprise_eyJ..." <image> license --status. Steps 4 (cloud credentials — pass them as -e vars or a mounted --env file), 6–7 (your first scan and --aws-region scoping), and 9 (reading the results) apply identically inside the container. Billing, renewal, and cancellation for Marketplace orders are managed in your AWS account (see the EULA's AWS Marketplace Addendum) — your license key still verifies fully offline.

The 10 steps

  1. Install Node.js (LTS)
  2. Authenticate with npm
  3. Install the platform + Enterprise package
  4. Activate your license
  5. Configure AI & cloud credentials
  6. Verify activation
  7. Run your first Enterprise scan
  8. Scope the AWS audit to regions
  9. Use NSAuditor AI with Claude Desktop (MCP)
  10. Read the results & troubleshoot

Enterprise add-on (new in EE 0.38.0): Establish evidence-pack authorship — compliance sign-pack / verify-pack

Isolated networks (new in EE 0.37.0): Install on an air-gapped host — carry the package and the CVE data in

Enterprise add-on (new in EE 0.36.0): Sign and verify suppression approvals

Pro add-on (new in EE 0.34.0): Set up exploit intelligence — CISA KEV + FIRST EPSS

0Install Node.js (LTS)

NSAuditor AI runs on Node.js 20 LTS or newer. Download the installer from the official Node.js Downloads page, choose the LTS build (not "Current" — LTS is the stable line), and run the installer that matches your OS (Windows .msi / macOS .pkg).

verify node
$ node --version   # v20.x or newer
$ npm --version

1Authenticate with npm

@nsasoft/nsauditor-ai-ee is a private (restricted) package. Configure the read-token from your license email so npm can download it.

npm auth
# Global: set the auth token for the npm registry
$ npm config set //registry.npmjs.org/:_authToken npm_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# Or, scoped to your home ~/.npmrc
$ echo "//registry.npmjs.org/:_authToken=npm_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" >> ~/.npmrc

2Install the platform + Enterprise package

Install all three packages: the free Community Edition runtime, the agent skill (for Claude Desktop / Claude Code), and the Enterprise package — your 28 Enterprise plugins (27 cloud auditors plus 1023 Zero Trust Assessment, which scores a network-host scan) and seven compliance frameworks.

install the trio
$ npm install -g nsauditor-ai@latest \
                  nsauditor-ai-agent-skill@latest \
                  @nsasoft/nsauditor-ai-ee@latest
⚠ UPGRADING FROM AN EARLIER RELEASE — THREE THINGS TO DO

1 · Re-check any suppression rule that matches the evidence-gap finding by title. EE 0.32.9 changed the title of the evidence-gap finding. A suppression rule written against the old text stops matching, so those findings resurface rather than stay hidden. That is the safe direction — nothing is silently suppressed — but the change itself is silent: no error, no warning, the rule simply goes inert. Re-point any rule keyed to the old wording.

2 · Re-scan; do not re-process a scan captured before 0.32.9. An archived scan taken on an earlier build was captured before the fail-closed evidence-gap behavior existed, so re-processing it cannot reconstruct what a cloud that failed to start would now disclose. The engine warns when it re-processes such a scan. Run the scan again against the live account instead of re-rendering the old capture.

Why it matters: before 0.32.9, when a cloud plugin could not start — its optional SDK absent, its credentials unusable — it refused to report, and that refusal evaporated one layer up. The compliance report came out byte-identical to one where the scanner ran and found nothing: ten controls read PASS, no violation, no warning. The dangerous shape is ordinary — AWS and GCP scan for real, Azure's SDK is absent, and the combined pack reads as a clean three-cloud audit. Now every in-scope control of a cloud that could not be scanned carries a fail-closed evidence gap. Expect gaps where you previously saw passes; that is the corrected reading.

3 · Expect four more out-of-scope rows in your next SOC 2 report. EE 0.32.11 enumerates the SOC 2 matrix in full at 10 covered / 4 partial / 37 out of scope = 51. CC7.4, CC7.5, A1.1 and A1.3 had appeared nowhere in the mapping data — silently non-enumerated, which is not the same thing as out-of-scope-with-a-reason. Each now renders with an architectural reason and the operator-side evidence that closes it. No control changed status and no routing changed: covered stays 10, partial stays 4, and every existing control-id membership is identical. The other six matrices are unchanged.

3Activate your license

One command — no shell-rc edits. The CLI verifies the JWT signature before persisting, and stores the key in the macOS Keychain (or ~/.nsauditor/.env mode 0600 on Linux/Windows).

license install
$ nsauditor-ai license install enterprise_eyJhbGciOiJFUzI1NiIs...
✓ Enterprise license installed
  Stored at: macOS Keychain (service=nsauditor-ai)
  Org: you@example.com   Seats: 5
  Expires: 2027-04-04T...
CI/CD ALTERNATIVE

An environment variable also works and takes highest priority in the multi-source loader: export NSAUDITOR_LICENSE_KEY=enterprise_eyJ...

⚠ macOS RESET GOTCHA

If you ever hit a license_id_mismatch on macOS, clear both ~/.nsauditor/license-state.json and the Keychain entry NSAUDITOR_LICENSE_ID (Keychain wins on read). On Linux/Windows it is file-only.

4Configure AI & cloud credentials

Create a dotenv file with your AI provider key and the cloud credentials for the account(s) you want to audit. Everything stays on your machine — nothing is sent to NSAuditor. Keep one .env per account (e.g. ~/envs/dev.env, ~/envs/prod.env) and select it per scan with --env (Step 6).

~/envs/prod.env
# ── AI provider (used for the optional AI analysis layer) ──
AI_ENABLED=true
AI_PROVIDER=claude                       # openai | claude | ollama
ANTHROPIC_API_KEY=sk-ant-...             # or keychain:ANTHROPIC_API_KEY on macOS — your key, stays local
ANTHROPIC_MODEL=claude-sonnet-4-6

# ── Which cloud(s) this file audits ──
CLOUD_PROVIDER=aws,gcp,azure

# ── AWS ──
AWS_ACCOUNT_ID=
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1

# ── Azure ──
AZURE_CLIENT_ID=
AZURE_CLIENT_SECRET=
AZURE_TENANT_ID=
AZURE_SUBSCRIPTION_ID=

# ── GCP (Application Default Credentials) ──
GOOGLE_CLOUD_NUMBER=
GOOGLE_CLOUD_PROJECT_ID=
GOOGLE_APPLICATION_CREDENTIALS=/Users/myuser/.config/gcloud/application_default_credentials.json
GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=your@project-id
⚠ USE READ-ONLY CREDENTIALS

NSAuditor AI performs read-only auditing — it reads configuration and metadata only and never needs create, write, modify, or delete permissions. Always supply read-only, least-privilege credentials for the account(s) you audit:

  • AWS — attach the ReadOnlyAccess or SecurityAudit managed policy
  • Azure — assign the built-in Reader role
  • GCP — grant roles/viewer

Read-only scoping guarantees an audit cannot change your environment. Supplying credentials with write or other state-changing permissions is unnecessary and is entirely at your own risk. You are responsible for provisioning, scoping, rotating, and revoking these credentials — see EULA §5.5.

5Verify activation

license status
$ nsauditor-ai license --status
✓ Enterprise license active | Org: you@example.com | Seats: 5 | Expires: 2027-04-04

6Run your first Enterprise scan

The headline use case is a cloud audit: point NSAuditor at a cloud account and get one evidence pack mapped across the compliance frameworks you ask for.

cloud audit recipes
# Scan ALL THREE clouds in one run — comma-separated sentinels (CE 0.2.17+).
# --plugins all auto-scopes each cloud to only its plugins; one out/ subfolder per cloud.
$ nsauditor-ai scan --host aws,gcp,azure --env ~/envs/clouds.env --plugins all --compliance all --out out

# …or one cloud at a time, one dotenv file per account (credentials load for THIS scan only)
$ nsauditor-ai scan --host aws --env ~/envs/dev.env     --plugins all --compliance soc2 --out out/aws-dev
$ nsauditor-ai scan --host aws --env ~/envs/prod.env    --plugins all --compliance soc2 --out out/aws-prod

# Or a named profile from ~/.aws/credentials (no .env needed)
$ nsauditor-ai scan --host aws --aws-profile prod --plugins all --compliance soc2 --out out/aws-prod

# GCP / Azure per-account via --env (key-file path / service-principal vars live in the file)
$ nsauditor-ai scan --host gcp   --env ~/envs/gcp-prod.env   --plugins 1021 --compliance soc2,cis-v8 --out out/gcp-prod
$ nsauditor-ai scan --host azure --env ~/envs/azure-prod.env --plugins 1022 --compliance soc2        --out out/azure-prod
A directory listing of the audit-evidence output files NSAuditor AI EE writes per scan — per-framework reports plus SHA-256 chain-of-custody sidecars.
A sample of the audit-evidence output files a scan produces.

Pro: set up exploit intelligence CISA KEV + FIRST EPSS

On the Pro tier and above (every Enterprise license includes it), findings that carry CVEs are joined by CVE ID against a local CISA KEV catalog and a local FIRST EPSS scores file, banded KNOWN_EXPLOITED / ELEVATED / BASELINE, and the finding queue is reordered exploit-first — a KEV-listed MEDIUM outranks an unexploited CRITICAL, with the reason attached to every promoted finding: the KEV flag, the EPSS score with its percentile, the matched CVE ids, and the store's own as-of date. It is off until you configure it, and no feed data ships with the product — both stores are public files you download and refresh yourself.

exploit intelligence setup
# 1 · Download the two public feeds (no account needed for either)
$ mkdir -p ~/.nsauditor/feeds
$ curl -fsSL https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json -o ~/.nsauditor/feeds/kev.json
$ curl -fsSL https://epss.empiricalsecurity.com/epss_scores-current.csv.gz | gunzip > ~/.nsauditor/feeds/epss.csv

# 2 · Point the scanner at them (add both lines to your shell profile to persist)
$ export NSAUDITOR_EXPLOIT_KEV_STORE=~/.nsauditor/feeds/kev.json
$ export NSAUDITOR_EXPLOIT_EPSS_STORE=~/.nsauditor/feeds/epss.csv

# 3 · Scan as usual — findings that carry CVEs now arrive banded and ordered exploit-first
$ nsauditor-ai scan --host 10.0.0.0/24 --plugins all --out out/lan

Enterprise: sign and verify suppression approvals signed approvals

A suppression is you telling the report that a finding is an accepted risk or a false positive. Suppressions without a signature are first-class and always have been. What 0.36.0 adds is that every compliance report cryptographically verifies each suppression signature it renders, for approvers whose registry entry carries key material — so an auditor can check that an approval came from the person it names, instead of taking a string in a file at its word. This is off until you set it up, it is CLI-only (the MCP surface does not reach these commands), and the private key never leaves your machine. Step 9 shows you how to read the verdict this produces.

signed approval setup
# 1 · Create an approval keypair. Private half is written 0600 and stays here.
#     It prints an identity-registry member you can paste as-is.
$ nsauditor-ai compliance keygen --key ~/.nsauditor/approver.pem \
    --approver "Ann Approver" --email ann@example.com --role CISO --team Security

# 2 · Paste that member into your identity registry and point the scanner at it.
#     KEEP THE publicKey FIELD — the fingerprint alone identifies an approver, but only
#     the key material lets a report CHECK their signature.
$ export NSAUDITOR_IDENTITY_REGISTRY=~/.nsauditor/identity_registry.json

# 3 · Record an approval. It is signed because NSAUDITOR_SIGNING_KEY names your key.
$ export NSAUDITOR_SIGNING_KEY=~/.nsauditor/approver.pem
$ nsauditor-ai compliance suppress --suppressions ./audit-out/suppressions.json \
    --source auth_agent --title-pattern "SSH password authentication enabled" \
    --status accepted_risk --rationale "WAF compensating control at the perimeter" \
    --approver "Ann Approver" --attestation-level approver
#   → wrote ./audit-out/suppressions.json — supp-ff78d65c-b96 (accepted_risk)
#       signed: ed25519/local at level approver, checked for approvers whose registry entry carries key material

# 4 · Scan. The approval is picked up and its signature checked, for approvers whose registry entry carries key material.
$ nsauditor-ai scan --host aws --compliance soc2 --out ./audit-out

# Review every approval with expiry status, or re-approve before expiry:
$ nsauditor-ai compliance review --history ./audit-out
$ nsauditor-ai compliance renew --suppressions ./audit-out/suppressions.json \
    --id supp-ff78d65c-b96 --rationale "quarterly re-review" --approver "Ann Approver"

Enterprise: sign and verify the evidence pack compliance sign-pack / verify-pack

A pack already carried SHA-256 sidecars, which prove bytes were not altered. Neither says who produced it. compliance sign-pack signs the chain-of-custody envelope with an operator-held Ed25519 key at an approval station, so the scanning fleet stays keyless. CLI-only, and off until you set it up.

Scope, because it decides what the signature is worth to an assessor. A verified signature proves the holder of a key asserted authorship of one framework’s envelope and the four artifacts it enumerates — not the directory, and not the pack. The key is yours, so authorship is relative to your own key custody and is never a vendor attestation. It is not proof the scan ran, and not proof the findings are true.

1 · Generate the key, outside any container

# the operator-held signing key — generated outside any container, never on a scanner host
openssl genpkey -algorithm ed25519 -out nsauditor-signing.pem
chmod 600 nsauditor-signing.pem
openssl pkey -in nsauditor-signing.pem -pubout -out nsauditor-signing.pub

# the registry keys on the SPKI-DER fingerprint — first 24 hex of its SHA-256
echo "sha256:$(openssl pkey -pubin -in nsauditor-signing.pub -outform DER \
  | openssl dgst -sha256 -binary | xxd -p -c 256 | cut -c1-24)"

2 · Register the approver

Copy the shipped template (data/compliance/identity_registry.json) and add the fingerprint, the public key and a bounded validity window. Rotation is re-registering with a new window, and that window is what turns signed_after_revocation / signed_outside_validity into audit evidence.

{
  "schemaVersion": 1,
  "members": [{
    "displayName": "Release Signing Authority",
    "email": "security@example.com",
    "role": "Release Signing Authority",
    "publicKeyId": "sha256:975a2fc435697025c2765ed7",
    "publicKey": "-----BEGIN PUBLIC KEY-----\n…\n-----END PUBLIC KEY-----\n",
    "approverFor": ["soc2"],
    "attestationLevel": "approver",
    "validFrom": "2026-01-01T00:00:00.000Z",
    "validUntil": "2027-12-31T23:59:59.000Z",
    "revokedAt": null
  }]
}

⚠️ Malformed shapes are refused at load, each with exit 2 — “could not measure” rather than a verdict: a missing schemaVersion, a member without email, a member without an approverFor array. Key material under any name other than publicKey ends the same way by a different route: the registry loads, the member counts as having no key material, and the run reports that nothing was checked — exit 2 again. Exit 2 is never a pass.

3 · Sign the envelope

NSAUDITOR_SIGNING_KEY=./nsauditor-signing.pem \
nsauditor-ai compliance sign-pack \
  --manifest ./out/<scan>/scan_chain_of_custody_soc2.json \
  --approver "Release Signing Authority" \
  --registry ./identity_registry.json

Two files are written beside the pack: scan_pack_signature_soc2.json (the record) and scan_pack_signature_soc2.payload.jsonthe exact bytes that were signed. The payload ships so an auditor never has to reproduce a canonicalisation.

4 · Verify — two arms, and they answer different questions

# through the identity registry — checks WHO, plus revocation and validity AS AT SIGNING TIME
nsauditor-ai compliance verify-pack --manifest ./out/<scan>/scan_chain_of_custody_soc2.json \
  --registry ./identity_registry.json

# with a bare public key — verifies the SIGNATURE only, and says so in its own output
nsauditor-ai compliance verify-pack --manifest ./out/<scan>/scan_chain_of_custody_soc2.json \
  --public-key ./nsauditor-signing.pub

Supplying both is refused with exit 2, deliberately: a silent precedence rule would let the output name an anchor that was never consulted. Exit 0 verified · 1 a violation · 2 the run could not measure — and the third is never a failure.

A clean run prints the scope bound too, on success as well as on failure:

identity:  OK — resolved through the identity registry … revocation and validity checked as at signing time.
signature: VERIFIED — soc2 / <scanId>, identity model not declared
artifacts: 4 of 4 re-hashed and matching

The trailing identity model not declared is expected, not a warning about your setup: the signature record itself asserts no attestation level, and the verifier will not promote a registry attribute into a claim the record never made — who signed comes from the identity: line above it.

5 · Why the verifier re-hashes the artifacts

Because a signature alone would authenticate a manifest whose artifact claims nothing had ever checked. Edit one byte of a covered report — without changing its length — and you get:

signature: VERIFIED          ← the signature is genuinely fine
artifacts: ARTIFACT_HASH_MISMATCH — artifact(s) do not match their recorded sha256: scan_compliance_soc2.md
exit 1

A verifier that stopped at the signature would have called that pack clean. Tamper with the manifest instead and the verdict is MANIFEST_DIGEST_MISMATCH, with both digests printed — and it accuses nobody, because a manifest legitimately regenerated after signing hashes differently by construction.

6 · What an auditor does, with openssl and nothing of ours

# 1 — read the digest from the PAYLOAD, never from the record (the record is unauthenticated)
jq -r .manifestSha256 scan_pack_signature_soc2.payload.json

# 2 — hash the manifest on disk and compare
shasum -a 256 scan_chain_of_custody_soc2.json

# 3 — verify the signature over the payload bytes
jq -r .value scan_pack_signature_soc2.json | base64 -d > sig.bin
openssl pkeyutl -verify -pubin -inkey nsauditor-signing.pub -rawin \
  -in scan_pack_signature_soc2.payload.json -sigfile sig.bin
# → Signature Verified Successfully

Step 1 reads the payload, not the record, and that ordering is the point: an attacker who substitutes the manifest could also rewrite the digest it is compared against if you read the record.

7Scope the AWS audit to regions --aws-region

By default an AWS scan covers a single region (AWS_REGION, else us-east-1). The --aws-region <one|csv|all> flag controls which regions the regional auditors cover — and each now audits every in-scope region, with genuine per-region fan-out (security groups, EC2, RDS, KMS, Lambda, Secrets Manager, DynamoDB, CodePipeline/CodeBuild, Backup, SQS/SNS, VPC endpoints, ElastiCache, SES, Inspector/GuardDuty, and CloudTrail).

--aws-region <one|csv|all>
# A single region
$ nsauditor-ai scan --host aws --plugins all --compliance soc2 --aws-region us-east-1

# A comma-separated list of regions
$ nsauditor-ai scan --host aws --plugins all --compliance soc2 \
    --aws-region us-east-1,eu-west-1,ap-southeast-2

# Every region enabled on the account (DescribeRegions; static-list fallback on AccessDenied)
$ nsauditor-ai scan --host aws --plugins all --compliance soc2 --aws-region all

What a scoped run looks like, end to end:

per-region fan-out
$ nsauditor-ai scan --host aws --plugins all --compliance soc2 \
    --aws-region us-east-1,eu-west-1,ap-southeast-2 --out out/aws-prod

[region] scope = flag-list · 3 regions requested → resolving…
[region] scanning us-east-1, eu-west-1, ap-southeast-2  (regional plugins fan out per-region)
[plugin] AWS EC2 SG Perimeter Auditor (1170) ......... 3 regions · 5 findings
[plugin] AWS RDS Auditor (1140) ...................... 3 regions · 2 findings
[plugin] AWS IAM Deep Auditor (1030) ................. global · audited once
[plugin] AWS S3 Security Auditor (1020) .............. per-bucket region resolved · 6 findings
✓ scanScope: regionsScanned=[us-east-1, eu-west-1, ap-southeast-2]  regionsWithError=[]
ℹ️ THE INCOMPLETE-COVERAGE ADVISORY

> Incomplete region coverage — 16 enabled region(s) not scanned (eu-west-1, ap-south-1, …). Re-run with --aws-region all (or set AWS_REGION) for full coverage.

8Use NSAuditor AI with Claude Desktop (MCP)

Run audits conversationally from Claude Desktop — "Audit my AWS account" — via the MCP server bundled with the package.

1 · Add the skill

In Claude Desktop go to Customize → Skills, click +, choose Create skill → Upload a skill, and upload SKILL.md from the @nsasoft/nsauditor-ai-ee package (SKILL.md on GitHub).

2 · Configure Claude Desktop environment variables

macOS example, using Keychain references:

Claude Desktop · MCP server env
NSA_ENV_FILE=~/envs/dev.env
MCP_TIMEOUT=180000
AI_PROVIDER=claude
ANTHROPIC_API_KEY=keychain:ANTHROPIC_API_KEY
NSA_ALLOW_ALL_HOSTS=1
PLUGIN_TIMEOUT_MS=90000
CLOUD_PLUGIN_TIMEOUT_MS=45000
NSA_MCP_AUTH_KEY=keychain:NSA_MCP_AUTH_KEY
NSAUDITOR_LICENSE_KEY=keychain:NSAUDITOR_LICENSE_KEY
⚠ TIMEOUT TUNING

Claude Desktop enforces a hard ~60s per-tool-call limit that MCP_TIMEOUT does not extend (that knob only governs server startup). Keep CLOUD_PLUGIN_TIMEOUT_MS under ~60s (e.g. 45000) so a slow plugin returns partial-with-disclosure rather than a hard timeout.

3 · Ask Claude to audit

Say "Audit my AWS account" (one cloud) or "Audit my AWS and Azure accounts" (several). For region scoping, the MCP scan_cloud tool takes the same scope as a regions argument:

A snippet of a Claude Desktop response after asking it to audit an AWS account — surfacing per-severity finding counts and the audited region scope.
A snippet from a Claude Desktop AWS-account audit response.

8bInstall on an air-gapped host

Everything above assumes the host can reach a registry. If yours cannot, the install and the vulnerability data both have to be carried in. One condition travels with this path: the bundle is a restricted distribution rather than a public npm install. It is not architecture-restricted. Both arches are exercised for this release rather than inherited from an earlier one: native aarch64, and linux/amd64 in a container run with --network none — no interface, and DNS does not resolve. (The published container image is amd64 only — a different delivery vehicle from this bundle.)

1 · On a connected host — get the bundle

The air-gap bundle is a separate, restricted package carrying dependency-complete tarballs for both editions, an install script and SHA-256 checksums. Authenticate with the download token from your purchase email, then fetch it:

npm pack @nsasoft/nsauditor-ai-ee-airgap@0.39.0
tar xzf nsasoft-nsauditor-ai-ee-airgap-0.39.0.tgz

2 · Carry it across, then install offline

Copy the extracted directory to the isolated host and run its script. It verifies the carried checksums before installing anything, installs Community Edition first (Enterprise declares a peer dependency on it), and forces every install offline:

./install.sh
nsauditor-ai license --plugins

A host with no sha256sum or shasum is refused rather than installed onto — unverified bytes are not installed silently.

What is checked before this bundle ships. Each release re-builds the bundle and runs a delivery gate against those exact tarballs: it asserts that every declared production dependency resolves from the carried closure (43 of 43 for EE 0.40.1), that the plugin census loads (28 Enterprise plugins), that a store-backed scan returns findings from carried CVE data, and that the run makes zero network attempts. The gate never reads npm’s exit status, because that is measured non-evidence here: every production dependency is an optionalDependency, and a NIC-down install of the unvendored package exits 0 while installing almost nothing. Verify the delivery, not the installer.

2b · Verify the delivery yourself — not the installer

The installer’s exit code is the one thing you should not trust here, and the reason is structural rather than a bug: every production dependency is an optionalDependency, and npm skips an unfetchable optional silently. On an isolated host, an install that fetched nothing still exits 0 and still prints a cheerful summary line. Two commands tell you what actually landed:

# 1. every declared production dependency resolves from the carried closure
node -e "const p=require('@nsasoft/nsauditor-ai-ee/package.json');
const d=Object.keys(p.dependencies||{}).concat(Object.keys(p.optionalDependencies||{}));
let ok=0; for (const m of d) { try { require.resolve(m); ok++; } catch {} }
console.log(ok + '/' + d.length + ' resolved');"

# 2. the plugin census loads, and the Enterprise tier is active
nsauditor-ai license --plugins

For EE 0.40.1 the first command must print 43/43, and the second must report the Enterprise plugins active (28 Enterprise, 55 in total). A partial number is a failed delivery that npm reported as a success. As a deliberate control, the same procedure run against the ordinary (non-bundled) package on an isolated host resolves 0 of 43 — while npm still exits 0.

3 · Carry the vulnerability data

No CVE, KEV or EPSS data ships with the product. On a connected host, download the NVD feeds you want and bundle them; optionally carry your own CISA KEV catalogue and FIRST EPSS scores in the same archive:

nsauditor-ai feed bundle --from ./nvd-downloads --out feeds.json.gz \
  --kev ./known_exploited_vulnerabilities.json \
  --epss ./epss_scores.csv.gz

Carry feeds.json.gz across and import it on the isolated host:

# imports the feeds you downloaded, from a bundle you carried — restricted distribution
nsauditor-ai feed import --file feeds.json.gz --extras-dir ./stores

Import prints the environment variables to set for any carried KEV/EPSS files, and names why it skipped records. About a quarter of a real NVD year file is skipped by design — withdrawn CVEs and entries with no CPE match data. That is not data loss. Malformed records are a different signal and mean re-download.

4 · Scan offline

export NSAUDITOR_OFFLINE_ONLY=1
export NSAUDITOR_EXPLOIT_KEV_STORE=./stores/known_exploited_vulnerabilities.json
export NSAUDITOR_EXPLOIT_EPSS_STORE=./stores/epss_scores.csv.gz
nsauditor-ai scan --host aws --env ./org.env --compliance soc2 --out ./out

NSAUDITOR_OFFLINE_ONLY=1 refuses to start if an outbound path is also configured, which rules out the opt-in RFC 3161 timestamping path in the enclave — that setting makes an outbound call to the authority you name, and has no default. A store that cannot answer emits a coverage gap rather than a silent clean.

⚠️ A carried bundle is integrity-checked, not authenticated. The recorded SHA-256 detects a file altered in transit, but it travels inside the archive it covers, so it cannot establish who built it. Treat a bundle as you would any media crossing into an enclave: obtain it over a channel you trust, and check it against the checksums you were given separately.

The bundle's dependency closure is as of its build date; each release rebuilds and re-audits it, and enclaves update by carrying the new bundle. One optional dependency (mdns) is a native module that cannot compile without network access — it is not required, and mDNS discovery falls back to a pure-JavaScript implementation.

9Read the results & troubleshoot