JFrog Artifactory integration

OSA Proxy supports static routes to JFrog Artifactory repositories and automatic repository discovery through the Artifactory inventory API. This page covers discovery configuration, client URL construction, repository context propagation, and block response behavior.

Automatic repository discovery

Runtime discovery retrieves the Artifactory repository list and creates dynamic routes for matching entries. Repositories do not need to be listed one by one in static ecosystem sections such as npm, maven, or pypi.

Discovery runs at startup and periodically afterwards. For every repository, OSA Proxy determines the ecosystem, builds the upstream URL, and applies the first matching repository-routing rule.

Configuration changes

When Configuration Store is disabled, changes in osa-proxy.yml require a restart. With Configuration Store enabled, managed discovery fields such as enabled, repository-types, repository-routing, route-mode, and the refresh interval can be changed through the Admin API without a restart. Credentials and base-url remain bootstrap-owned and require a restart.

Access requirements

Create a service account or access token that can:

  • call GET /artifactory/api/repositories to retrieve the inventory;
  • view the repositories that must be included in the API response.

In the following commands, ARTIFACTORY_BASE_URL is the platform URL without /artifactory, for example https://jfrog.example.com.

Verify Basic auth:

curl --fail --user "$ARTIFACTORY_USERNAME:$ARTIFACTORY_PASSWORD" \
  "$ARTIFACTORY_BASE_URL/artifactory/api/repositories"

Verify an access token:

curl --fail \
  --header "Authorization: Bearer $ARTIFACTORY_ACCESS_TOKEN" \
  "$ARTIFACTORY_BASE_URL/artifactory/api/repositories"

Discovery client credentials are used only for the inventory API. OSA Proxy does not automatically add them to package requests sent to discovered upstream repositories. Configure private package access separately, for example with incoming client credentials, anonymous read access, or a trusted reverse proxy.

Discovery configuration

artifactory:
  enabled: true
  base-url: https://jfrog.example.com
  username: ${ARTIFACTORY_USERNAME:}
  password: ${ARTIFACTORY_PASSWORD:}
  access-token: ${ARTIFACTORY_ACCESS_TOKEN:}
  route-mode: native
  repository-types:
    - remote
    - virtual
  refresh:
    interval: 1m
  repository-routing:
    - name: npm-consumers
      package-type: npm
      name-regex: '^npm-.*$'
      scan-manifest: true
      scan-package: true
      remove-blocked-versions: true
      work-mode: strict_wait
      url-encoded-config: true
      file-type-filter: {}
    - name: swift-consumers
      package-type: swift
      name-regex: '^swift-.*$'
      scan-manifest: true
      scan-package: true
      work-mode: strict_wait
    - name: cran-consumers
      package-type: r
      name-regex: '^cran-.*$'
      scan-package: true
      work-mode: strict_wait

Select exactly one authentication mode:

  • Basic auth: both username and password are set and access-token is empty;
  • Bearer token: access-token is set and both username and password are empty.

Set base-url without an arbitrary path. Both https://jfrog.example.com and https://jfrog.example.com/artifactory are normalized to the Artifactory root.

Allowed repository classes in repository-types are local, remote, virtual, and federated.

Selecting repositories

Every package type requires a separate rule. package-type does not support wildcards.

Omit name-regex to include all repositories of one type:

repository-routing:
  - name: all-npm
    package-type: npm
    scan-manifest: true
    scan-package: true

Key filters use Go regular expressions (RE2), not globs. Anchors ^ and $ are recommended:

repository-routing:
  - name: team-a-npm
    package-type: npm
    name-regex: '^team-a-npm-(remote|virtual)$'

Rules for one package type are evaluated from top to bottom. If a repository matches several rules, the first rule wins. Put specific expressions before a general fallback rule.

The scan-manifest, scan-package, remove-blocked-versions, work-mode, url-encoded-config, distro, and file-type-filter fields have the same meaning as in static ecosystem configuration. An unsupported combination causes a configuration error or rejects the discovery snapshot.

Supported formats

Artifactory discovery supports:

npm, maven, nuget, pypi, composer, cocoapods, swift, ruby, r, conan, go, hex, debian, alpine, and rpm.

The inventory formats gems, yum, and CRAN are normalized to ruby, rpm, and r. Docker and unknown package types are not discovered.

For Hex with scan-manifest: true, configure a shared OSA Proxy RSA key through hex.signing-private-key or hex.signing-private-key-file:

hex:
  signing-private-key: ${HEX_SIGNING_PRIVATE_KEY:}

artifactory:
  repository-routing:
    - name: hex-consumers
      package-type: hex
      name-regex: '^hex-.*$'
      scan-manifest: true
      scan-package: true

OSA Proxy retrieves the Artifactory public key from the upstream URL with the /public_key suffix, verifies the upstream signature, applies policies, and signs the modified manifest with its own RSA key. Clients retrieve the OSA Proxy public key from /{dynamic-route}/public_key. With scan-manifest disabled, signed metadata is proxied without modification. Key input methods and variable precedence are documented in Hex configuration.

Client routes

With route-mode: native, the client path mirrors the Artifactory URL:

  • /artifactory/{key} for Maven, R, Debian, Alpine, and RPM;
  • /artifactory/api/npm/{key}, /artifactory/api/pypi/{key}, /artifactory/api/swift/{key}, and equivalent API paths for the other formats.

For example, a discovered npm repository with the npm-remote key is available at:

https://osa-proxy.example.com/artifactory/api/npm/npm-remote/

The client or reverse proxy only needs to replace the Artifactory host with the OSA Proxy host while preserving the path.

With route-mode: flat, OSA Proxy uses the legacy /{key} route. A route-prefix in an individual routing rule takes precedence over route-mode; the resulting path is /{route-prefix}/{key}.

Static routes from ecosystem sections have the highest priority. If a discovered repository path conflicts with a static route, the dynamic route is skipped while the other discovered repositories continue to work.

Repository context in the URL

The url-encoded-config: true setting allows URL-safe Base64 metadata containing the repository manager URL, repository name, and user to be passed in a request. This is used for CodeScoring policies bound to a specific Artifactory repository context.

For a native route, the encoded segment is placed after the complete repository path:

/artifactory/api/npm/npm-remote/{base64-context}/{package-path}

The object format and encoding examples are documented in Base64 URL configuration.

Block responses

Requests containing the Origin-Artifactory or X-Artifactory-Originated header receive JFrog Curation-compatible JSON, the required curation headers, and HTTP 403. Header values are not interpreted; the response mode is enabled by the presence of either header.

Artifactory returns its standard response to the client and does not propagate the custom HTTP status line from OSA Proxy. Therefore, do not rely on codescoring.enable-status-line to display the Artifactory block reason to users.

Refresh and diagnostics

The first inventory sync runs at startup. Further syncs run at refresh.interval with up to 10% jitter. Added and removed repositories are applied without a restart.

When an HTTP, decoding, or route construction error occurs, OSA Proxy keeps the last successful snapshot. A successful empty inventory removes all dynamic Artifactory routes. The snapshot is stored in memory only; after a restart with an unavailable Artifactory, only static routes are available.

The /healthz endpoint checks the OSA Proxy process, not discovery status. Use these metrics:

  • artifactory_discovery_syncs_total;
  • artifactory_discovery_sync_duration_seconds;
  • artifactory_discovery_last_success_timestamp_seconds;
  • artifactory_discovery_active_repositories;
  • artifactory_discovery_repository_changes_total.
curl --fail http://localhost:8080/metrics \
  | grep '^artifactory_discovery_'

The other supported repository manager is documented in Nexus Repository integration.

Was this page helpful?