A unified vulnerability intelligence tool that aggregates multiple sources (OSV, NVD, etc.) for cross-platform lookup via CLI, API, and Web UI.
Mayu ingests vulnerability data from the OSV ecosystem into a local PostgreSQL database, enabling fast cross-platform search and triage of known vulnerabilities.
Current capabilities:
- Full and delta import of OSV vulnerability data from the GCS bucket
- Direct import of GitHub Security Advisories — fetch directly from the GitHub API with
--source ghsa --repo - SBOM vulnerability audit — feed a CycloneDX or SPDX SBOM and get a full vulnerability report against local data
- Lockfile scanning — directly scan go.sum, package-lock.json, yarn.lock, Cargo.lock, etc. without SBOM generation
- SBOM continuous monitoring — upload SBOMs, track finding statuses, auto-rescan on new vuln data
- CLI-based vulnerability search by ID, package name, ecosystem, or alias (with full-text search support)
- REST API server with OpenAPI 3.1 specification (78+ endpoints)
- Web UI with dashboard, EPSS trending, LEV visualization, and SBOM management
- Supports all OSV ecosystems (Go, PyPI, npm, Maven, crates.io, etc.)
- VEX (Vulnerability Exploitability eXchange) import/export
- Policy-based gating and license compliance checking
- Team management and watchlist-based notifications (webhook + email)
- Raw OSV JSON preserved for full data reversibility
Mayu comes from the Japanese word 繭 (mayu), meaning "cocoon" — the protective casing a silkworm spins around itself. The name reflects the tool's purpose: using vulnerability intelligence to wrap your environment in a gentle yet resilient layer of protection.
There are several excellent vulnerability intelligence tools available. Mayu occupies a unique position by combining the following characteristics in a single, self-contained tool:
| Cloud-based CVE CLIs | CVE Monitoring Platforms | Mayu | |
|---|---|---|---|
| Data ownership | Cloud API dependent | Self-hosted or SaaS | Fully local (PostgreSQL) |
| Offline / air-gap | ❌ | Partial (self-hosted) | ✅ After initial sync |
| REST API built-in | ❌ (client only) | ✅ | ✅ (78+ endpoints) |
| Web UI built-in | ❌ | ✅ | ✅ (dashboard, SBOM mgmt) |
| CLI | ✅ | Limited | ✅ (full-featured) |
| OSV ecosystem coverage | ❌ (CVE/CPE only) | ❌ (CVE/CPE only) | ✅ All OSV ecosystems (package-level) |
| Package-name search | ❌ | ❌ | ✅ |
| EPSS / KEV / LEV | EPSS + KEV | EPSS + KEV | EPSS + KEV + LEV + Exploit-DB |
| Lockfile scanning | Partial | ❌ | ✅ (10+ lockfile formats) |
| SBOM audit + monitoring | ❌ | Partial | ✅ (CycloneDX, SPDX, continuous) |
| VEX / Policy gating | ❌ | ❌ | ✅ (OpenVEX import/export, policy YAML) |
| Custom data import | ❌ | ❌ | ✅ (local JSON files) |
| Raw data preservation | ❌ | Partial | ✅ Full reversibility |
| Account / API key required | ✅ | ✅ (SaaS) | ❌ |
In short:
- Unlike cloud-based CLI tools, mayu owns all data locally and provides a built-in REST API and Web UI — no external service dependency or API key required.
- Unlike CVE monitoring platforms focused on vendor/product (CPE) matching and alerting, mayu supports package-level search across all OSV ecosystems (Go, npm, PyPI, Maven, crates.io, etc.) and computes LEV scores for exploitation likelihood estimation.
- Mayu is designed as a vulnerability intelligence backend — a single binary that can serve as both a personal lookup tool and an organization-wide vulnerability data API.
Download the latest release from GitHub Releases.
Release binaries include the Web UI embedded — just run mayu serve and access the UI at http://localhost:8080/.
| Platform | Architecture | Download |
|---|---|---|
| Linux | x86_64 | mayu_*_linux_amd64.tar.gz |
| Linux | ARM64 | mayu_*_linux_arm64.tar.gz |
| macOS | x86_64 (Intel) | mayu_*_darwin_amd64.tar.gz |
| macOS | ARM64 (Apple Silicon) | mayu_*_darwin_arm64.tar.gz |
| Windows | x86_64 | mayu_*_windows_amd64.zip |
| Windows | ARM64 | mayu_*_windows_arm64.zip |
# Example: Linux x86_64
curl -LO https://github.com/kato83/mayu/releases/latest/download/mayu_0.0.27_linux_amd64.tar.gz
tar xzf mayu_0.0.27_linux_amd64.tar.gz
sudo mv mayu /usr/local/bin/
# Verify installation
mayu versionBuild from Source
Requires:
- Go 1.26+
- Node.js 24+ (for Web UI build)
- pnpm 11+ (for Web UI dependency management)
git clone https://github.com/kato83/mayu.git
cd mayu
# Build with embedded Web UI (recommended — same as release binaries)
make build
# Run — UI is served automatically at /
./bin/mayu serve[!TIP] If you only need the CLI/API without the Web UI, you can build with Go alone:
make build-no-uiIn this case, use
--ui-dirto serve the Web UI from a separately built directory.
- PostgreSQL 17+
Tip
If you only want to try mayu quickly, use Docker to run PostgreSQL:
docker run -d --name mayu-pg -e POSTGRES_USER=mayu -e POSTGRES_PASSWORD=mayu -e POSTGRES_DB=mayu -p 5432:5432 postgres:17# Run database migrations
mayu migrate# Import all Go ecosystem vulnerabilities (full sync)
mayu ingest --ecosystem Go
# Import with delta update (only new/modified since last sync)
mayu ingest --ecosystem Go --update
# Import all OSV ecosystems
mayu ingest --source osv
# Import all ecosystems with custom parallelism
mayu ingest --source osv --concurrency 5 --store-workers 8
# Import NVD CVE data (OSV-converted format from GCS)
mayu ingest --source osv --type nvd
# Import Debian Security Advisories (OSV-converted format from GCS)
mayu ingest --source osv --type debian
# Import NVD CVE data directly from NVD JSON Feed 2.0
mayu ingest --source nvd
# Import only a specific year's NVD data
mayu ingest --source nvd --year 2024
# Import MITRE CVE data from cvelistV5 GitHub Releases
mayu ingest --source mitre
# Delta update from hourly MITRE releases
mayu ingest --source mitre --update
# Import EPSS scores (Exploit Prediction Scoring System)
mayu ingest --source epss
# Update EPSS scores (daily refresh if outdated)
mayu ingest --source epss --update
# Backfill EPSS historical data (required for LEV computation)
mayu ingest --source epss --backfill
# Backfill EPSS for a specific date range
mayu ingest --source epss --backfill --from 2024-01-01 --to 2025-07-19
# Import CISA KEV catalog (Known Exploited Vulnerabilities)
mayu ingest --source kev
# Update KEV catalog (refresh if outdated)
mayu ingest --source kev --update
# Import Exploit-DB entries (from official GitLab CSV)
mayu ingest --source exploitdb
# Update Exploit-DB (refresh if outdated)
mayu ingest --source exploitdb --update
# Import endoflife.date product lifecycle data (EOL dates, LTS status)
mayu ingest --source eol
# Update endoflife.date data (refresh if last sync > 24h ago)
mayu ingest --source eol --update
# Import GitHub repository security advisories via API
mayu ingest --source ghsa --repo WordPress/wordpress-develop
# With authentication (for rate limits or private repos)
GITHUB_TOKEN=ghp_xxx mayu ingest --source ghsa --repo owner/repo
# View ingest job history
mayu ingest history
# View details for a specific job (including failed IDs)
mayu ingest history --job-id 42# Search by vulnerability ID
mayu search --id GO-2024-2687
# Search by package name
mayu search --package golang.org/x/crypto
# Search by ecosystem
mayu search --ecosystem Go --limit 10
# Search by CVE alias
mayu search --id CVE-2024-24790
# Search by Package URL (purl)
mayu search --purl pkg:npm/%40angular/core
# Positional argument (shorthand for --id)
mayu search CVE-2024-24790
# Filter by severity level
mayu search --severity critical --ecosystem Go
# Filter by date (modified since)
mayu search --since 2024-01-01 --ecosystem npm
# Filter by affected version
mayu search --package golang.org/x/crypto --version 0.17.0
# Filter by KEV (Known Exploited Vulnerabilities)
mayu search --kev --limit 10
# Sort by EPSS score
mayu search --ecosystem Go --sort epss_desc --limit 10
# Full-text search (requires --init first)
mayu search --init
mayu search --query "remote code execution" --ecosystem Go
# Pagination
mayu search --ecosystem Go --limit 10 --offset 20
# Cursor-based pagination (use NextToken from previous output)
mayu search --ecosystem Go --limit 10 --starting-token <token>
# Count results only
mayu search --ecosystem Go --count
# Detailed view (all fields)
mayu search --id GO-2024-2687 --detail
# JSON output for scripting
mayu search --id GO-2024-2687 --format json
# CSV export
mayu search --ecosystem Go --format csv > vulns.csv# Audit CycloneDX SBOM for vulnerabilities
mayu audit --sbom ./sbom.cdx.json
# Audit SPDX SBOM
mayu audit --sbom ./sbom.spdx.json
# Include dev dependencies
mayu audit --sbom ./sbom.cdx.json --include-dev
# Skip version matching (show all vulnerabilities for matched packages)
mayu audit --sbom ./sbom.cdx.json --no-version-check
# JSON output
mayu audit --sbom ./sbom.cdx.json --format json
# CSV output
mayu audit --sbom ./sbom.cdx.json --format csv
# SARIF output (for GitHub Code Scanning / GitLab SAST)
mayu audit --sbom ./sbom.cdx.json --format sarif > results.sarif
# Fail only on critical and high severity findings
mayu audit --sbom ./sbom.cdx.json --fail-on critical,high
# Suppress accepted vulnerabilities
mayu audit --sbom ./sbom.cdx.json --ignore .mayu-ignore
# Apply VEX suppressions
mayu audit --sbom ./sbom.cdx.json --vex product.vex.json
# Apply policy-based gating
mayu audit --sbom ./sbom.cdx.json --policy policy.yaml
# License compliance checking
mayu audit --sbom ./sbom.cdx.json --license-policy license-policy.yaml
# CI/CD gate: combine all options
mayu audit --sbom bom.json --fail-on critical,high --ignore .mayu-ignore --format sarif > results.sarif
# Generate enriched SBOM (input SBOM + vulnerability findings + EPSS/LEV/KEV data)
mayu audit --sbom ./sbom.cdx.json --output-sbom enriched.cdx.json# Start the server (API + Web UI, default port: 8080)
mayu serve
# Start on custom port
mayu serve --addr :3000Import vulnerability data from OSV into the local database.
| Flag | Description | Default |
|---|---|---|
--ecosystem |
Ecosystem to import (e.g., Go, PyPI, npm) | — |
--source |
Import from source (osv, nvd, mitre, epss, kev, exploitdb, eol, ghsa) |
— |
--type |
Sub-type for --source osv (nvd, debian) to import OSV-converted data |
— |
--update |
Perform delta update instead of full import | false |
--backfill |
Backfill historical data (with --source epss) |
false |
--from |
Start date for backfill (YYYY-MM-DD) | 2023-03-07 (EPSS v3) |
--to |
End date for backfill (YYYY-MM-DD) | today |
--repo |
GitHub repository (owner/repo) for --source ghsa |
— |
--year |
Import only a specific year's NVD feed (with --source nvd) |
— |
--concurrency |
Number of ecosystems to import in parallel (with --source osv) |
3 |
--store-workers |
Number of parallel DB store workers per ecosystem | CPU cores - 1 |
--batch-size |
Number of vulnerabilities per batch insert | 100 |
Tip
The list of available ecosystems is published at ecosystems.txt.
Show ingest job execution history. Every ingest command is automatically recorded with its options, timing, status, and any failed vulnerability IDs.
| Flag | Description | Default |
|---|---|---|
--limit |
Number of recent jobs to display | 20 |
--job-id |
Show details for a specific job ID (includes failure list) | — |
--format |
Output format: table, json |
table |
Examples:
# List recent ingest jobs
mayu ingest history
# Show last 5 jobs
mayu ingest history --limit 5
# Show details for job #42 (including failed CVE/OSV IDs)
mayu ingest history --job-id 42
# JSON output for scripting
mayu ingest history --format jsonRecorded information per job:
- Command options used (ecosystem, source, update mode, etc.)
- Start and end timestamps
- Status:
success,failed,partial(some items failed) - Counts: total, success, failure
- For each failure: vulnerability ID, error type, error message, stack trace
Note
Only the 100 most recent jobs are retained. Older jobs are automatically pruned.
Audit an SBOM for known vulnerabilities.
| Flag | Description | Default |
|---|---|---|
--sbom |
Path to SBOM file (CycloneDX 1.7 or SPDX 2.3 JSON) | (required) |
--format |
Output format: table, json, csv, sarif |
table |
--include-dev |
Include development dependencies in audit | false |
--no-version-check |
Skip version matching, report all vulnerabilities for package name | false |
--fail-on |
Fail only for findings at or above specified severity (comma-separated: critical, high, medium, low, none) |
(all findings fail) |
--ignore |
Path to ignore file containing vulnerability IDs to suppress (one per line, # for comments) |
- |
--vex |
Path to OpenVEX file to suppress not_affected findings |
— |
--policy |
Path to policy YAML file for custom gating (block/warn/suppress) | — |
--license-policy |
Path to license policy YAML file for license compliance checking | — |
--output-sbom |
Path to write enriched SBOM with vulnerabilities section (CycloneDX format) | — |
Exit codes:
| Code | Meaning |
|---|---|
| 0 | No vulnerabilities found (or none above --fail-on threshold) |
| 1 | Findings above threshold detected (or any findings when --fail-on is not set) |
| 2 | Error (invalid input, database connection failure, etc.) |
Supported SBOM formats:
- CycloneDX 1.7 (JSON) -- dev dependencies detected via
scopeandcdx:npm:package:developmentproperty - SPDX 2.3 (JSON) -- all packages treated as production (SPDX lacks dev/prod distinction)
Ignore file format (.mayu-ignore):
# Accepted risks
CVE-2024-1234 # reason: no impact on our usage
GHSA-xxxx-yyyy # suppressed until 2025-03-01
CI/CD integration example (GitHub Actions):
- name: Audit dependencies
run: |
mayu audit --sbom bom.json --fail-on critical,high --ignore .mayu-ignore --format sarif > results.sarif
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarifWarning
The mayu sbom subcommands are experimental. Breaking changes to the CLI interface, API responses, or database schema may occur without notice. SBOM scan results stored in the database may be reset during schema migrations in future releases.
All mayu sbom subcommands require authentication. You can authenticate using either method:
- API key (recommended for CI/CD): Set the
MAYU_API_KEYenvironment variable. - Session token: Run
mayu loginto store a session locally.
# Option 1: API key
export MAYU_API_KEY=your-api-key
# Option 2: Session-based login
mayu loginUpload an SBOM file and run a vulnerability scan.
| Flag | Description | Default |
|---|---|---|
--project |
Project name | (required) |
--version |
SBOM version label | (required) |
--sbom |
Path to SBOM file (CycloneDX or SPDX JSON) | (required) |
--environment |
Environment label (e.g., production, staging) |
— |
Examples:
export MAYU_API_KEY=your-api-key
mayu sbom upload --project my-app --version 1.0.0 --sbom bom.json
mayu sbom upload --project my-app --version 2.0.0 --sbom bom.json --environment productionRe-scan an existing SBOM version for vulnerabilities using the latest vulnerability database.
| Flag | Description | Default |
|---|---|---|
--project |
Project name | (required) |
--version |
Version to scan (if omitted, scans the latest version) | — |
Examples:
export MAYU_API_KEY=your-api-key
mayu sbom scan --project my-app
mayu sbom scan --project my-app --version 1.0.0List SBOM projects or versions within a project.
| Flag | Description | Default |
|---|---|---|
--project |
Project name (if omitted, lists all projects) | — |
Examples:
export MAYU_API_KEY=your-api-key
mayu sbom list # List all projects
mayu sbom list --project my-app # List versions for a projectSearch for vulnerabilities in the local database.
| Flag | Description | Default |
|---|---|---|
--id |
Search by vulnerability ID or alias (e.g., CVE-2024-1234, GO-2024-2687, GHSA-xxxx) | — |
--package |
Search by package name | — |
--ecosystem |
Filter by ecosystem | — |
--purl |
Search by Package URL (e.g., pkg:npm/%40angular/core) |
— |
--severity |
Filter by CVSS severity level (critical, high, medium, low, none) | — |
--since |
Filter by modified date (YYYY-MM-DD or RFC3339) | — |
--version |
Filter by affected version | — |
--kev |
Filter to only KEV (Known Exploited Vulnerabilities) entries | false |
--sort |
Sort order: modified_desc, modified_asc, published_desc, published_asc, epss_desc, epss_asc |
modified_desc |
--query |
Full-text search query (requires search.engine configured in config.yaml) |
— |
--format |
Output format: table, json, csv |
table |
--limit |
Maximum number of results | 20 |
--offset |
Offset for pagination (deprecated: use --starting-token) |
0 |
--starting-token |
Cursor token for pagination (from previous NextToken output) |
— |
--count |
Show only the result count | false |
--detail |
Show detailed information for each result | false |
--init |
Initialize full-text search indexes (required before first --query use) |
false |
Start the server (REST API + Web UI) for vulnerability data access.
| Flag | Description | Default |
|---|---|---|
--addr |
Address to listen on (host:port) | :8080 |
--ui-dir |
Path to SPA static files directory for Web UI hosting | — |
Endpoints:
For the full API specification, see internal/server/openapi.yaml or access http://localhost:8080/openapi.yaml while the server is running.
Run database migrations (embedded in the binary).
| Flag | Description | Default |
|---|---|---|
--steps |
Number of migrations to apply (0 = all, negative to roll back) | 0 |
Subcommands:
| Subcommand | Description |
|---|---|
up |
Apply all pending migrations (default) |
down |
Roll back one migration (or --steps N) |
status |
Show current migration version |
Examples:
mayu migrate # Apply all pending migrations
mayu migrate up
mayu migrate down
mayu migrate down --steps 3
mayu migrate statusCreate a new user account.
| Flag | Description | Default |
|---|---|---|
--email |
User email address (required) | — |
--name |
User display name | — |
--role |
User role: admin or viewer |
viewer |
--password |
User password (required) | — |
Examples:
mayu user create --email admin@example.com --name Admin --role admin --password secret
mayu user create --email viewer@example.com --role viewer --password mypassUpdate an existing user's role.
| Flag | Description | Default |
|---|---|---|
--email |
User email address to update (required) | — |
--role |
New role: admin or viewer (required) |
— |
Examples:
mayu user update --email user@example.com --role admin
mayu user update --email user@example.com --role viewerList all users in table format (ID, Email, Name, Role).
Examples:
mayu user listReset a user's password (admin operation). Only available when auth.mode=local.
| Flag | Description | Default |
|---|---|---|
--email |
User email address (required) | — |
--password |
New password (required) | — |
Examples:
mayu user reset-password --email user@example.com --password newpasswordNote
This command exits with an error if auth.mode is not local (i.e., none or oidc).
Create a new API key for a user. The generated key is displayed once and cannot be recovered.
| Flag | Description | Default |
|---|---|---|
--user-email |
Email of the user to associate the key with (required) | — |
--name |
Description/name for the API key | — |
--expires |
Expiration duration (e.g., 90d, 1y, 24h) |
— (no expiration) |
Examples:
mayu apikey create --user-email admin@example.com --name 'CI Pipeline'
mayu apikey create --user-email admin@example.com --name 'Temp Key' --expires 90dAll mayu webhook subcommands require authentication. You can authenticate using either method:
- API key (recommended for CI/CD): Set the
MAYU_API_KEYenvironment variable. - Session token: Run
mayu loginto store a session locally.
Webhooks are scoped per user -- each user can only manage their own webhooks.
# Option 1: API key
export MAYU_API_KEY=your-api-key
# Option 2: Session-based login
mayu loginCreate a new webhook for notifications.
Tip
For detailed documentation on template variables, events, signature verification, and delivery behavior, see docs/webhooks.md.
| Flag | Description | Default |
|---|---|---|
--name |
Webhook name (required) | — |
--url |
Webhook URL (required) | — |
--events |
Comma-separated list of events to subscribe to (required) | — |
--content-type |
Content-Type header for the webhook request | application/json |
--body-template |
Mustache template for the request body | — |
--secret |
HMAC secret for webhook signature verification | — |
--enabled |
Whether the webhook is enabled | true |
Examples:
export MAYU_API_KEY=your-api-key
mayu webhook create --name "security-team-slack" --url "https://hooks.slack.com/services/T00/B00/xxxx" --events "new_critical,new_high" --body-template '{"text": "{{ID}} ({{Severity}}) - {{Summary}}"}'
mayu webhook create --name "all-vulns" --url "https://example.com/webhook" --events "*"List webhooks for the authenticated user in table format (ID, Name, URL, Events, Enabled).
Examples:
export MAYU_API_KEY=your-api-key
mayu webhook listSend a test payload to a webhook to verify connectivity.
| Flag | Description | Default |
|---|---|---|
--id |
Webhook ID to test (required) | — |
Examples:
export MAYU_API_KEY=your-api-key
mayu webhook test --id 1Scan lockfiles for known vulnerabilities without generating an SBOM.
| Flag | Description | Default |
|---|---|---|
--lockfile |
Path to a lockfile to scan | — |
--dir |
Directory to scan for lockfiles | — |
--format |
Output format: table, json, csv, sarif |
table |
--fail-on |
Fail with exit code 1 only for findings at or above severity | — |
--ignore |
Path to ignore file containing vulnerability IDs to suppress | — |
--include-dev |
Include development dependencies in scan | false |
--no-version-check |
Skip version matching | false |
--policy |
Path to policy YAML file for custom gating | — |
--reachability |
Run reachability analysis on Go projects | false |
Supported lockfile formats:
- go.sum (Go)
- package-lock.json (npm)
- yarn.lock (Yarn)
- pnpm-lock.yaml (pnpm)
- Pipfile.lock (Python/pipenv)
- poetry.lock (Python/poetry)
- Gemfile.lock (Ruby)
- Cargo.lock (Rust)
- requirements.txt (Python/pip)
- composer.lock (PHP)
Examples:
mayu scan --lockfile ./go.sum
mayu scan --dir .
mayu scan --lockfile ./package-lock.json --format json
mayu scan --dir . --fail-on critical,high
mayu scan --lockfile ./Cargo.lock --ignore .mayu-ignore
mayu scan --lockfile ./go.sum --reachabilityShow data source sync status and EPSS coverage.
| Flag | Description | Default |
|---|---|---|
--format |
Output format: table, json |
table |
Examples:
mayu status
mayu status --format jsonManage watchlists for automatic vulnerability notification when new vulnerabilities match your criteria.
Subcommands: add, list, remove, check
| Flag | Description | Default |
|---|---|---|
--name |
Watchlist entry name (required) | — |
--type |
Match type: package, purl, cpe, ecosystem (required) |
— |
--ecosystem |
Ecosystem name (for package/ecosystem match types) | — |
--package |
Package name (for package match type) | — |
--purl |
Purl pattern for prefix matching (for purl match type) | — |
--cpe |
CPE pattern for prefix matching (for cpe match type) | — |
--severity-min |
Minimum severity: critical, high, medium, low, none | — |
--epss-threshold |
Minimum EPSS score threshold (0.0-1.0) | — |
--user-email |
Email of the user who owns this watchlist (required) | — |
| Flag | Description | Default |
|---|---|---|
--user-email |
Email of the user (required) | — |
| Flag | Description | Default |
|---|---|---|
--id |
Watchlist entry ID (required) | — |
--user-email |
Email of the user (required) | — |
| Flag | Description | Default |
|---|---|---|
--dry-run |
Preview matches without sending notifications | false |
Examples:
mayu watch add --name 'Go crypto' --type package --ecosystem Go --package golang.org/x/crypto --user-email admin@example.com
mayu watch add --name 'Express' --type purl --purl pkg:npm/express --user-email admin@example.com
mayu watch add --name 'Apache HTTPD' --type cpe --cpe 'cpe:2.3:a:apache:http_server' --user-email admin@example.com
mayu watch add --name 'Go Critical' --type ecosystem --ecosystem Go --severity-min critical --user-email admin@example.com
mayu watch list --user-email admin@example.com
mayu watch remove --id 1 --user-email admin@example.com
mayu watch check --dry-runManage teams for collaborative vulnerability tracking and shared resources (watchlists, webhooks, SBOM projects).
Subcommands: create, list, add-member, remove-member, members
| Flag | Description | Default |
|---|---|---|
--name |
Team name (required) | — |
--description |
Team description | — |
No flags. Lists all teams.
| Flag | Description | Default |
|---|---|---|
--team |
Team name (required) | — |
--email |
User email to add (required) | — |
--role |
Member role: owner or member |
member |
| Flag | Description | Default |
|---|---|---|
--team |
Team name (required) | — |
--email |
User email to remove (required) | — |
| Flag | Description | Default |
|---|---|---|
--team |
Team name (required) | — |
Examples:
mayu team create --name "platform-team" --description "Platform engineering team"
mayu team list
mayu team add-member --team platform-team --email user@example.com --role owner
mayu team add-member --team platform-team --email dev@example.com
mayu team remove-member --team platform-team --email dev@example.com
mayu team members --team platform-teamImport and export OpenVEX documents for SBOM finding status management.
Subcommands: export, import
All mayu vex subcommands require authentication (set MAYU_API_KEY or run mayu login).
| Flag | Description | Default |
|---|---|---|
--project |
Project name (required) | — |
--version |
Version (default: latest) | — |
--author |
Document author | mayu |
--id |
Document ID (default: auto-generated) | — |
| Flag | Description | Default |
|---|---|---|
--project |
Project name (required) | — |
--version |
Version (default: latest) | — |
--file |
Path to OpenVEX file (required) | — |
Examples:
export MAYU_API_KEY=your-api-key
mayu vex export --project my-app --version 1.0.0 > product.vex.json
mayu vex export --project my-app --author security-team@example.com
mayu vex import --project my-app --file product.vex.jsonValidate and manage policy files for audit gating.
Subcommands: validate
| Flag | Description | Default |
|---|---|---|
--file |
Path to policy YAML file (required) | — |
Examples:
mayu policy validate --file policy.yamlManage notification channels and templates.
Subcommands: templates, test-email
| Flag | Description | Default |
|---|---|---|
--format |
Output format: table, json |
table |
--name |
Show full template content for a specific template (slack, teams, email) | — |
| Flag | Description | Default |
|---|---|---|
--to |
Recipient email address (required) | — |
--subject |
Email subject | Mayu Test Email Notification |
Examples:
mayu notification templates
mayu notification templates --name slack
mayu notification test-email --to admin@example.comGenerate an SBOM from lockfiles in CycloneDX or SPDX format. No authentication required (local operation only).
| Flag | Description | Default |
|---|---|---|
--lockfile |
Path to lockfile | — |
--dir |
Directory to scan for lockfiles | — |
--format |
Output format: cyclonedx or spdx |
cyclonedx |
--name |
Project/component name | — |
--version |
Project version | — |
--output |
Output file path (default: stdout) | — |
Examples:
mayu sbom generate --lockfile ./go.sum --format cyclonedx --name my-app --version 1.0.0
mayu sbom generate --dir . --format spdx --name my-app
mayu sbom generate --lockfile ./package-lock.json --output sbom.cdx.jsonSuppress a finding (mark as not applicable to this context). Requires authentication.
| Flag | Description | Default |
|---|---|---|
--project |
Project name (required) | — |
--version |
Version (default: latest) | — |
--vuln |
Vulnerability ID (required) | — |
--purl |
Package URL (if omitted, applies to first matching finding) | — |
--reason |
Justification | — |
--expires |
Expiration duration (e.g., 90d, 1y) | — |
Examples:
export MAYU_API_KEY=your-api-key
mayu sbom suppress --project my-app --vuln CVE-2024-1234 --reason "not applicable"Accept risk for a finding (known vulnerability that cannot be patched). Requires authentication.
| Flag | Description | Default |
|---|---|---|
--project |
Project name (required) | — |
--version |
Version (default: latest) | — |
--vuln |
Vulnerability ID (required) | — |
--purl |
Package URL (if omitted, applies to first matching finding) | — |
--reason |
Justification (required) | — |
--expires |
Expiration duration (e.g., 90d, 1y) | — |
Examples:
export MAYU_API_KEY=your-api-key
mayu sbom accept --project my-app --vuln CVE-2024-1234 --reason "isolated environment" --expires 90dView or reset finding statuses for an SBOM version. Requires authentication.
| Flag | Description | Default |
|---|---|---|
--project |
Project name (required) | — |
--version |
Version (default: latest) | — |
--filter |
Filter by status (comma-separated: open, in_triage, suppressed, false_positive, risk_accepted, resolved) | — |
--reset |
Reset status for vulnerability ID | — |
--purl |
Package URL for reset operation | — |
Examples:
export MAYU_API_KEY=your-api-key
mayu sbom status --project my-app
mayu sbom status --project my-app --filter suppressed,risk_accepted
mayu sbom status --project my-app --reset CVE-2024-1234 --purl pkg:npm/example@1.0.0Authenticate with a mayu server and store session credentials locally. Credentials are saved to ~/.config/mayu/credentials.json with 0600 permissions.
| Flag | Description | Default |
|---|---|---|
--oidc |
Use OIDC browser-based login | false |
--server |
Server URL | http://localhost:8080 |
Modes:
- Interactive (default): Prompts for email and password on the terminal.
- OIDC (
--oidc): Opens the default browser for OIDC authentication. A temporary local HTTP server is started on a random port to receive the callback.
Authentication priority (used by mayu sbom, mayu webhook, and other authenticated commands):
MAYU_API_KEYenvironment variable (recommended for CI/CD)- Stored session token from
mayu login(~/.config/mayu/credentials.json) - Error with a message suggesting
mayu loginor settingMAYU_API_KEY
Examples:
# Interactive email/password login
mayu login
# Specify a server URL
mayu login --server http://example.com:8080
# OIDC browser-based login (requires auth.mode=oidc in config)
mayu login --oidc
# OIDC login with custom server
mayu login --oidc --server http://example.com:8080Remove stored session credentials. Optionally invalidates the session on the server (best-effort; does not fail if the server is unreachable).
Examples:
mayu logoutPrint version information.
Mayu supports a YAML configuration file. The default path is:
$HOME/.config/mayu/config.yaml
You can specify a custom path with the --config global option:
mayu --config /path/to/config.yaml search --id CVE-2024-1234If the default config file does not exist, mayu silently falls back to environment variables and defaults. If --config is explicitly specified and the file does not exist, mayu reports an error.
Example config.yaml:
# Database connection
database_url: postgres://mayu:mayu@localhost:5432/mayu?sslmode=disable
# Authentication settings
auth:
# mode: none | local | oidc (default: none)
mode: none
# EPSS data retention (default: 365 days, counted from yesterday)
# Set to -1 to retain all historical data indefinitely (required for full LEV accuracy)
epss:
retention_days: 365Example with local authentication:
database_url: postgres://mayu:mayu@localhost:5432/mayu?sslmode=disable
auth:
mode: local
session_secret: "your-random-secret-key"
session_max_age: 86400 # seconds (default: 86400 = 24h)Example with OIDC authentication:
database_url: postgres://mayu:mayu@localhost:5432/mayu?sslmode=disable
auth:
mode: oidc
session_secret: "your-random-secret-key"
session_max_age: 86400
oidc:
issuer: "https://accounts.google.com"
client_id: "your-client-id.apps.googleusercontent.com"
client_secret: "your-client-secret"
redirect_url: "http://localhost:8080/auth/callback"
scopes:
- openid
- email
- profilePriority order (highest to lowest):
- Environment variables (
DATABASE_URL) - Configuration file (
config.yaml— specify path with--config) - Default values
| Environment Variable | Description | Default |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | postgres://mayu:mayu@localhost:5432/mayu?sslmode=disable |
Warning
The default connection string uses sslmode=disable, which is
appropriate only for local development against the bundled Docker PostgreSQL.
For any remote or production database, enforce TLS by setting
sslmode=require (or verify-full for certificate verification), e.g.
postgres://user:pass@db.example.com:5432/mayu?sslmode=verify-full.
Mayu prints a warning when it detects a connection to a non-local host without
enforced TLS.
| Source | Status | Method |
|---|---|---|
| OSV | ✅ Supported | GCS bucket (gs://osv-vulnerabilities/) |
| NVD CVE (converted) | ✅ Supported | mayu ingest --source osv --type nvd |
| NVD CVE (native) | ✅ Supported | mayu ingest --source nvd |
| Debian Security Advisories | ✅ Supported | mayu ingest --source osv --type debian |
| MITRE CVE (cvelistV5) | ✅ Supported | mayu ingest --source mitre |
| GitHub Security Advisories | ✅ Supported | mayu ingest --source ghsa --repo owner/repo |
Note
Converted sources (NVD, Debian) contain 50,000+ entries and are downloaded individually since no bulk archive is available. This may take significant time.
Tip
For a detailed comparison of the two NVD import methods (native vs. OSV-converted), see docs/nvd-import-comparison.md.
| Source | Status | Method |
|---|---|---|
| KEV | ✅ Supported | mayu ingest --source kev |
| EPSS | ✅ Supported | mayu ingest --source epss |
| LEV | ✅ Supported | Computed from EPSS + KEV (see below) |
| Exploit-DB | ✅ Supported | mayu ingest --source exploitdb |
| endoflife.date | ✅ Supported | mayu ingest --source eol |
Mayu computes LEV scores — a probabilistic metric proposed by NIST (CSWP 41) that estimates the chance a CVE has already been exploited in the wild.
LEV combines two data sources already in mayu:
| Data Source | Role | Time Perspective |
|---|---|---|
| EPSS | Daily exploitation probability (P30) | Future (next 30 days) |
| CISA KEV | Confirmed exploitation | Past (known exploited) |
| LEV | Probability of past exploitation | Past (estimated) |
Algorithm (rigorous approach from NIST CSWP 41):
P1 = 1 - (1 - P30)^(1/30) # Convert EPSS 30-day prob → daily prob
LEV = 1 - ∏(1 - P1_i) # Compound across all historical days
If the CVE is in the CISA KEV catalog, LEV is automatically set to 1.0 (confirmed exploitation).
Note
This implementation uses the rigorous P30→P1 conversion, not the P30/30 approximation from the paper which is inaccurate for high EPSS scores.
LEV requires historical daily EPSS data. Use the backfill command to build up the time-series:
# 1. Import CISA KEV catalog
mayu ingest --source kev
# 2. Backfill EPSS daily scores from EPSS v3 release (2023-03-07) to today
mayu ingest --source epss --backfill
# Or specify a custom date range
mayu ingest --source epss --backfill --from 2024-01-01 --to 2025-07-19
# 3. After initial backfill, keep EPSS up-to-date with daily updates
mayu ingest --source epss --updateTip
The backfill downloads ~5-7 MB per day (~200,000 CVE scores). A full backfill from 2023-03-07 covers ~860 days. Already-imported dates are automatically skipped on re-run.
Important
By default, mayu retains 365 days of EPSS history. LEV accuracy improves with more historical data. For maximum accuracy, set epss.retention_days: -1 in config.yaml to retain all data indefinitely. After each EPSS ingest, data beyond the retention period is automatically cleaned up.
LEV is displayed automatically in the --detail view and the API ?detail=true response:
mayu search --id CVE-2023-38831 --detailOutput includes EPSS, KEV, and LEV sections:
EPSS:
Score: 0.94218 (94.2%)
Percentile: 0.99923 (99.9%)
Score Date: 2026-07-19
KEV (CISA Known Exploited Vulnerabilities):
Vendor/Project: WinRAR
Product: WinRAR
Vuln Name: RARLAB WinRAR Code Execution Vulnerability
Date Added: 2023-08-24
Due Date: 2023-09-14
Ransomware Use: Known
LEV (Likely Exploited Vulnerabilities - NIST CSWP 41):
Score: 1.00000 (100.0%)
In KEV: true
EPSS Days: 730
First EPSS: 2023-03-07
Last EPSS: 2025-07-19
API example:
curl "http://localhost:8080/api/v1/vulnerabilities/CVE-2023-38831?detail=true" | jq '.lev'{
"lev": 1.0,
"in_kev": true,
"epss_score_count": 730,
"first_epss_date": "2023-03-07",
"last_epss_date": "2025-07-19",
"computed_at": "2026-07-19T12:00:00Z"
}| LEV Range | Interpretation |
|---|---|
| 0.95 – 1.0 | Almost certainly exploited (or confirmed via KEV) |
| 0.70 – 0.95 | Very likely exploited |
| 0.30 – 0.70 | Possibly exploited |
| 0.05 – 0.30 | Low probability of past exploitation |
| 0.00 – 0.05 | Unlikely to have been exploited |
Important
LEV is a probabilistic estimate, not a confirmed fact. It should be used alongside other signals (KEV, EPSS, CVSS) for vulnerability prioritization.
See CONTRIBUTING.md for development setup, coding conventions, and how to submit changes.
Emoji graphics by Twemoji are licensed under CC-BY 4.0.
See .agents/tasks/PLAN.md for the full implementation plan.
- Phase 1: Data Pipeline (OSV ingestion)
- Phase 2: CLI (ingest + search)
- Phase 3: CI/CD (GitHub Actions)
- Phase 4: API Server (REST)
- Phase 5: Web UI (Angular)
- Phase 6: Additional Data Sources (EPSS, KEV, LEV)
- EPSS trend graph & LEV visualization
- Advanced triage workflows (SSVC decision support)
- Dashboard & reporting
- Notifications (webhook)
- Notifications (email)
- endoflife.date integration
- SBOM features (continuous monitoring, finding status management)
- Lockfile scanning (10+ formats, reachability analysis)
- VEX import/export (OpenVEX)
- Policy-based gating & license compliance
- Team management & watchlists
- Exploit-DB integration
