Equinox Platform 9.0 · Supply Chain Security

SBOM — Software Bill of Materials

Platform-wide supply chain transparency tooling: every service generates a machine-readable inventory of its dependencies for security auditing, license compliance, and regulatory reporting.

Summary: Platform-wide supply chain transparency tooling for the Equinox Platform 9.0. Each service generates a CycloneDX 1.5 SBOM document that inventories every npm dependency, cloud integration, DevOps component, test framework, and custom library — enriched with build metadata (git commit, branch, Node.js version) — producing a machine-readable artifact for security auditing, license compliance, and regulatory reporting.

1. Overview

A Software Bill of Materials (SBOM) is a complete, machine-readable inventory of every open-source and third-party component inside a piece of software — the equivalent of an ingredients list on a food label.

The Equinox Platform implements SBOM generation as a per-service npm script (npm run sbom:generate). The script uses the industry-standard CycloneDX format (spec version 1.5), scans the service’s node_modules and package.json, and writes an enriched JSON artifact to a local sbom/ directory. Two variants of the script exist:

  • Basic variant — used by eq-auth-service (and similar). Generates a CycloneDX document and appends a small set of build/runtime metadata properties.
  • Enhanced variant — used by eq-inventory-service (and similar). Adds a full sbomConfig declaration block that maps every backend framework, database driver, cloud SDK, custom library, DevOps component, test framework, third-party integration, and microservice endpoint. It also auto-detects AWS CDK resources from the cdk/ directory and merges security/AI/MCP status metadata.

The generated file is consumed by security scanners, audit tools (e.g. Snyk, OWASP Dependency-Check), license-compliance pipelines, and any procurement/regulatory process that requires a full supply chain manifest.

Business Value

Capability Outcome
Complete dependency inventory Every npm package (direct + transitive) listed with version — no hidden dependencies
License compliance Detect GPL/AGPL dependencies before they reach production
Vulnerability alerting Feed to Snyk / OWASP Dependency-Check / npm audit to catch CVEs in real time
Regulatory compliance Satisfies US Executive Order 14028, EU Cyber Resilience Act SBOM mandates for enterprise/government buyers
Build provenance Each artifact is stamped with git commit SHA, branch, timestamp, and Node.js version
Supply chain transparency Procurement teams can verify exactly what software runs in production
Multi-layer tracking Not just npm — CDK infrastructure, test frameworks, CI/CD pipelines, and cloud services also catalogued

Who Uses It

Persona Interaction
Platform / DevOps engineer Runs npm run sbom:generate locally or as part of a release pipeline to produce the artifact
Security team Feeds the sbom/ JSON into Snyk, OWASP Dependency-Check, or an internal SBOM registry to scan for CVEs and license violations
Compliance / audit team Attaches the SBOM artifact to release records for regulatory evidence (EO 14028, CRA, SOC 2)
Procurement / enterprise buyer Reviews SBOM to verify third-party components before approving software procurement
PIM service CI The only service with an additional npm run sbom:validate step — validates the generated artifact against schema
  • CycloneDX specification: https://cyclonedx.org/specification/overview/
  • cdxgen tool: https://github.com/CycloneDX/cdxgen
  • SBOM output location: <service-root>/sbom/
  • Versioned filename: <pkg-name>-<version>-cyclonedx.json
  • Always-current copy: <pkg-name>-latest-cyclonedx.json

2. Core Features

# Feature Description Applies to
1 CycloneDX 1.5 generation Produces a fully spec-compliant CycloneDX JSON SBOM via @cyclonedx/cdxgen scanning all npm dependencies All services
2 Build provenance metadata Stamps every SBOM with git commit SHA, branch, ISO timestamp, Node.js version, service type, and service domain All services
3 Versioned + latest output Writes two files: <name>-<version>-cyclonedx.json (immutable archive) and <name>-latest-cyclonedx.json (always-current pointer) All services
4 Structured component declaration Enhanced variant maps 10 categories of components explicitly in sbomConfig: backend, devops, QE, integrations, microservices, admin, security, AI, MCP, detected resources eq-inventory-service and enhanced variants
5 CDK resource auto-detection Scans ./cdk/*.ts / *.js files and detects Lambda functions, API Gateway, RDS, and EventBridge declarations automatically Enhanced variant
6 API endpoint tracking Extracts and records all documented API endpoint paths into SBOM metadata Enhanced variant
7 Security posture metadata Records vulnerability scanning tools (npm audit, OWASP, Snyk), static analysis tool (SonarQube), and security header config (Helmet/CSP/CORS) Enhanced variant
8 AI / MCP status tracking Documents whether AI integrations and MCP server integrations are implemented or planned Enhanced variant
9 SBOM validation eq-pim-service additionally runs npm run sbom:validate to verify the generated artifact against the CycloneDX schema eq-pim-service only
10 In-memory / offline safe No network calls during generation — all data comes from local node_modules, package.json, and the cdk/ directory All services

3. How It Works — Generation Pipeline

npm run sbom:generate
        │
        ▼
  Read package.json
  (name, version, description, author)
        │
        ▼
  Read git metadata
  (git rev-parse --short HEAD → commit)
  (git rev-parse --abbrev-ref HEAD → branch)
        │
        ▼
  Create ./sbom/ directory (recursive, idempotent)
        │
        ▼
  Run @cyclonedx/cdxgen
  (-t js --spec-version 1.5
   --author "Equinox Platform Team"
   --supplier "Equinox"
   -o ./sbom/<name>-<version>-cyclonedx.json)
        │
        ▼
  Parse generated JSON
        │
        ├── [Basic variant stops here — skip to Metadata Enrichment]
        │
        ├── [Enhanced variant — auto-detect phase]
        │       detectCDKResources()
        │         └── scan ./cdk/*.ts / *.js for:
        │               lambda.Function → { type: "lambda" }
        │               apigateway       → { type: "api-gateway" }
        │               rds              → { type: "rds" }
        │               eventbridge      → { type: "eventbridge" }
        │       detectAPIEndpoints()
        │         └── read sbomConfig.microservices.services[].endpoints
        │
        ├── [Enhanced variant — component injection phase]
        │       Append to sbomData.components[]:
        │         1. Backend frameworks (type: library, group: Backend-Components, layer: Application Framework)
        │         2. Database drivers  (type: library, layer: Data Access)
        │         3. Cloud SDKs        (type: library, layer: Cloud SDK)
        │         4. Custom libraries  (type: library, layer: custom)
        │         5. Compute platform  (type: platform, layer: Compute)
        │         6. Infrastructure    (type: application, layer: Infrastructure)
        │         7. CI/CD pipelines   (type: application, layer: CI/CD)
        │         8. Containers        (type: container, layer: Containerization)
        │         9. E2E tests         (type: library, scope: optional, layer: E2E Testing)
        │        10. Unit tests        (type: library, scope: optional, layer: Unit Testing)
        │        11. API test libs     (type: library, scope: optional, layer: API Testing)
        │        12. Reporting libs    (type: library, scope: optional, layer: Test Reporting)
        │        13. Auth integrations (type: application, supplier: vendor)
        │        14. DB integrations   (type: application)
        │        15. Event streaming   (type: application)
        │        16. Caching           (type: application, status: optional)
        │        17. Monitoring        (type: application)
        │        18. Microservices     (type: application, endpoints, dataClassification, trustBoundary)
        │        19. Admin consoles    (type: application, access-level, security)
        │
        ▼
  Metadata Enrichment (sbomData.metadata.properties[])
  ─────────────────────────────────────────────────────
  equinox:build:timestamp  → ISO 8601 datetime
  equinox:build:commit     → short git SHA (or "unknown")
  equinox:build:branch     → branch name (or "unknown")
  equinox:runtime:nodejs   → process.version (e.g. v22.x)
  equinox:service:type     → e.g. "fastify-lambda", "inventory-service"
  equinox:service:domain   → e.g. "inventory-management"
  [Enhanced only:]
  orchestration:platform   → "AWS Lambda with API Gateway"
  orchestration:eventBus   → "AWS EventBridge for async event processing"
  orchestration:cronJobs   → e.g. "EventBridge Rules for replenishment scheduling"
  security:vulnerability-scanning:tools   → ["npm audit","OWASP","Snyk"]
  security:vulnerability-scanning:status  → "Manual - integrate automated scanning"
  security:static-analysis:tool           → "SonarQube"
  security:headers:helmet                 → "Enabled with CSP, HSTS, X-Frame-Options"
  security:headers:cors                   → "Strict origin whitelist with credentials disabled"
  ai:status                               → "NOT_IMPLEMENTED"
  ai:planned                              → JSON array of planned AI features
  mcp:status                              → "NOT_IMPLEMENTED"
  mcp:planned                             → JSON array of planned MCP tools
  detected:cdk-resources                  → JSON array of CDK-detected AWS resources
  detected:api-endpoints                  → JSON array of API path strings
        │
        ▼
  Write versioned file:  ./sbom/<name>-<version>-cyclonedx.json
  Write latest copy:     ./sbom/<name>-latest-cyclonedx.json
        │
        ▼
  Console summary printed
  (version, commit, branch, Node.js, component counts)

4. SBOM Output Structure

The generated JSON is a valid CycloneDX 1.5 BOM document. Top-level fields:

Field Type Description
bomFormat string Always "CycloneDX"
specVersion string Always "1.5"
serialNumber string UUID URN, e.g. urn:uuid:… — unique per generation run
version integer BOM document version (starts at 1)
metadata object Service identity, author, timestamp, and all enriched properties
components array Full dependency tree + injected component entries
dependencies array Dependency graph edges (package → depends-on list)

metadata object

Sub-field Source Description
timestamp cdxgen ISO 8601 generation time
tools[] cdxgen [{ name: "cdxgen", version: "11.7.0" }]
component cdxgen + enrichment { name, version, description, author } from package.json
authors[] CLI flag [{ name: "Equinox Platform Team" }]
supplier CLI flag { name: "Equinox" }
properties[] enrichment script All equinox:*, orchestration:*, security:*, ai:*, mcp:*, detected:* key-value pairs

Component entry shape (injected components)

{
  "type": "library | platform | application | container",
  "bom-ref": "backend:@fastify/fastify",
  "group": "Backend-Components",
  "name": "Fastify Web Framework",
  "version": "detected",
  "description": "High-performance web framework for Node.js",
  "scope": "required | optional",
  "supplier": { "name": "AWS" },
  "externalReferences": [{ "type": "website", "url": "https://…" }],
  "properties": [
    { "name": "category", "value": "Backend Components & Libraries" },
    { "name": "layer",    "value": "Application Framework" },
    { "name": "custom",   "value": "true" }
  ]
}

5. Configuration — sbomConfig Block (Enhanced Variant)

The enhanced generate-sbom.js contains a single sbomConfig constant that acts as the declarative manifest for all non-npm components. Edit this block per service to reflect the service’s actual technology footprint.

sbomConfig sections

Section Key CycloneDX type What to declare here
Backend frameworks backend.frameworks[] library Web framework (Fastify), ORM, etc.
Backend databases backend.databases[] library DB driver (mysql2, mongoose)
Backend cloud SDKs backend.cloudSDKs[] library AWS SDK packages used
Custom libraries backend.customLibraries[] library @equinox/* internal npm packages
Compute platform devops.compute[] platform AWS Lambda, runtime version
Infrastructure devops.infrastructure[] application AWS CDK, API Gateway, location
CI/CD devops.cicd[] application GitHub Actions workflow path
Containers devops.containers[] container Docker / container runtime
E2E testing qe.e2eTesting[] library Playwright, location
Unit testing qe.unitTesting[] library Jest, location
API testing qe.apiTesting[] library @equinox/eqx-api-automation-core
Reporting qe.reporting[] library Allure, reporting tools
Auth integrations integrations.authentication[] application Amazon Cognito, OIDC providers
Database integrations integrations.databases[] application RDS MySQL, DynamoDB, MongoDB Atlas
Event streaming integrations.eventStreaming[] application Amazon EventBridge
Caching integrations.caching[] application Redis (mark status: "optional")
Monitoring integrations.monitoring[] application AWS X-Ray, CloudWatch
Microservices microservices.services[] application API name, endpoints[], dataClassification, trustBoundary
Orchestration microservices.orchestration metadata Platform, event bus, cron jobs (written to metadata.properties)
Admin consoles admin.consoles[] application Swagger UI endpoint, access level
Security security metadata Scanning tools, static analysis, HTTP security headers
AI ai metadata Status (NOT_IMPLEMENTED / ACTIVE) + planned features
MCP mcp metadata Status + planned MCP tools

bom-ref naming convention

Each injected component gets a bom-ref that is globally unique within the document:

Prefix Example
backend: backend:@fastify/fastify, backend:mysql2
devops: devops:aws-lambda, devops:aws-cdk-infrastructure
qe: qe:@playwright/test, qe:jest
integration: integration:amazon-cognito, integration:redis-cache
microservice: microservice:inventory-management-api
admin: admin:swagger-ui-admin-console

6. Running SBOM Generation

When to run

Trigger Recommended action
Pre-release / release cut Always run npm run sbom:generate and archive the versioned file
Major dependency upgrade Run after npm install to capture the updated tree
Security incident Run immediately to get a current snapshot for forensic analysis
Onboarding a new service Add the script and @cyclonedx/cdxgen devDependency on day one
Compliance audit Provide the sbom/ directory or the latest JSON artifact

How to run (per service)

# Standard services (generate-sbom.js at root)
cd eq-auth-service       # or eq-cart-service, eq-oms-service, etc.
npm run sbom:generate

# Inventory service (script in tools/scripts/)
cd eq-inventory-service
npm run sbom:generate

# PIM service — also validate after generating
cd eq-pim-service
npm run sbom:generate
npm run sbom:validate

What you see in the console

Generating SBOM for eq-authservices v9.0.0...
Git: main@a1b2c3d
✓ Generated: eq-authservices-9.0.0-cyclonedx.json
✓ Updated: eq-authservices-latest-cyclonedx.json

SBOM Location: /path/to/sbom/eq-authservices-9.0.0-cyclonedx.json

Metadata:
  - Version: 9.0.0
  - Commit: a1b2c3d
  - Branch: main
  - Node.js: v22.x.x

Enhanced variant additionally prints:

Components Tracked:
  - Backend: 1 frameworks, 1 databases
  - DevOps: 1 compute, 2 infrastructure
  - QE: 2 test frameworks
  - Integrations: 5 services
  - Microservices: 1 services

7. Services Coverage

Service Script location npm script Validate step Version
eq-auth-service generate-sbom.js sbom:generate No 9.0.0
eq-cart-service generate-sbom.js sbom:generate No 9.0.1
eq-oms-service generate-sbom.js sbom:generate No 9.0.1
eq-pim-service generate-sbom.js sbom:generate Yes (sbom:validate) 9.0.2
eq-foundation-service generate-sbom.js sbom:generate No 9.0.2
eq-marketplace-service generate-sbom.js sbom:generate No 1.0.0
eq-subscription-service generate-sbom.js sbom:generate No 1.0.0
eq-account-service generate-sbom.js sbom:generate No 9.0.0
eq-user-service generate-sbom.js sbom:generate No 9.0.1
eq-inventory-service tools/scripts/generate-sbom.js sbom:generate No 9.0.1
eq-promotion-service Not configured 9.0.0

Note: eq-promotion-service does not have an SBOM script. It should have generate-sbom.js added and @cyclonedx/cdxgen added as a devDependency.

8. Output Files

<service-root>/
└── sbom/
    ├── <pkg-name>-<version>-cyclonedx.json    ← versioned, immutable archive
    └── <pkg-name>-latest-cyclonedx.json       ← always-current copy (overwritten on each run)

Example (inventory service):

eq-inventory-service/
└── sbom/
    ├── eq-inventoryservices-9.0.1-cyclonedx.json
    └── eq-inventoryservices-latest-cyclonedx.json

The versioned file is suitable for artifact storage (S3, GitHub Releases, Nexus). The latest file is convenient for tooling that always needs the current state without knowing the version.

9. Tool & Dependency Details

@cyclonedx/cdxgen

Property Value
Package @cyclonedx/cdxgen
Version pinned 11.7.0
Declared in devDependencies (not shipped to production)
CLI flags used -t js (JavaScript project), --spec-version 1.5, --author "Equinox Platform Team", --supplier "Equinox", -o <outputPath>
What it scans package.json, package-lock.json / node_modules
Output format CycloneDX JSON (also supports XML, but not used here)

cdxgen invocation (exact command)

npx @cyclonedx/cdxgen \
  -t js \
  --spec-version 1.5 \
  --author "Equinox Platform Team" \
  --supplier "Equinox" \
  -o "./sbom/<pkg-name>-<version>-cyclonedx.json"

10. Auto-Detection Logic (Enhanced Variant)

CDK Resource Detection — detectCDKResources()

Scans every .ts and .js file in ./cdk/ using string-pattern matching:

String found in file Detected resource type
lambda.Function { type: "lambda", file: "<filename>" }
apigateway { type: "api-gateway", file: "<filename>" }
rds { type: "rds", file: "<filename>" }
eventbridge { type: "eventbridge", file: "<filename>" }

Results are pushed into sbomData.metadata.properties as detected:cdk-resources (JSON array).

If ./cdk/ does not exist, the function returns an empty array silently.

API Endpoint Detection — detectAPIEndpoints()

Reads sbomConfig.microservices.services[].endpoints[] and deduplicates the list. Results pushed to detected:api-endpoints in metadata properties.

11. Metadata Properties Reference

All custom properties follow the equinox: namespace convention and are stored in sbomData.metadata.properties[] as { name, value } pairs.

Property name Example value Set by
equinox:build:timestamp 2026-08-11T10:30:00.000Z Both variants
equinox:build:commit a1b2c3d (or "unknown") Both variants
equinox:build:branch main (or "unknown") Both variants
equinox:runtime:nodejs v22.14.0 Both variants
equinox:service:type fastify-lambda / inventory-service Both variants
equinox:service:domain inventory-management Enhanced variant
orchestration:platform AWS Lambda with API Gateway Enhanced variant
orchestration:eventBus AWS EventBridge for async event processing Enhanced variant
orchestration:cronJobs EventBridge Rules for replenishment scheduling Enhanced variant
security:vulnerability-scanning:tools ["npm audit","OWASP Dependency-Check","Snyk"] Enhanced variant
security:vulnerability-scanning:status Manual - integrate automated scanning Enhanced variant
security:static-analysis:tool SonarQube Enhanced variant
security:static-analysis:status Available Enhanced variant
security:headers:helmet Enabled with CSP, HSTS, X-Frame-Options Enhanced variant
security:headers:cors Strict origin whitelist with credentials disabled Enhanced variant
ai:status NOT_IMPLEMENTED Enhanced variant
ai:planned ["Demand forecasting","Anomaly detection"] Enhanced variant
mcp:status NOT_IMPLEMENTED Enhanced variant
mcp:planned ["Inventory assistant context management"] Enhanced variant
admin:status Swagger UI available in dev/staging - Disabled in production Enhanced variant
detected:cdk-resources [{"type":"lambda","file":"stack.ts"}] Enhanced variant (auto)
detected:api-endpoints ["/api/v1/locations","/api/v1/skus"] Enhanced variant (auto)

12. Environment Variables / Prerequisites

The SBOM script itself reads no environment variables. It relies on the following being present:

Prerequisite Required Behaviour if missing
node_modules/ installed Yes cdxgen has no packages to scan; output will be minimal
package.json at service root Yes Script crashes — pkg.name / pkg.version unavailable
git CLI on PATH No gitCommit and gitBranch default to "unknown" — generation continues
./cdk/ directory No CDK detection returns empty array silently (enhanced variant only)
Write permission to ./sbom/ Yes fs.mkdirSync fails; generation aborts
@cyclonedx/cdxgen devDependency Yes npx will download it on first run if not cached

Node.js version

The script uses process.version — whichever Node.js is active on PATH. The Equinox platform targets Node.js 22.x (nodejs22.x Lambda runtime).

13. FAQs

Q: What is the difference between the basic and enhanced generate-sbom.js?
A: The basic variant (e.g. eq-auth-service) calls cdxgen and appends 5 metadata properties (timestamp, commit, branch, Node.js version, service type). The enhanced variant (e.g. eq-inventory-service) adds a full sbomConfig declarative block covering 10 categories of components, CDK auto-detection, API endpoint tracking, and security/AI/MCP status — making the SBOM a comprehensive architecture artifact, not just a dependency list.
Q: Where are the generated files stored?
A: In a sbom/ directory at the service root. Two files are written per run: a versioned immutable archive (<name>-<version>-cyclonedx.json) and an always-current latest copy (<name>-latest-cyclonedx.json).
Q: Is the SBOM generated automatically in CI/CD?
A: Not currently — it is a manual npm script (npm run sbom:generate). It should be integrated into the release pipeline (e.g. GitHub Actions) so every release build produces and archives an SBOM artifact. This is flagged as a manual step in the security config ("status": "Manual - integrate automated scanning").
Q: Why does eq-promotion-service not have an SBOM script?
A: The script was not added during initial setup. Add generate-sbom.js (copy from another service and update sbomConfig), add "sbom:generate": "node generate-sbom.js" to package.json scripts, and add @cyclonedx/cdxgen to devDependencies.
Q: Does running the script upload anything to the internet?
A: No. cdxgen scans only local node_modules and package.json. No network calls are made during generation. All output is written to the local sbom/ directory.
Q: Why is gitCommit sometimes "unknown"?
A: When the script runs outside a git repository (e.g. inside a Docker container that doesn’t mount .git, or a cloud build that doesn’t clone git history), git rev-parse fails. The script catches the error and defaults to "unknown" — generation still completes successfully.
Q: What does @cyclonedx/cdxgen -t js do exactly?
A: It tells cdxgen to treat the project as a JavaScript/Node.js project. It reads package.json and package-lock.json, enumerates all direct and transitive dependencies from node_modules, and emits a CycloneDX BOM document with each package as a component entry (including purl, version, licenses, and hashes where available).
Q: How does npm run sbom:validate work in eq-pim-service?
A: The PIM service has an additional validate-sbom.js script that reads the generated JSON and checks it against the CycloneDX 1.5 JSON schema. This catches structural errors (missing required fields, wrong types) before the artifact is archived or submitted.
Q: Can I add custom components that aren’t npm packages?
A: Yes — that’s exactly what the sbomConfig block in the enhanced variant is for. Add entries to backend.customLibraries, integrations, or any other section. They will be injected as explicit components[] entries in the output document.
Q: How do I verify the SBOM output is valid CycloneDX?
A: Use the CycloneDX CLI validator (npx @cyclonedx/cyclonedx-npm --validate) or the CycloneDX online validator at https://cyclonedx.org/tool-center/. For in-repo validation, follow the pattern in eq-pim-service‘s validate-sbom.js.

14. Glossary

Term Meaning
SBOM Software Bill of Materials — a machine-readable list of all software components and their dependencies
CycloneDX An OWASP-backed open standard for SBOMs; supported format is JSON spec version 1.5
BOM Bill of Materials — the document itself (the CycloneDX file)
bom-ref A string identifier that uniquely addresses a component within one CycloneDX document
cdxgen The CLI tool (@cyclonedx/cdxgen) that auto-generates a CycloneDX BOM by scanning npm packages
Component A single entry in the BOM components[] array — may be an npm library, cloud platform, container, or application
Metadata properties Free-form { name, value } pairs in bom.metadata.properties[] used for Equinox-specific enrichment
sbomConfig The declarative configuration block in the enhanced generate-sbom.js that defines non-npm components
Basic variant The simpler generate-sbom.js (e.g. eq-auth-service) that only adds 5 metadata properties after cdxgen runs
Enhanced variant The full generate-sbom.js (e.g. eq-inventory-service) that adds structured component declarations across 10 categories
Build provenance The set of facts (git commit, branch, timestamp, Node.js version) stamped on every SBOM to prove when and from what source it was built
purl Package URL — a standard identifier for an npm package, e.g. pkg:npm/%40fastify%2Ffastify@4.x.x
Transitive dependency A package that your code does not require directly, but is required by one of your direct dependencies
License compliance Verifying that none of the dependencies use licenses (e.g. GPL-3.0) that are incompatible with your software’s distribution model
CVE Common Vulnerabilities and Exposures — a public record of a known security vulnerability in a software component
Supply chain The full set of upstream software (dependencies, tools, build systems) that contributes to your shipped binary
CRA EU Cyber Resilience Act — European regulation requiring SBOM disclosure for connected software products
EO 14028 US Executive Order on Improving the Nation’s Cybersecurity (2021) — mandates SBOM for software sold to the US government

Revision History
2026-08-12 | JP – Created the page and added the content.