# pylynk

pylynk is the official command-line interface for the Interlynk platform. It provides programmatic access to SBOM upload, download, product management, vulnerability queries, and processing status checks — designed for both interactive use and CI/CD automation.

**Repository:** [github.com/interlynk-io/pylynk](https://github.com/interlynk-io/pylynk)

***

## When to Use pylynk

| Use Case                      | pylynk      | API (curl)  | UI             |
| ----------------------------- | ----------- | ----------- | -------------- |
| CI/CD SBOM upload             | Recommended | Possible    | Not practical  |
| Bulk vulnerability export     | Recommended | Possible    | Manual         |
| Interactive product browsing  | Good        | Verbose     | Best           |
| SBOM download with enrichment | Recommended | Complex     | Manual         |
| One-off queries               | Good        | Good        | Good           |
| Scripted automation           | Recommended | Recommended | Not applicable |

Use pylynk when you need repeatable, scriptable access to the platform. Use the API directly when you need fine-grained control over GraphQL queries. Use the UI for visual exploration and ad hoc operations.

***

## Installation

### pip (from source)

```bash
git clone https://github.com/interlynk-io/pylynk.git
cd pylynk
pip3 install -r requirements.txt
python3 pylynk.py --help
```

### Docker

```bash
# Pull the latest image
docker pull ghcr.io/interlynk-io/pylynk:latest

# Run a command
docker run -e INTERLYNK_SECURITY_TOKEN=$INTERLYNK_SECURITY_TOKEN \
  -v $(pwd):/app/data \
  ghcr.io/interlynk-io/pylynk:latest upload --prod 'my-app' --sbom /app/data/sbom.json
```

### Verify Installation

```bash
python3 pylynk.py version
```

Expected output:

```
pylynk version: v0.x.x
```

***

## Authentication

### Environment Variable (Recommended)

```bash
export INTERLYNK_SECURITY_TOKEN="lynk_service_your_token_here"
```

This is the recommended method for both local use and CI/CD. The token is read automatically by all commands.

### Command-Line Flag

```bash
python3 pylynk.py prods --token "lynk_service_your_token_here"
```

{% hint style="warning" %}
Avoid `--token` in CI/CD — the value may appear in build logs. Always use the environment variable.
{% endhint %}

### API Endpoint Override

The default API endpoint is `https://api.interlynk.io/lynkapi`. Override it with:

```bash
export INTERLYNK_API_URL="https://custom-api.example.com/lynkapi"
```

### Token Security Best Practices

* Store tokens in a secrets manager (Vault, AWS Secrets Manager, GitHub Secrets).
* Never commit tokens to source control.
* Use service tokens for automation; user tokens for interactive sessions.
* Set expiration dates — 90 days for CI/CD, 30 days for development.
* Rotate tokens proactively. See [Rotation Strategy](https://docs.interlynk.io/administration/api-key-management#rotation-strategy).

***

## Core Commands

### prods — List Products

Lists all products in the organization.

```bash
python3 pylynk.py prods
python3 pylynk.py prods --output json
python3 pylynk.py prods --output csv
python3 pylynk.py prods --human-time
```

| Parameter       | Required | Default | Description                                   |
| --------------- | -------- | ------- | --------------------------------------------- |
| `--output`      | No       | `table` | Output format: `table`, `json`, `csv`         |
| `--human-time`  | No       | Off     | Show relative timestamps (e.g., "2 days ago") |
| `--token`       | No       | Env var | Override authentication token                 |
| `-v, --verbose` | No       | Off     | Enable debug output                           |

**Output columns:** NAME, ID, VERSIONS, UPDATED AT

***

### vers — List Versions

Lists all versions (SBOMs) for a product.

```bash
python3 pylynk.py vers --prod 'my-app'
python3 pylynk.py vers --prod 'my-app' --env 'production' --output json
```

| Parameter       | Required | Default   | Description                           |
| --------------- | -------- | --------- | ------------------------------------- |
| `--prod`        | Yes      | —         | Product name                          |
| `--env`         | No       | `default` | Environment name                      |
| `--output`      | No       | `table`   | Output format: `table`, `json`, `csv` |
| `--human-time`  | No       | Off       | Relative timestamps                   |
| `-v, --verbose` | No       | Off       | Debug output                          |

**Output columns:** ID, VERSION, PRIMARY COMPONENT, UPDATED AT

Use the `ID` value from this output as `--verId` in other commands.

***

### upload — Upload SBOM

Uploads an SBOM file to the platform.

```bash
# Upload to default environment
python3 pylynk.py upload --prod 'my-app' --sbom sbom.json

# Upload to a specific environment
python3 pylynk.py upload --prod 'my-app' --env 'production' --sbom sbom.json

# Upload with custom retry count
python3 pylynk.py upload --prod 'my-app' --sbom sbom.json --retries 5
```

| Parameter       | Required | Default   | Description                       |
| --------------- | -------- | --------- | --------------------------------- |
| `--prod`        | Yes      | —         | Product name                      |
| `--sbom`        | Yes      | —         | Path to SBOM file                 |
| `--env`         | No       | `default` | Target environment                |
| `--retries`     | No       | `3`       | Retry count on transient failures |
| `-v, --verbose` | No       | Off       | Debug output                      |

**Supported formats:** CycloneDX (JSON, XML), SPDX (JSON, tag-value)

**Retry behavior:**

| Condition        | Behavior                                      |
| ---------------- | --------------------------------------------- |
| 5xx server error | Retries with exponential backoff (1s, 2s, 4s) |
| 429 rate limit   | Retries with exponential backoff              |
| 401 unauthorized | Fails immediately — no retry                  |
| Other 4xx        | Fails immediately — no retry                  |
| Network error    | Retries with exponential backoff              |

**CI metadata:** When running in a CI environment (`CI=true`), pylynk automatically captures build metadata (PR number, commit SHA, branch name, build URL) and sends it as HTTP headers with the upload.

***

### download — Download SBOM

Downloads an SBOM from the platform, optionally enriched with vulnerabilities, support status, or converted to a different format.

```bash
# Download by version ID
python3 pylynk.py download --verId 'abc-123' --out-file sbom.json

# Download by product/environment/version
python3 pylynk.py download --prod 'my-app' --env 'default' --ver 'v1.0.0' --out-file sbom.json

# Download with vulnerabilities included
python3 pylynk.py download --verId 'abc-123' --vuln true --out-file enriched.json

# Download in a specific format
python3 pylynk.py download --verId 'abc-123' --spec CycloneDX --spec-version 1.5

# Download the original uploaded SBOM (unmodified)
python3 pylynk.py download --verId 'abc-123' --original --out-file original.json

# Download a lite version
python3 pylynk.py download --verId 'abc-123' --lite --out-file lite.json

# Include support status metadata
python3 pylynk.py download --verId 'abc-123' --include-support-status --out-file sbom.json

# Export support levels only (CSV)
python3 pylynk.py download --verId 'abc-123' --support-level-only --out-file support.csv
```

**Identification — provide one of:**

| Method        | Parameters Required          |
| ------------- | ---------------------------- |
| By version ID | `--verId`                    |
| By name       | `--prod` + `--env` + `--ver` |

| Parameter                  | Required    | Default  | Description                              |
| -------------------------- | ----------- | -------- | ---------------------------------------- |
| `--verId`                  | Conditional | —        | Version ID (from `vers` command output)  |
| `--prod`                   | Conditional | —        | Product name                             |
| `--env`                    | Conditional | —        | Environment name                         |
| `--ver`                    | Conditional | —        | Version name                             |
| `--out-file`               | No          | stdout   | Output file path                         |
| `--vuln`                   | No          | `false`  | Include vulnerabilities (`true`/`false`) |
| `--spec`                   | No          | Original | Output spec: `SPDX` or `CycloneDX`       |
| `--spec-version`           | No          | Original | Spec version (e.g., `2.3`, `1.5`)        |
| `--lite`                   | No          | Off      | Download lightweight version             |
| `--original`               | No          | Off      | Download exact uploaded SBOM             |
| `--dont-package-sbom`      | No          | Off      | Keep multi-SBOM files separate           |
| `--exclude-parts`          | No          | Off      | Exclude linked parts                     |
| `--include-support-status` | No          | Off      | Add support status metadata              |
| `--support-level-only`     | No          | Off      | CSV output with support levels only      |

***

### vulns — List Vulnerabilities

Queries vulnerabilities across products.

```bash
# All vulnerabilities for a product
python3 pylynk.py vulns --prod 'my-app'

# With vulnerability and VEX details
python3 pylynk.py vulns --prod 'my-app' --vuln-details --vex-details

# Specific version
python3 pylynk.py vulns --prod 'my-app' --verId 'abc-123'

# JSON export
python3 pylynk.py vulns --prod 'my-app' --output json > vulns.json

# CSV export for spreadsheet analysis
python3 pylynk.py vulns --prod 'my-app' --output csv > vulns.csv

# Custom column selection
python3 pylynk.py vulns --prod 'my-app' --columns 'id,component_name,severity,cvss,status'

# List all available columns
python3 pylynk.py vulns --list-columns
```

| Parameter             | Required | Default     | Description                                                |
| --------------------- | -------- | ----------- | ---------------------------------------------------------- |
| `--prod`              | No       | —           | Product name (uses latest version if no version specified) |
| `--env`               | No       | `default`   | Environment name                                           |
| `--verId`             | No       | —           | Specific version ID                                        |
| `--ver`               | No       | —           | Version name                                               |
| `--output`            | No       | `table`     | Output format: `table`, `json`, `csv`                      |
| `--columns`           | No       | Default set | Comma-separated column names                               |
| `--vuln-details`      | No       | Off         | Include severity, CVSS, EPSS, KEV, CWE                     |
| `--vex-details`       | No       | Off         | Include VEX status, justification, actions                 |
| `--timestamp-details` | No       | Off         | Include all timestamp columns                              |
| `--human-time`        | No       | Off         | Relative timestamps                                        |
| `--list-columns`      | No       | —           | Display available column names and exit                    |

**Default columns:** id, part\_name, part\_version, component\_name, component\_version, severity, source, status, assigned

**Vulnerability detail columns (`--vuln-details`):** severity, kev, cvss, cvss\_vector, epss, cwe

**VEX detail columns (`--vex-details`):** status, details, justification, action\_statement, impact\_statement, response

***

### status — Check Processing Status

Checks the processing status of an uploaded SBOM.

```bash
python3 pylynk.py status --prod 'my-app' --verId 'abc-123'
python3 pylynk.py status --prod 'my-app' --env 'production' --ver 'v1.0.0'
```

| Parameter  | Required    | Default   | Description                     |
| ---------- | ----------- | --------- | ------------------------------- |
| `--prod`   | Yes         | —         | Product name                    |
| `--verId`  | Conditional | —         | Version ID                      |
| `--ver`    | Conditional | —         | Version name (requires `--env`) |
| `--env`    | No          | `default` | Environment name                |
| `--output` | No          | `table`   | Output format: `table`, `json`  |

**Status types tracked:**

| Status           | Description                    |
| ---------------- | ------------------------------ |
| checksStatus     | SBOM quality and format checks |
| policyStatus     | Policy evaluation              |
| labelingStatus   | Internal labeling              |
| automationStatus | Automation rule execution      |
| vulnScanStatus   | Vulnerability scanning         |

**Status values:** `UNKNOWN`, `NOT_STARTED`, `IN_PROGRESS`, `COMPLETED`

***

### version — Show CLI Version

```bash
python3 pylynk.py version
```

***

## CI/CD Integration

### GitHub Actions

```yaml
name: Upload SBOM
on:
  push:
    branches: [main]
  release:
    types: [published]

jobs:
  upload-sbom:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Generate SBOM
        run: |
          # Use your preferred SBOM generator
          syft . -o cyclonedx-json > sbom.cdx.json

      - name: Upload SBOM to Interlynk
        env:
          INTERLYNK_SECURITY_TOKEN: ${{ secrets.INTERLYNK_SERVICE_TOKEN }}
        run: |
          pip3 install -r requirements.txt
          python3 pylynk.py upload --prod '${{ github.event.repository.name }}' --sbom sbom.cdx.json
```

### GitLab CI

```yaml
upload-sbom:
  stage: deploy
  image: python:3.11
  variables:
    INTERLYNK_SECURITY_TOKEN: $INTERLYNK_SERVICE_TOKEN
  script:
    - pip3 install -r requirements.txt
    - python3 pylynk.py upload --prod 'my-app' --sbom sbom.cdx.json
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
    - if: '$CI_COMMIT_TAG'
```

### Bitbucket Pipelines

```yaml
pipelines:
  branches:
    main:
      - step:
          name: Upload SBOM
          image: python:3.11
          script:
            - pip3 install -r requirements.txt
            - python3 pylynk.py upload --prod 'my-app' --sbom sbom.cdx.json
  tags:
    'v*':
      - step:
          name: Upload Release SBOM
          image: python:3.11
          script:
            - pip3 install -r requirements.txt
            - python3 pylynk.py upload --prod 'my-app' --sbom sbom.cdx.json
```

### Azure DevOps

```yaml
trigger:
  branches:
    include: [main]
  tags:
    include: ['v*']

steps:
  - script: pip3 install -r requirements.txt
    displayName: 'Install pylynk'
  - script: python3 pylynk.py upload --prod 'my-app' --sbom sbom.cdx.json
    displayName: 'Upload SBOM'
    env:
      INTERLYNK_SECURITY_TOKEN: $(INTERLYNK_SERVICE_TOKEN)
```

### Docker-Based CI

For environments where Python installation is not practical:

```yaml
# GitHub Actions with Docker
- name: Upload SBOM
  run: |
    docker run --rm \
      -e INTERLYNK_SECURITY_TOKEN=${{ secrets.INTERLYNK_SERVICE_TOKEN }} \
      -v $(pwd):/app/data \
      ghcr.io/interlynk-io/pylynk:latest \
      upload --prod 'my-app' --sbom /app/data/sbom.cdx.json
```

### Handling Secrets Securely

| CI Platform    | Secret Storage                 | Reference Syntax                         |
| -------------- | ------------------------------ | ---------------------------------------- |
| GitHub Actions | Repository Secrets             | `${{ secrets.INTERLYNK_SERVICE_TOKEN }}` |
| GitLab CI      | CI/CD Variables (masked)       | `$INTERLYNK_SERVICE_TOKEN`               |
| Bitbucket      | Repository Variables (secured) | `$INTERLYNK_SERVICE_TOKEN`               |
| Azure DevOps   | Pipeline Variables (secret)    | `$(INTERLYNK_SERVICE_TOKEN)`             |

{% hint style="warning" %}
Always mark the variable as **secret/masked** in your CI platform to prevent it from appearing in build logs.
{% endhint %}

### Fail-the-Build Patterns

Use exit codes to gate builds on SBOM upload success:

```bash
# Fail if upload fails
python3 pylynk.py upload --prod 'my-app' --sbom sbom.json || exit 1

# Upload and verify processing completed
python3 pylynk.py upload --prod 'my-app' --sbom sbom.json
sleep 10
python3 pylynk.py status --prod 'my-app' --verId "$VERSION_ID" --output json | \
  python3 -c "import sys, json; d=json.load(sys.stdin); sys.exit(0 if all(s=='COMPLETED' for s in d.values()) else 1)"
```

### CI Metadata

When `CI=true` is set (automatically by most CI platforms), pylynk captures build metadata and sends it with uploads:

| Header               | Description                                 |
| -------------------- | ------------------------------------------- |
| `X-CI-Provider`      | CI platform name                            |
| `X-Event-Type`       | Trigger type (push, pull\_request, release) |
| `X-PR-Number`        | Pull request number                         |
| `X-Commit-SHA`       | Git commit hash                             |
| `X-Repository-URL`   | Repository URL                              |
| `X-Build-URL`        | Build/job URL                               |
| `X-PR-Source-Branch` | Source branch name                          |
| `X-PR-Target-Branch` | Target branch name                          |

Control metadata collection:

```bash
export PYLYNK_INCLUDE_CI_METADATA=auto   # Default — enabled only in CI
export PYLYNK_INCLUDE_CI_METADATA=true   # Force enable
export PYLYNK_INCLUDE_CI_METADATA=false  # Disable
```

***

## Troubleshooting

### Debug Mode

Enable verbose logging with `-v` or `--verbose`:

```bash
python3 pylynk.py upload --prod 'my-app' --sbom sbom.json -v
python3 pylynk.py prods -vv  # More detailed output
```

Debug output includes: API request/response details, token validation (masked), CI metadata detection, upload speed metrics, and timing information.

### Common Errors

| Error                                  | Cause                              | Resolution                                                   |
| -------------------------------------- | ---------------------------------- | ------------------------------------------------------------ |
| `Security token not found`             | `INTERLYNK_SECURITY_TOKEN` not set | Export the environment variable or use `--token`             |
| `Authentication failed`                | Invalid or expired token           | Verify token in UI; create a new one if expired              |
| `Product not found`                    | Typo in product name or no access  | Run `pylynk prods` to list available products                |
| `Version not found`                    | Invalid version ID or name         | Run `pylynk vers --prod 'name'` to list versions             |
| `File not found`                       | Bad SBOM file path                 | Verify path; use absolute paths in Docker                    |
| `Request failed with status code: 429` | Rate limited                       | Automatic retry handles this; increase `--retries` if needed |
| `RequestException`                     | Network or connectivity issue      | Check firewall, proxy, and `INTERLYNK_API_URL`               |
| `Error uploading sbom`                 | Invalid SBOM format                | Verify SBOM is valid CycloneDX or SPDX                       |

### Exit Codes

| Code | Meaning                |
| ---- | ---------------------- |
| `0`  | Success                |
| `1`  | General error          |
| `2`  | Argument parsing error |

***

## Best Practices

### Least Privilege Token Usage

Create service tokens with the minimum role required:

| Workflow                      | Recommended Role                   |
| ----------------------------- | ---------------------------------- |
| SBOM upload only              | Custom role with upload permission |
| Upload + vulnerability review | Operator                           |
| Full automation               | Admin (use sparingly)              |
| Read-only reporting           | Viewer                             |

### Automation Design Patterns

* **Idempotent uploads:** Uploading the same SBOM twice to the same product/environment creates a new version. Design pipelines to upload only on meaningful changes.
* **Environment separation:** Use `--env` to separate staging, production, and development SBOMs.
* **Version tracking:** Capture the version ID from `vers` output after upload for downstream status checks.
* **Error handling:** Always check exit codes. Use `--retries 0` to disable retries when you need fast failure.

### Parallel vs Serial Execution

* **Serial:** Upload SBOMs for the same product sequentially to avoid race conditions on version ordering.
* **Parallel:** Upload SBOMs for different products concurrently. Each product is independent.
* **Status polling:** Wait for `COMPLETED` status before downloading enriched SBOMs. Processing is asynchronous.

***

## Common Misconfigurations

| Issue                         | Symptom                         | Fix                                                  |
| ----------------------------- | ------------------------------- | ---------------------------------------------------- |
| Token in `--token` flag in CI | Token visible in build logs     | Use `INTERLYNK_SECURITY_TOKEN` env var               |
| Wrong `INTERLYNK_API_URL`     | Connection errors or 401        | Verify URL or remove the override to use default     |
| Missing `--prod` on upload    | Argument parsing error          | Always specify `--prod` with upload                  |
| Docker volume not mounted     | File not found inside container | Add `-v $(pwd):/app/data` and use `/app/data/` paths |
| Using `--ver` without `--env` | Version not found               | Provide both `--env` and `--ver` together            |
| Old pylynk version            | Unexpected API errors           | Pull latest from repository or Docker image          |
