New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

@aihq/scan

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@aihq/scan

AIH Scanner evidence producer for AI Development Assurance.

Source
npmnpm
Version
0.2.4
Version published
Weekly downloads
21
-44.74%
Maintainers
1
Weekly downloads
 
Created
Source

AIH Scanner (@aihq/scan)

License: Apache-2.0

@aihq/scan captures and verifies bounded scanner evidence without deciding whether an organization should approve, install, or activate the scanned subject. The V2 path has one code-owned execution capability, cisco-oci-v1, which can run either the built-in Cisco identity or an organization-defined exact detector registration in a pinned Linux amd64 OCI image against an explicitly selected source closure. Registration does not load organization code into the Scanner process or grant governance authority.

Core governs. Scan produces evidence. Catalog provides AIH qualification. The organization provides authority.

Status

The promoted @aihq/scan stable train contains the V2 library, aih-scan CLI, strict detector-registration grammar, detached bundle format, Ed25519 DSSE signing, Linux amd64 OCI CI chain, Core organization-evidence projection, and bounded baseline-vet execution. The fixed aih-baseline-v1 profile emits canonical receipt and annex records and separates baseline-vet, baseline-sign, and baseline-verify. Projection and baseline evidence are transport only; neither qualifies, approves, admits, observes, installs, or activates a subject. Source state alone never proves publication; the live checks below establish exact custody. Public package custody is not organization evidence custody. Candidate versions are first published under npm next; only public installed acceptance plus separate authorization can promote the same bytes to latest.

The one-use bootstrap source and GitHub environment secret are absent. The protected environment is tag-only and secret-free. npm Trusted Publishing is bound to samartomar/aih-scan, workflow release.yml, environment npm-publish, and allows only npm publish; bypass tokens are disallowed and the old bootstrap token is revoked. The current workflow rejects npm token credentials and publishes through GitHub OIDC only. The one-use recovery workflow was removed after success; its terminal run and the original failed publication runs remain durable audit evidence. See VERSIONING.md, RELEASING.md, and CONTRIBUTING.md. The new package line starts at 0.1.0 to describe its current maturity; it does not inherit the frozen Core legacy package's historical major version.

The GitHub Actions example signs with a generated test-ephemeral key and uploads that public root beside the evidence. It proves the capture, signing, and verification mechanics. It is not an organization trust root or public qualification authority.

Install and verify the package boundary

Node.js 20 or newer is required.

npm ci --ignore-scripts
npm run typecheck
npm run lint
npm test
npm run test:cov
npm run build
npm pack

Install the resulting tarball into a disposable consumer:

npm install --save-dev /path/to/aihq-scan-X.Y.Z.tgz
npx aih-scan --help

The package exports only the V2 API from @aihq/scan. Internal V1 modules are implementation details and are not package export paths.

For an exact candidate, publication custody is proven only when every live registry and Release check below succeeds. Until full custody exists, at least one leg fails; after publication every leg must succeed. Source never decides the result:

version=X.Y.Z
npm view "@aihq/scan@$version" name version dist --json
npm install --save-exact "@aihq/scan@$version"
npm audit signatures
release_root="$(mktemp -d)"
gh release download "v-scan-$version" --repo samartomar/aih-scan --dir "$release_root"
release_sha="$(gh api "repos/samartomar/aih-scan/git/ref/tags/v-scan-$version" --jq .object.sha)"
(cd "$release_root" && sha256sum --strict --check SHA256SUMS.txt)
cosign verify-blob "$release_root/SHA256SUMS.txt" --bundle "$release_root/SHA256SUMS.txt.sigstore.json" --certificate-identity "https://github.com/samartomar/aih-scan/.github/workflows/release.yml@refs/tags/v-scan-$version" --certificate-oidc-issuer https://token.actions.githubusercontent.com
gh attestation verify "$release_root/aihq-scan-$version.tgz" --repo samartomar/aih-scan --bundle "$release_root/provenance.intoto.jsonl" --signer-workflow samartomar/aih-scan/.github/workflows/release.yml --source-ref "refs/tags/v-scan-$version" --source-digest "$release_sha" --deny-self-hosted-runners
npx --no-install aih-scan --help

The release workflow builds and smoke-installs the candidate in a read-only job. Its protected job verifies the workflow-artifact digest, original tarball digest, and packed identity without executing Scanner package code. It then binds npm provenance, a GitHub build attestation, an SPDX SBOM, the tarball checksum, and a keyless cosign checksum bundle to the exact tagged source. npm view "@aihq/scan@$version" is the live registry check; source review or a local tarball is not publication evidence. Exact historical release 0.1.1 required a bounded recovery after npm publication, so its checksum bundle has the historical recover-v-scan-0.1.1.yml@refs/heads/main identity while its provenance bundle remains the original tag-run build attestation. Do not use that recovery identity for a normal release or describe either signature as the other.

Evidence flow

Keep capture, signing, verification, and governance as separate phases:

  • A candidate job builds or obtains the exact registered scanner runtime and runs aih-scan capture against a disposable target. The selected registration names one code-owned adapter capability and binds the exact runtime, adapter, configuration, execution profile, platform, SBOM, provenance, and broker identities. It has no signing or repository-write authority.
  • A signing job reads the completed detached bundle and signs its exact candidate and annex identities. It does not run Docker or the candidate scanner.
  • An administrator verifies the DSSE signature, detached bundle, expected source and CI claims, validity window, replay state, and signer identity against roots supplied outside the evidence bundle.
  • For an organization signer and successful scan, the administrator may project those already verified facts into Core's exact canonical OrganizationEvidenceEnvelopeV1 contract.
  • An administrator uses Core's structured Workbench UI to generate and protect a PolicyBundle V2 file that binds the exact evidence and decision; an optional GitHub-attested V3 workflow remains available. Scanner output does not approve findings or grant installation, runtime projection, or activation authority.

Core baseline-vet boundary

Scanner owns the bounded analyzer-execution half of baseline vetting. Core still owns catalog selection, reuse, interpretation, finding dispositions, vendor-lock and ECC-preview assembly, qualification, and organization policy. Maintainer analyzer-offload runners provide execution capacity only; they grant no approval authority.

Core creates one canonical BaselineVetRequestV1 for an exact source and at most 100 components. The fixed aih-baseline-v1 profile requires native, SkillSpector, and Semgrep observations for every component and Cisco for every component declared as Skill content. Callers cannot weaken that floor or supply commands, image names, runtime paths, adapters, targets, approvals, or effects. Scanner acquires each required analyzer once per exact source, verifies the pinned SkillSpector image and bundled uv locks/versions, uses hardened Docker and lock-backed uv execution against the canonical PyPI index, re-observes every source/component digest after the run, and writes one canonical content-addressed receipt with detached annexes. The execution host needs Docker, Bubblewrap, uv, and root-provisioned Python 3.13 on Linux. The fixed Python runtime is rooted at /usr/local, with its standard library at /usr/local/lib/python3.13 and its executable exposed as /usr/bin/python3.13. The private analyzer snapshot preserves an exact relative source symlink only when every raw path segment resolves inside the source root to an already validated regular file or directory. Absolute, drive-relative, UNC, backslash, escaping, dangling, chained, excluded .git, and cyclic targets fail closed. Component hashing remains stricter: any symlink inside a selected component is rejected before analyzer execution. The Scanner runtime invokes the root-provisioned /usr/bin/docker, /usr/bin/bwrap, /usr/local/bin/uv, and /usr/bin/python3.13 paths directly; after clearing the host environment it adds only the fixed read-only standard-library directories to PYTHONPATH, so the isolated analyzer virtual environment remains active. It does not discover Python through the ambient PATH, HOME, PYTHONPATH, or uv environment variables. It uses Docker's local default context, and terminates analyzer process groups as one unit. Semgrep and Cisco run as PID 1 inside Bubblewrap user/PID/mount namespaces with an empty explicit environment, no host user-manager socket, nested user namespaces disabled, and a Scanner-owned runtime ceiling. Network is enabled only for locked, no-build wheel acquisition, where the hostile source is not mounted, the working directory is the bundled project, and PYTHONSAFEPATH=1; analyzer execution receives the source only inside a private network namespace. Scanner acquires the exact digest-addressed public SkillSpector image with an isolated empty Docker client configuration and allows uv to acquire only the distributions selected and hashed by the bundled lock; neither route accepts caller-provided registries, images, commands, or configuration.

npx aih-scan baseline-vet \
  --request /path/to/canonical-baseline-request.json \
  --source /path/to/exact-pinned-source \
  --output /path/to/new-baseline-observation-bundle

That first command produces an unsigned observation bundle, not portable trusted evidence. Keep the organization evidence key out of the analyzer process. In a separate protected step, sign the exact canonical request and bundle, then verify the signature, trust root, freshness, annexes, request binding, and replay state:

npx aih-scan baseline-sign \
  --request /path/to/canonical-baseline-request.json \
  --bundle /path/to/baseline-observation-bundle \
  --signer /protected/signer.json \
  --private-key /protected/ed25519-private.pem \
  --claims /protected/claims.json \
  --output /path/to/new-baseline-attestation.json

npx aih-scan baseline-verify \
  --evidence /path/to/baseline-attestation.json \
  --request /path/to/canonical-baseline-request.json \
  --bundle /path/to/baseline-observation-bundle \
  --roots /protected/trust-roots.json \
  --expected /protected/expected-signer-and-time.json \
  --seen /protected/seen-evidence-digests.json

The output directory must not exist. It contains only receipt.json and the exact annex/*.json files named and hashed by the receipt. Duplicate, missing, substituted, malformed, truncated, stale, drifted, unknown-profile, and replay-conflicting states fail closed. A single bundle therefore scales to 100 requested components without importing 100 evidence files or rerunning the same analyzer 100 times. Core must independently verify the request, receipt, annexes, source identity, analyzer floor, and replay state before normalizing facts. An unsigned receipt cannot satisfy the public completion contract. Scanner evidence remains preflight evidence throughout even after organization-key authentication; it never qualifies, approves, installs, or activates a component.

Organization-class baseline-sign custody is Linux-only. It opens the bundle and annex directories through protected /proc/self/fd descriptors, requires them to be owned by the signer account and not group/world-writable, and copies the bounded canonical bytes into process-private memory before signing. The optional baseline replay file has the exact shape {"digests":["<accepted-evidence-sha256>"],"receipts":[{"requestSha256":"<request-sha256>","receiptSha256":"<accepted-receipt-sha256>"}]}; both envelope replay and conflicting receipts for one request fail closed.

The repository workflow at .github/workflows/cisco-oci-equivalence.yml is the complete current capture example. It builds the pinned Cisco runtime twice for OCI-layout and local Docker identity equivalence, scans a generated public fixture offline, creates a complete bundle, signs it in a separate job, and verifies it in a third job.

Capture

npx aih-scan capture \
  --request /path/to/capture-request.json \
  --output /path/to/new-capture-bundle

--output must not already exist. A built-in Cisco request binds:

  • the canonical Cisco OCI layout and exact image/config digests;
  • the source root and selected relative closure paths;
  • analyzer, adapter, runtime, configuration, and execution-profile identities;
  • digest-bound SBOM and provenance annex files;
  • the broker identity.

An organization-defined request replaces the top-level runtime and broker fields with registration and detectorId. registration has the strict authoring shape below; every digest is 64 lowercase hexadecimal characters and the OCI reference must end in the same manifest digest:

{
  "protocol": "DetectorRegistrationV1",
  "registrations": [
    {
      "detector": {
        "detectorId": "detector.example.policy",
        "analyzerIdentity": "native.0123456789ab",
        "ociImage": {
          "reference": "local.invalid/aih-scan/cisco@sha256:<manifest-sha256>",
          "sha256": "<manifest-sha256>"
        },
        "adapter": {
          "identity": "adapter.0123456789ab",
          "sha256": "<adapter-sha256>"
        },
        "observationConfigurationSha256": "<configuration-sha256>",
        "executionProfileSha256": "<execution-profile-sha256>",
        "supportedPlatforms": [{ "os": "linux", "architecture": "amd64" }],
        "sbom": { "mediaType": "application/spdx+json", "sha256": "<sbom-sha256>" },
        "provenance": {
          "mediaType": "application/vnd.in-toto+json",
          "sha256": "<provenance-sha256>"
        }
      },
      "runtime": {
        "sourceReference": "local.invalid/aih-scan/cisco@sha256:<manifest-sha256>",
        "sourceSha256": "<manifest-sha256>",
        "configSha256": "<image-config-sha256>"
      },
      "adapterCapability": "cisco-oci-v1",
      "broker": { "identity": "broker.0123456789ab", "capability": "cisco-oci-v1" }
    }
  ]
}

The selected detectorId need not appear in an AIH-maintained catalog. The code-owned detector.cisco identity is reserved for the direct built-in path and cannot be claimed by an organization registration. The current adapter requires the selected image to be loaded under the canonical local execution alias shown above; the registration, OCI layout, loaded image, SBOM, and provenance still bind its exact organization-chosen bytes. Scanner does not fetch or execute an arbitrary registry reference. The registration is strict, canonically hashed, deterministically ordered, capped at 128 entries and 512 KiB, and rejects duplicate IDs, mutable OCI references, unsupported platforms, unknown fields, digest mismatches, and unknown adapter capabilities. The V2 candidate records the selected identity under the neutral scanner.detector field and binds the complete registration digest. It does not claim that every registered entry ran.

The only executable capability is the checked-in cisco-oci-v1 adapter. An organization may select its own exact compatible OCI detector runtime and evidence identities, but cannot supply JavaScript, a command line, a host path, or another adapter implementation for Scanner to execute. A new execution capability requires reviewed Scanner code and tests.

The capture command seals the source before and after execution and refuses a changed source, link/reparse escape, hard link, oversized input or output, timeout, truncated process output, nonzero scanner result, or incomplete container cleanup. It also safely re-reads the capture request after execution and before bundle creation, refusing changed canonical request bytes. Docker runs with no network, a read-only root filesystem, no added capabilities, no-new-privileges, a non-root user, bounded CPU, memory, process count, temporary storage, and output.

Sign

Signing requires a completed bundle, an Ed25519 private key, a public signer projection, and exact claims:

npx aih-scan sign \
  --bundle /path/to/capture-bundle \
  --signer /path/to/signer.json \
  --private-key /protected/path/to/ed25519-private.pem \
  --claims /path/to/claims.json \
  --output /path/to/new-evidence.json

signer.json:

{"class":"organization","identity":"example-admin","keyId":"ed25519:<sha256-of-SPKI-DER>"}

claims.json has the following closed shape:

{
  "repository": "owner/repository",
  "workflow": ".github/workflows/evidence.yml",
  "issuer": "https://token.actions.githubusercontent.com",
  "sourceRef": "refs/heads/main",
  "commit": "0123456789abcdef0123456789abcdef01234567",
  "environment": "evidence-signing",
  "runId": "123456789",
  "runAttempt": 1,
  "signedAt": "2026-08-22T17:00:00.000Z",
  "expiresAt": "2026-08-22T18:00:00.000Z"
}

The validity window must be positive and no longer than 24 hours. On POSIX, the CLI refuses a private-key file accessible by group or other users. Do not commit private keys. Windows ACL enforcement and hardware-backed/HSM signing are outside the current CLI contract.

Verify

npx aih-scan verify \
  --evidence /path/to/evidence.json \
  --bundle /path/to/capture-bundle \
  --roots /path/to/admin-roots.json \
  --expected /path/to/expected-policy.json \
  --seen /path/to/replay-identities.json

admin-roots.json is supplied independently by the administrator:

{
  "roots": [
    {
      "identity": "example-admin",
      "class": "organization",
      "keyId": "ed25519:<sha256-of-SPKI-DER>",
      "publicKeySpkiBase64": "<canonical-base64-SPKI-DER>"
    }
  ]
}

expected-policy.json repeats every signed claim exactly and adds:

{
  "now": "2026-08-22T17:30:00.000Z",
  "subjectSha256": "<exact-source-tree-sha256>",
  "signer": {
    "identity": "example-admin",
    "class": "organization",
    "keyId": "ed25519:<sha256-of-SPKI-DER>"
  }
}

The full file also contains all fields from claims.json; extra or missing fields are rejected. The optional replay file has the shape {"identities":["<previous-replay-identity>"]}. Persist an accepted replayIdentity in administrator-owned state before accepting another copy.

Successful verification prints canonical JSON facts. It does not print PASS, approval, a finding disposition, or an activation decision.

Project verified evidence for Core

After successful verification, write one canonical Core evidence envelope:

npx aih-scan project-core-evidence \
  --evidence /path/to/evidence.json \
  --bundle /path/to/capture-bundle \
  --roots /path/to/admin-roots.json \
  --expected /path/to/expected-policy.json \
  --seen /path/to/replay-identities.json \
  --subject-digest sha256:<exact-core-subject-digest> \
  --output /path/to/new-core-evidence.json

The packed @aihq/scan package exposes the same command surface through aih-scan project-core-evidence --help.

--seen is optional; all other options are required exactly once. The command repeats the complete V2 verification before writing anything. It accepts only an organization-class signer and a successful scan, and it refuses an existing output, linked output parent, malformed or mismatched evidence, stale validity, replay, incomplete annex custody, changed source, failed cleanup, or a failed or refused scan.

The caller supplies the exact canonical Core subject digest. Scanner binds that digest to the verified evidence, candidate, payload, source-seal, and annex identities, but it neither derives the Core subject nor decides whether the organization should associate that evidence with it. The output validity comes only from the signed scan claims. Its deterministic attestor binds the verified organization signer identity and key id. The stdout reports envelopeSha256, the raw output-file hash, and organizationEvidenceDigest, Core's domain-separated qualification digest. Enter organizationEvidenceDigest—not envelopeSha256—in Policy Workbench's organization-evidence digest field. The must-not-exist output file is the evidence handoff, and the caller must preserve its custody until a separate authorized Core decision binds that exact qualification digest.

Core still requires exact organization authority and an exact decision before the envelope can qualify anything. The default Enterprise path is the protected PolicyBundle V2 file generated by Core's Workbench and supplied through an absolute AIH_ORG_POLICY path outside the governed target. The administrator's OS or MDM must make that file read-only to the AIH process; optional GitHub-attested V3 authority remains available. A matching live observation or registered AIH-managed effect is a further boundary. Evidence projection alone is never effective state.

What the evidence means

Verification establishes that:

  • the DSSE signature is valid for the exact canonical in-toto payload under an administrator-supplied Ed25519 root;
  • the payload binds the detached candidate, source seals, selected detector, registration and code-owned adapter capability, runtime and configuration identities, platform, raw facts, coverage, annex digests, cleanup result, signer, and expected claims;
  • the detached candidate and all annex bytes are present and match their descriptors;
  • the evidence is current at the caller-supplied time and has not appeared in the supplied replay state.

Current SBOM and provenance annexes are labeled digest-bound-unverified. Broker enforcement is labeled unverified. GitHub claims are signer-asserted with provenance none; this package does not verify an OIDC token or GitHub certificate. An administrator must obtain signer roots and expected claims through an independent trusted process.

Limits

  • Registered execution capability: cisco-oci-v1 only. Organizations can bind their own exact compatible detector identities and OCI bytes; Scanner does not dynamically load arbitrary adapters.
  • Qualified capture platform: Linux amd64 only.
  • Source tree: at most 4,096 entries, 16 MiB per file, and 256 MiB total. The canonical SourceSealV2 record is also capped at 512 KiB. Selected files appear in both the complete file inventory and the selected-closure binding, so long paths or large selected closures can reach the canonical record cap before the entry or aggregate-byte limits.
  • Detached annex: at most 16 MiB per annex; the Cisco bundle requires raw, SBOM, and provenance annexes.
  • Scanner process: 120-second command timeout, 64 KiB per stdout/stderr stream, 16 MiB SARIF output, then bounded termination and cleanup.
  • The package emits evidence facts only. It does not provide catalog promotion, organization approval, installation, runtime/effect projection, revocation custody, or publication authority.
  • Exact public 0.2.1 has an immutable tag, npm registry signatures and provenance, a verified five-asset Release, a passing disposable install, and an independently observed least-privilege Trusted Publisher. The protected environment is tag-only and secret-free, bypass tokens are disallowed, and the old bootstrap token is revoked.

Organizations are not required to use an AIH-maintained catalog entry. The Core Strict V2 contract can bind organization-qualified evidence for an exact tool, skill, MCP server, package, or profile; catalog membership remains an optional qualification source rather than admission authority.

Development verification

npm run typecheck
npm run lint
npm test
npm run build
npm run verify:workflow-action-pins -- --online
npm audit --omit=dev
git diff --check

The packed compatibility proof additionally requires AIH_SCAN_CORE_SOURCE to be the filesystem path of a clean Core checkout whose HEAD is exactly 6130dd837b8e8bd41e999fb40733e0e460e69720. Its manifest still identifies as @aihq/core@0.1.1, but it is a post-0.1.1 compatibility fixture rather than the public package. Public @aihq/core@0.2.0 and immutable tag v-core-0.2.0 carry the released Workbench handoff from later source 0d63a9853bd51072a5108eee21013d5fb8a8472b; the cold lock preserves the exact schema bytes Scanner targets:

AIH_SCAN_CORE_SOURCE=/path/to/exact-clean-core-checkout \
  npm run verify:cold-core-evidence

The proof builds and packs that exact locked Core source, packs the current @aihq/scan source, installs both tarballs in disposable roots, captures a catalog-absent organization detector through the registered adapter boundary, signs and independently verifies the resulting V2 bundle, projects the evidence, validates the exact packaged Core schema, and uses the packed Core Workbench form to generate a protected policy file, and invokes packed Core's real policy resolve command. Without the protected file the same request refuses authority-unverified; with it, Core verifies the authority, accepts the exact envelope as organization-qualified, and stops honestly at observation-missing without an effect. The deterministic runner, generated organization-class key, and generated policy are disposable test mechanics—not human approval, public attestation, production authority, or a production effect.

Historical clean-machine acceptance used exact npm packages in a disposable installed root. The promoted @aihq/scan stable train carries bounded baseline-vet execution, safely preserves validated in-root relative source symlinks in private analyzer snapshots, and independently passes its release-custody checks. The complete cross-package acceptance is repeated after the corresponding Core release; it is not publication authority.

Never run AIH product behavior against this repository checkout. Tests exercise scanner behavior only against disposable fixture roots.

License

Apache-2.0. Scanner evidence and software are provided on an "AS IS" basis without organization approval, qualification, warranty, support, or effect authority.

FAQs

Package last updated on 31 Aug 2026

Related posts