업데이트로 돌아가기
New releaseAug 31, 2026

proxy v0.7.0

패키지 레지스트리용 경량 캐싱 프록시.

공유

git-pkgs 프록시

패키지 레지스트리용 캐싱 프록시입니다. 아티팩트를 로컬에 캐싱하여 패키지 다운로드 속도를 높이고, 대역폭 사용량을 줄이며 안정성을 개선합니다.

버전 쿨다운

대부분의 공급망 공격은 속도에 의존합니다. 악성 버전이 게시되면 아무도 알아차리기 전에 수 분 내에 자동화된 파이프라인에 의해 소비됩니다. 쿨다운 기능은 새로 게시된 버전에 격리 기간을 추가합니다. 활성화되면 프록시는 버전이 구성 가능한 임계값을 넘어설 때까지 메타데이터 응답에서 버전을 제거합니다.```yaml cooldown: default: "3d" # hide versions published less than 3 days ago ecosystems: npm: "7d" # npm gets a longer window cargo: "0" # disable for cargo packages: "pkg:npm/lodash": "0" # exempt trusted packages

3일 쿨다운은 `lodash`가 버전 `4.18.0`을 게시해도 3일이 지나기 전까지는 빌드가 계속 `4.17.21`을 사용한다는 뜻입니다. 새 릴리스가 손상된 것으로 밝혀져도 노출되지 않습니다.

해석 순서: 패키지 오버라이드, 그다음 에코시스템 오버라이드, 마지막으로 전역 기본값입니다. 이를 통해 보수적인 기본값을 설정하고 더 빠른 업데이트가 필요한 패키지에 대해 예외를 지정할 수 있습니다. 전체 구성 참조는 [docs/configuration.md](https://github.com/git-pkgs/proxy/blob/main/docs/configuration.md)를 참조하세요.

## 지원되는 레지스트리

| 레지스트리 | 언어/플랫폼 | 쿨다운 | 완료 |
|----------|-------------------|:--------:|:---------:|
| npm | JavaScript | 예 | ✓ |
| Cargo | Rust | 예 | ✓ |
| RubyGems | Ruby | 예 | ✓ |
| Go proxy | Go | | ✓ |
| Hex | Elixir | 예* | ✓ |
| pub.dev | Dart | 예 | ✓ |
| PyPI | Python | 예 | ✓ |
| Maven | Java | | ✓ |
| Gradle Build Cache | Java/Kotlin | | ✓ |
| NuGet | .NET | 예 | ✓ |
| Composer | PHP | 예 | ✓ |
| Conan | C/C++ | | ✓ |
| Conda | Python/R | 예 | ✓ |
| CRAN | R | | ✓ |
| Julia | Julia | | ✓ |
| Container | Docker/OCI | | ✓ |
| Debian | Debian/Ubuntu | | ✓ |
| RPM | RHEL/Fedora | | ✓ |
| Alpine | Alpine Linux | | ✗ |
| Arch | Arch Linux | | ✗ |
| Chef | Chef | | ✗ |
| Generic | 모든 | | ✗ |
| Helm | Kubernetes | | ✗ |
| Swift | Swift | | ✗ |
| Vagrant | Vagrant | | ✗ |

쿨다운에는 메타데이터의 게시 타임스탬프가 필요합니다. 쿨다운 열에 "예"가 없는 레지스트리는 타임스탬프를 노출하지 않거나 아직 연결되지 않은 것입니다.

\* Hex 쿨다운은 프록시가 protobuf 페이로드를 다시 인코딩하므로 레지스트리 서명 검증을 비활성화해야 합니다(`HEX_NO_VERIFY_REPO_ORIGIN=1`).

## 설치```bash
brew install git-pkgs/git-pkgs/proxy

Or 릴리스 페이지에서 바이너리를 다운로드하세요.

빠른 시작```bash

Build from source

go build -o proxy ./cmd/proxy

Run with defaults (listens on :8080)

./proxy

Run with custom settings

./proxy -listen :3000 -base-url https://proxy.example.com

프록시가 실행 중입니다. 패키지 관리자가 이를 사용하도록 구성하세요.

## OpenAPI (Swagger)

이 저장소는 swaggo를 사용하여 주석이 달린 핸들러에서 OpenAPI 스펙을 생성합니다.

스펙 생성:```bash
go install github.com/swaggo/swag/cmd/swag@latest
go generate ./internal/server

Generated files are written to docs/swagger/.

When the proxy is running, fetch the live spec from:

  • http://localhost:8080/openapi.json

Or replace http://localhost:8080 with your configured base URL. This link is also shown on the dashboard.

Configuring Package Managers

npm

Create or edit ~/.npmrc:``` registry=http://localhost:8080/npm/

Or set per-project in `.npmrc`:```
registry=http://localhost:8080/npm/

Or use environment variable:

환경 변수를 사용하세요:```bash npm_config_registry=http://localhost:8080/npm/ npm install

### Cargo

`~/.cargo/config.toml` 파일을 생성하거나 편집합니다:```toml
[source.crates-io]
replace-with = "proxy"

[source.proxy]
registry = "sparse+http://localhost:8080/cargo/"

Or set per-project in .cargo/config.toml in your project root.

RubyGems / Bundler

Set the gem source in your Gemfile:```ruby source "http://localhost:8080/gem"

Or 전역으로 구성하려면:```bash
gem sources --add http://localhost:8080/gem/
bundle config mirror.https://rubygems.org http://localhost:8080/gem

Go 모듈

GOPROXY 환경 변수를 설정하세요:```bash export GOPROXY=http://localhost:8080/go,direct

Or in your shell profile for persistence.

### Hex (Elixir)

Configure in `~/.hex/hex.config`:```erlang
{default_url, <<"http://localhost:8080/hex">>}.

Or set the environment variable:```bash export HEX_MIRROR=http://localhost:8080/hex

### pub.dev (Dart/Flutter)

`PUB_HOSTED_URL` 환경 변수를 설정하세요:```bash
export PUB_HOSTED_URL=http://localhost:8080/pub

PyPI (pip)

pip이 프록시를 사용하도록 구성합니다:```bash pip install --index-url http://localhost:8080/pypi/simple/ package_name

Or set in `~/.pip/pip.conf`:```ini
[global]
index-url = http://localhost:8080/pypi/simple/

Maven

~/.m2/settings.xml에 다음을 추가하세요:```xml proxy central http://localhost:8080/maven/

`/maven/` 엔드포인트는 Maven Central을 기본 업스트림으로 사용하며, 기본 업스트림에서 not found가 반환되면 Gradle 플러그인 마커 메타데이터 및 관련 아티팩트에 대해 Gradle Plugin Portal로 폴백합니다.

동일한 프록시 엔드포인트를 통한 Gradle 플러그인 해석의 경우:```kotlin
pluginManagement {
  repositories {
    maven(url = "http://localhost:8080/maven/")
  }
}

Gradle HTTP 빌드 캐시

settings.gradle(.kts)에서 구성하세요:```kotlin buildCache { local { enabled = false } remote { url = uri("http://localhost:8080/gradle/") push = true } }

### NuGet

`nuget.config`에서 구성하세요:```xml
<configuration>
  <packageSources>
    <clear />
    <add key="proxy" value="http://localhost:8080/nuget/v3/index.json" />
  </packageSources>
</configuration>

Or use the CLI:```bash dotnet nuget add source http://localhost:8080/nuget/v3/index.json -n proxy

### Composer (PHP)

`composer.json`에 다음과 같이 구성하세요:```json
{
    "repositories": [
        {
            "type": "composer",
            "url": "http://localhost:8080/composer"
        }
    ]
}

Or set globally:```bash composer config -g repositories.proxy composer http://localhost:8080/composer

### Conan (C/C++)

프록시를 원격 저장소로 추가:```bash
conan remote add proxy http://localhost:8080/conan
conan remote disable conancenter

Or configure in ~/.conan2/remotes.json.

Conda

Configure in ~/.condarc:```yaml channels:

Or set via command:

명령어로 설정할 수도 있습니다:```bash
conda config --add channels http://localhost:8080/conda/main

CRAN (R)

R에서 저장소를 설정합니다:```r options(repos = c(CRAN = "http://localhost:8080/cran"))

Or in `~/.Rprofile` for persistence:```r
local({
  r <- getOption("repos")
  r["CRAN"] <- "http://localhost:8080/cran"
  options(repos = r)
})

Julia

Julia를 시작하기 전에 Pkg 서버를 설정하세요:```bash export JULIA_PKG_SERVER=http://localhost:8080/julia

Or inside a running session:```julia
ENV["JULIA_PKG_SERVER"] = "http://localhost:8080/julia"
using Pkg; Pkg.update()

Docker / 컨테이너 레지스트리

프록시를 레지스트리 미러로 사용하도록 /etc/docker/daemon.json에서 Docker를 구성합니다:```json { "registry-mirrors": ["http://localhost:8080"] }

그런 다음 Docker를 재시작합니다:```bash
sudo systemctl restart docker

Or pull images directly:

이미지를 직접 가져오려면:```bash docker pull localhost:8080/library/nginx:latest

### Helm

각 HTTP 차트 저장소를 이름과 함께 구성한 다음, 일치하는 프록시 URL을 Helm에 추가합니다:```yaml
upstream:
  helm:
    bitnami: "https://charts.bitnami.com/bitnami"

I need the input content to translate. Please provide the chunk 69/165 content.```bash helm repo add bitnami http://localhost:8080/helm/bitnami helm repo update helm pull bitnami/nginx

The proxy caches `index.yaml` using the normal metadata-cache settings and
caches chart archives after verifying their SHA-256 digest from the index.

For charts stored in an OCI registry, configure a named OCI upstream and add
the reserved `upstream/{name}` prefix to the chart reference:```yaml
upstream:
  oci:
    ghcr: "https://ghcr.io"
## 🛡️ 보안 기능

- **암호화된 통신**: 모든 클라이언트-서버 통신은 TLS 1.3을 사용하여 암호화됩니다.
- **다중 인증**: 사용자 이름/비밀번호, TOTP, 하드웨어 키를 포함한 다중 인증 방법을 지원합니다.
- **역할 기반 접근 제어(RBAC)**: 세분화된 권한으로 사용자 역할을 정의하여 민감한 작업에 대한 접근을 제한합니다.
- **감사 로깅**: 모든 중요한 작업은 규정 준수 및 포렌식 분석을 위해 상세한 감사 로그에 기록됩니다.
- **입력 검증**: 서버 측 입력 검증을 통해 주입 공격을 방지합니다.
- **세션 관리**: 안전한 세션 토큰과 자동 만료로 세션 하이재킹을 방지합니다.
- **속도 제한**: 무차별 대입 공격을 완화하기 위해 API 엔드포인트에 속도 제한을 적용합니다.
- **보안 헤더**: X-Frame-Options, CSP, HSTS 등과 같은 보안 헤더를 자동으로 설정합니다.
``````bash
helm pull oci://localhost:8080/upstream/ghcr/owner/charts/mychart --version 1.0.0 --plain-http

Debian / APT

APT가 /etc/apt/sources.list.d/proxy.list에서 프록시를 사용하도록 구성합니다:``` deb http://localhost:8080/debian stable main contrib

Replace your existing sources.list entries, then:```bash
sudo apt update

업스트림 기본값은 http://deb.debian.org/debian입니다. 다른 APT 저장소(예: Ubuntu)를 프록시하려면 구성 파일에서 upstream.debian을 설정하거나 환경 변수에서 PROXY_UPSTREAM_DEBIAN을 설정하세요:```yaml upstream: debian: "http://archive.ubuntu.com/ubuntu"

### RPM / Yum / DNF

`/etc/yum.repos.d/proxy.repo`에서 yum/dnf가 프록시를 사용하도록 구성합니다:```ini
[proxy-fedora]
name=Fedora via Proxy
baseurl=http://localhost:8080/rpm/releases/$releasever/Everything/$basearch/os/
enabled=1
gpgcheck=0

그런 다음:```bash sudo dnf clean all sudo dnf update

## Configuration

프록시는 다음을 통해 구성할 수 있습니다:

1. 명령줄 플래그 (최우선 순위)
2. 환경 변수
3. 구성 파일 (YAML 또는 JSON)

### 명령줄 플래그```
-config string           Path to configuration file
-listen string           Address to listen on (default ":8080")
-base-url string         Public URL of this proxy (default "http://localhost:8080")
-storage-url string      Storage URL (file://, s3://, gs://, azblob://)
-storage-path string     Path to artifact storage directory (deprecated, use -storage-url)
-database-driver string  Database driver: sqlite or postgres (default "sqlite")
-database-path string    Path to SQLite database file (default "./cache/proxy.db")
-database-url string     PostgreSQL connection URL
-log-level string        Log level: debug, info, warn, error (default "info")
-log-format string       Log format: text, json (default "text")
-access-log string       Path to the JSONL access log
-version                 Print version and exit

환경 변수```bash

PROXY_LISTEN=:8080 PROXY_BASE_URL=http://localhost:8080 PROXY_UI_URL=http://localhost:8080 # Optional; defaults to PROXY_BASE_URL PROXY_STORAGE_URL=file:///var/cache/proxy/artifacts PROXY_DATABASE_DRIVER=sqlite PROXY_DATABASE_PATH=./cache/proxy.db PROXY_DATABASE_URL=postgres://user:pass@localhost/proxy?sslmode=disable PROXY_LOG_LEVEL=info PROXY_LOG_FORMAT=text PROXY_ACCESS_LOG_PATH=/var/log/proxy/access.jsonl

### 구성 파일```yaml
listen: ":8080"
base_url: "http://localhost:8080"

storage:
  url: "file:///var/cache/proxy/artifacts"
  max_size: "10GB"  # Optional: evict LRU when exceeded

database:
  driver: "sqlite"
  path: "/var/lib/proxy/cache.db"

log:
  level: "info"
  format: "text"

access_log:
  path: "/var/log/proxy/access.jsonl"  # Optional JSONL activity log

# Optional: override upstream URLs
upstream:
  npm: "https://registry.npmjs.org"
  cargo: "https://index.crates.io"

# Optional: version cooldown (see above)
cooldown:
  default: "3d"

구성 참조(configuration reference)에서 모든 업스트림 키, 환경 변수 및 기본 URL을 확인하세요.

구성 파일로 실행:```bash ./proxy -config /etc/proxy/config.yaml

### PostgreSQL

SQLite는 기본값이며 단일 노드 배포에 잘 작동합니다. 다중 노드 설정이거나 관리형 데이터베이스를 선호하는 경우 Postgres로 전환하세요:```yaml
database:
  driver: "postgres"
  url: "postgres://user:password@localhost:5432/proxy?sslmode=disable"

Or via environment variables:

환경 변수를 통해서도 가능합니다:```bash PROXY_DATABASE_DRIVER=postgres PROXY_DATABASE_URL=postgres://user:password@localhost:5432/proxy?sslmode=disable

프록시는 첫 실행 시 테이블을 자동으로 생성합니다.

### S3 스토리지

프록시는 로컬 파일시스템 대신 S3 또는 S3 호환 서비스(MinIO, R2 등)에 캐시된 아티팩트를 저장할 수 있습니다.```yaml
storage:
  url: "s3://my-bucket-name?region=us-east-1"

MinIO와 같은 S3 호환 서비스의 경우:```yaml storage: url: "s3://my-bucket?endpoint=http://localhost:9000&disableSSL=true&s3ForcePathStyle=true"

표준 AWS 환경 변수(`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`)를 통해 자격 증명을 설정합니다.

### Google Cloud Storage

프록시는 `gs://` URL 스킴을 사용하여 GCS 버킷에 캐시된 아티팩트를 저장할 수 있습니다.```yaml
storage:
  url: "gs://my-bucket-name"

인증은 Application Default Credentials을 사용하므로, 구성 파일이나 환경에 자격 증명을 포함할 필요가 없습니다. 지원되는 소스는 우선순위 순서대로 다음과 같습니다:

  • GKE Workload Identity — 프록시를 실행하는 Kubernetes 서비스 계정을 버킷에 대해 roles/storage.objectAdmin 권한이 있는 Google 서비스 계정에 바인딩합니다. 프록시는 워크로드의 토큰을 자동으로 사용합니다.
  • GCE, Cloud Run, Cloud Functions 등에서의 연결된 서비스 계정.
  • 서비스 계정 JSON 키 파일을 가리키는 GOOGLE_APPLICATION_CREDENTIALS 환경 변수.
  • 로컬 개발을 위한 gcloud auth application-default login.

GKE Workload Identity 설정```bash

1. Create a Google service account

gcloud iam service-accounts create git-pkgs-proxy
--project=PROJECT_ID

2. Grant it access to the bucket

gsutil iam ch
serviceAccount:git-pkgs-proxy@PROJECT_ID.iam.gserviceaccount.com:objectAdmin
gs://my-bucket-name

3. Bind the Kubernetes service account to it

gcloud iam service-accounts add-iam-policy-binding
git-pkgs-proxy@PROJECT_ID.iam.gserviceaccount.com
--role=roles/iam.workloadIdentityUser
--member="serviceAccount:PROJECT_ID.svc.id.goog[NAMESPACE/KSA_NAME]"

4. Annotate the Kubernetes service account

kubectl annotate serviceaccount KSA_NAME
--namespace=NAMESPACE
iam.gke.io/gcp-service-account=git-pkgs-proxy@PROJECT_ID.iam.gserviceaccount.com

#### Workload Identity를 사용한 직접 서빙(서명된 URL)

`direct_serve: true`가 활성화되면 프록시는 사전 서명된 GCS URL로 HTTP 302 리디렉션을 발급합니다. Workload Identity는 개인 키를 제공하지 않으므로 GCS 백엔드는 [IAM Credentials `signBlob` API](https://docs.cloud.google.com/iam/docs/reference/credentials/rest/v1/projects.serviceAccounts/signBlob)를 호출합니다. 서비스 계정에 자기 자신에 대한 token-creator 역할을 부여하세요:```bash
gcloud iam service-accounts add-iam-policy-binding \
  git-pkgs-proxy@PROJECT_ID.iam.gserviceaccount.com \
  --role=roles/iam.serviceAccountTokenCreator \
  --member="serviceAccount:git-pkgs-proxy@PROJECT_ID.iam.gserviceaccount.com"

CLI 명령어

serve (기본값)

프록시 서버를 시작합니다. 다른 명령어가 지정되지 않은 경우 기본 명령어입니다.```bash proxy serve [flags] proxy [flags] # same as 'proxy serve'

### mirror

PURL, SBOM 파일 또는 전체 레지스트리에서 캐시를 미리 채웁니다. 오프라인 가용성을 보장하거나 배포 전에 캐시를 워밍업하는 데 유용합니다.```bash
# Mirror specific package versions
proxy mirror pkg:npm/[email protected] pkg:cargo/[email protected]

# Mirror all versions of a package
proxy mirror pkg:npm/lodash

# Mirror from a CycloneDX or SPDX SBOM
proxy mirror --sbom sbom.cdx.json

# Preview what would be mirrored
proxy mirror --dry-run pkg:npm/lodash

# Control parallelism
proxy mirror --concurrency 8 pkg:npm/[email protected]

미러 명령은 serve와 동일한 스토리지 및 데이터베이스 플래그를 허용합니다. 이미 캐시된 아티팩트는 건너뜁니다.

서버가 실행 중일 때 미러 API도 사용할 수 있습니다:```bash

Start a mirror job

curl -X POST http://localhost:8080/api/mirror
-H "Content-Type: application/json"
-d '{"purls": ["pkg:npm/[email protected]"]}'

Check job status

curl http://localhost:8080/api/mirror/mirror-1

Cancel a running job

curl -X DELETE http://localhost:8080/api/mirror/mirror-1

### stats

서버를 실행하지 않고 캐시 통계를 표시합니다.```bash
# Text output
proxy stats

# JSON output
proxy stats -json

# Custom database path
proxy stats -database-path /var/lib/proxy/cache.db

# With PostgreSQL
proxy stats -database-driver postgres -database-url postgres://user:pass@localhost/proxy

# Show top 20 most popular packages
proxy stats -popular 20

예제 출력:``` Cache Statistics

Packages: 45 Versions: 128 Artifacts: 128 Total size: 892.4 MB Total hits: 1547

Packages by ecosystem: npm 32 cargo 13

Most popular packages:

  1. npm/lodash (342 hits, 24.7 KB)
  2. npm/react (198 hits, 89.3 KB)
  3. cargo/serde (156 hits, 234.1 KB)

Recently cached: npm/[email protected] (2024-01-15 14:32, 54.2 KB) cargo/[email protected] (2024-01-15 14:28, 412.8 KB)

## API 엔드포인트

### 레지스트리 프로토콜

| 엔드포인트 | 설명 |
|----------|-------------|
| `GET /` | 대시보드 (웹 UI) |
| `GET /health` | 상태 확인 (JSON; HTTP 200 정상, 503 비정상) |
| `GET /stats` | 캐시 통계 (JSON) |
| `GET /metrics` | Prometheus 메트릭 |
| `GET /npm/*` | npm 레지스트리 프로토콜 |
| `GET /cargo/*` | Cargo sparse index 프로토콜 |
| `GET /gem/*` | RubyGems 프로토콜 |
| `GET /go/*` | Go 모듈 프록시 프로토콜 |
| `GET /hex/*` | Hex.pm 프로토콜 |
| `GET /pub/*` | pub.dev 프로토콜 |
| `GET /pypi/*` | PyPI simple/JSON API |
| `GET /maven/*` | Maven 저장소 프로토콜 |
| `GET /nuget/*` | NuGet V3 API |
| `GET /composer/*` | Composer/Packagist 프로토콜 |
| `GET /conan/*` | Conan C/C++ 프로토콜 |
| `GET /conda/*` | Conda/Anaconda 프로토콜 |
| `GET /cran/*` | CRAN (R) 프로토콜 |
| `GET /julia/*` | Julia Pkg 서버 프로토콜 |
| `GET /helm/{repository}/*` | HTTP Helm 차트 저장소 프로토콜 |
| `GET /v2/*` | OCI/Docker 레지스트리 프로토콜 |
| `GET /debian/*` | Debian/APT 저장소 프로토콜 |
| `GET /rpm/*` | RPM/Yum 저장소 프로토콜 |

### 미러 API

| 엔드포인트 | 설명 |
|----------|-------------|
| `POST /api/mirror` | 미러 작업 시작 (`purls`가 포함된 JSON 본문) |
| `GET /api/mirror/{id}` | 작업 상태 및 진행률 조회 |
| `DELETE /api/mirror/{id}` | 실행 중인 작업 취소 |

### 강화(Enrichment) API

프록시는 패키지 메타데이터 강화, 취약점 스캔, 오래된 버전 감지를 위한 REST 엔드포인트를 제공합니다.

| 엔드포인트 | 설명 |
|----------|-------------|
| `GET /api/package/{ecosystem}/{name}` | 패키지 메타데이터 조회 |
| `GET /api/package/{ecosystem}/{name}/{version}` | 취약점이 포함된 버전 메타데이터 조회 |
| `GET /api/vulns/{ecosystem}/{name}` | 패키지의 모든 취약점 조회 |
| `GET /api/vulns/{ecosystem}/{name}/{version}` | 특정 버전의 취약점 조회 |
| `POST /api/outdated` | 여러 패키지의 오래된 버전 확인 |
| `POST /api/bulk` | 대량 패키지 메타데이터 조회 |

#### 패키지 메타데이터 조회```bash
curl http://localhost:8080/api/package/npm/lodash

/api/scan 엔드포인트는 스캔 요청을 수락하고, /api/scan/<scan_id>는 스캔 상태를 반환하며, /api/scan/<scan_id>/result는 완료된 스캔 결과를 반환합니다. 모든 API 응답은 JSON 형식이며, 인증이 필요한 엔드포인트는 Authorization: Bearer <token> 헤더를 요구합니다.```json { "ecosystem": "npm", "name": "lodash", "latest_version": "4.17.21", "license": "MIT", "license_category": "permissive", "description": "Lodash modular utilities", "homepage": "https://lodash.com/", "repository": "https://github.com/lodash/lodash", "registry_url": "https://registry.npmjs.org" }

#### 취약점이 있는 버전 확인```bash
curl http://localhost:8080/api/package/npm/lodash/4.17.0

-f オプションは、-c オプションと組み合わせて使用すると、-c で指定されたコマンドを実行する前に、指定されたファイルを読み取って設定を適用します。これは、設定ファイルを読み込んでからコマンドを実行したい場合に便利です。```json { "package": { "ecosystem": "npm", "name": "lodash", "latest_version": "4.17.21", "license": "MIT", "license_category": "permissive" }, "version": { "ecosystem": "npm", "name": "lodash", "version": "4.17.0", "license": "MIT", "published_at": "2016-06-17T03:59:56Z", "yanked": false, "is_outdated": true }, "vulnerabilities": [ { "id": "GHSA-p6mc-m468-83gw", "summary": "Prototype Pollution in lodash", "severity": "HIGH", "cvss_score": 7.4, "fixed_version": "4.17.12" } ], "is_outdated": true, "license_category": "permissive" }

#### 오래된 패키지 확인```bash
curl -X POST http://localhost:8080/api/outdated \
  -H "Content-Type: application/json" \
  -d '{
    "packages": [
      {"ecosystem": "npm", "name": "lodash", "version": "4.17.0"},
      {"ecosystem": "pypi", "name": "requests", "version": "2.25.0"}
    ]
  }'

/etc/nginx/nginx.conf 파일을 편집하고 다음 줄을 추가합니다:

stream {
    server {
        listen 443 ssl;
        proxy_pass 127.0.0.1:8443;
        ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    }
}

그런 다음 Nginx를 다시 로드합니다:

sudo systemctl reload nginx

이제 https://example.com으로 이동하면 트래픽이 로컬 포트 8443에서 실행 중인 서비스로 프록시됩니다.```json { "results": [ { "ecosystem": "npm", "name": "lodash", "version": "4.17.0", "latest_version": "4.17.21", "is_outdated": true }, { "ecosystem": "pypi", "name": "requests", "version": "2.25.0", "latest_version": "2.31.0", "is_outdated": true } ] }

#### 대량 패키지 조회```bash
curl -X POST http://localhost:8080/api/bulk \
  -H "Content-Type: application/json" \
  -d '{
    "purls": [
      "pkg:npm/[email protected]",
      "pkg:pypi/[email protected]"
    ]
  }'
## 2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2```json
{
  "packages": {
    "pkg:npm/lodash": {
      "ecosystem": "npm",
      "name": "lodash",
      "latest_version": "4.17.21",
      "license": "MIT",
      "license_category": "permissive"
    },
    "pkg:pypi/requests": {
      "ecosystem": "pypi",
      "name": "requests",
      "latest_version": "2.31.0",
      "license": "Apache-2.0",
      "license_category": "permissive"
    }
  }
}

Stats Response (HTTP endpoint)```json

{ "cached_artifacts": 142, "total_size_bytes": 523456789, "total_size": "499.2 MB", "storage_url": "file:///path/to/cache/artifacts", "database_path": "./cache/proxy.db" }

## 작동 방식

1. 패키지 관리자가 프록시에 패키지 메타데이터를 요청합니다
2. 프록시가 업스트림에서 메타데이터를 가져와 아티팩트 URL을 프록시를 가리키도록 다시 작성합니다
3. 패키지 관리자가 아티팩트(타르볼, 크레이트 등)를 요청합니다
4. 프록시가 로컬 캐시를 확인합니다:
   - **캐시 적중**: 로컬 저장소에서 제공
   - **캐시 미스**: 업스트림에서 가져와 로컬에 저장한 후 클라이언트에 제공
5. 동일한 아티팩트에 대한 후속 요청은 캐시에서 제공됩니다```
┌─────────────┐     ┌─────────┐     ┌──────────┐
│   npm/cargo │────▶│  proxy  │────▶│ upstream │
│   client    │◀────│         │◀────│ registry │
└─────────────┘     └─────────┘     └──────────┘
                         │
                         ▼
                    ┌─────────┐
                    │  cache  │
                    │ storage │
                    └─────────┘

웹 인터페이스

프록시는 /ui에서 웹 UI를 제공합니다. 별도의 프론트엔드 빌드는 필요하지 않습니다. 템플릿과 에셋은 바이너리에 내장되어 있습니다. GET //ui/로 리디렉션됩니다. UI는 자체 접두사 아래에 마운트되므로 리버스 프록시가 패키지 엔드포인트와 다른 접근 규칙을 UI에 적용할 수 있습니다(예: PathPrefix(/ui)에 인증을 요구하면서 /npm, /pypi 등은 빌드 머신에 개방).

  • 대시보드 (/ui/) -- 캐시 통계, 인기 패키지, 최근 캐시된 아티팩트, 취약점 개요.
  • 설치 가이드 (/ui/install) -- 에코시스템별 구성 지침. 여기서 따로 찾아볼 필요가 없습니다.
  • 패키지 브라우저 (/ui/packages) -- 에코시스템별 필터링과 조회수, 크기, 이름 또는 취약점 수 기준 정렬로 모든 캐시된 패키지를 탐색합니다.
  • 검색 (/ui/search?q=...) -- 이름으로 캐시된 패키지를 검색합니다.
  • 패키지 상세 (/ui/package/{ecosystem}/{name}) -- 패키지의 메타데이터, 라이선스, 취약점 및 버전 목록. 두 버전을 선택하여 비교할 수 있습니다.
  • 버전 상세 (/ui/package/{ecosystem}/{name}/{version}) -- 버전별 메타데이터, 무결성 해시, 아티팩트 캐시 상태 및 조회 수.
  • 소스 브라우저 (/ui/package/{ecosystem}/{name}/{version}/browse) -- 텍스트 파일의 구문 강조 및 이미지 미리보기와 함께 캐시된 아카이브 내부의 파일을 탐색합니다.
  • 버전 비교 (/ui/package/{ecosystem}/{name}/compare/{v1}...{v2}) -- 추가, 제거 및 변경된 파일을 보여주는 두 캐시된 버전의 나란히 보기 diff.

모니터링

프록시는 GET /metrics에서 Prometheus 메트릭을 노출합니다. 모든 메트릭 이름에는 proxy_ 접두사가 붙습니다.

메트릭유형레이블설명
proxy_requests_totalcounterecosystem, status패키지 에코시스템 및 HTTP 상태별 프록시 응답
proxy_request_duration_secondshistogramecosystem, status프록시 요청 지속 시간
proxy_cache_hits_totalcounterecosystem캐시 적중
proxy_cache_misses_totalcounterecosystem캐시 미스
proxy_cache_size_bytesgauge캐시된 아티팩트의 총 크기
proxy_cached_artifacts_totalgauge캐시된 아티팩트 수
proxy_upstream_fetch_duration_secondshistogramecosystem업스트림에서 가져오는 데 소요된 시간
proxy_upstream_errors_totalcounterecosystem, error_type업스트림 가져오기 실패
proxy_storage_operation_duration_secondshistogramoperation스토리지 읽기/쓰기 지연 시간
proxy_storage_errors_totalcounteroperation스토리지 읽기/쓰기 실패
proxy_active_requestsgauge처리 중인 요청
proxy_health_probe_failures_totalcounterstep실패한 단계별 스토리지 상태 프로브 실패(write, size, read, verify, delete).

캐시 크기와 아티팩트 수는 60초마다 새로 고쳐집니다. 나머지 메트릭은 각 요청 시 업데이트됩니다.

상태 확인

/health는 하위 시스템 상태에 대한 구조화된 JSON 보고서를 반환합니다. 모든 검사가 통과하면 HTTP 200, 하나라도 실패하면 503을 반환합니다.```json { "status": "ok", "checks": { "database": {"status": "ok"}, "storage": {"status": "ok"} } }

실패한 검사에는 `"error"` 필드가 포함됩니다. 스토리지 실패에는 어떤 프로브 단계가 실패했는지 식별하는 `"step"` 필드(`write`, `size`, `read`, `verify`, `delete`)도 포함됩니다. 데이터베이스 검사가 실패하면 스토리지 항목은 `{"status": "skipped"}`를 보고하므로 응답은 항상 동일한 키 집합을 전달합니다.

스토리지 프로브 결과는 원격 백엔드 프로빙 비용을 제한하기 위해 `health.storage_probe_interval`(기본값 30초) 동안 캐시됩니다. 프로브는 최대 10초(하드코딩된 프로브별 타임아웃) 동안 내부 뮤텍스를 보유하므로 `/health`는 liveness 프로브가 아닌 Kubernetes **readiness** 프로브로 사용됩니다. 느린 S3 왕복은 포드를 재시작하는 것이 아니라 로테이션에서 제외해야 합니다.

Prometheus용 스크레이프 구성:```yaml
scrape_configs:
  - job_name: git-pkgs-proxy
    static_configs:
      - targets: ["localhost:8080"]

프로덕션 배포

Systemd 서비스

/etc/systemd/system/proxy.service 파일을 생성합니다:```ini [Unit] Description=git-pkgs proxy After=network.target

[Service] Type=simple User=proxy ExecStart=/usr/local/bin/proxy -config /etc/proxy/config.yaml Restart=always RestartSec=5

[Install] WantedBy=multi-user.target

Enable and start:```bash
sudo systemctl enable proxy
sudo systemctl start proxy

Docker

Dockerfile이 저장소에 포함되어 있습니다. 빌드 및 실행:```bash docker build -t proxy . docker run -p 8080:8080 -v proxy-data:/data proxy

Postgres와 S3를 사용하는 경우:```bash
docker run -p 8080:8080 \
  -e PROXY_DATABASE_DRIVER=postgres \
  -e PROXY_DATABASE_URL=postgres://user:pass@db:5432/proxy \
  -e PROXY_STORAGE_URL=s3://my-bucket?region=us-east-1 \
  -e AWS_ACCESS_KEY_ID=... \
  -e AWS_SECRET_ACCESS_KEY=... \
  proxy

리버스 프록시 뒤에서

nginx, Apache 또는 다른 리버스 프록시 뒤에서 실행할 때는 base_url을 공개 URL로 설정하세요:```yaml base_url: "https://proxy.example.com"

UI가 패키지 엔드포인트와 다른 호스트명으로 접근되는 경우(예: UI가 공개 도메인에 노출되고 빌드 머신이 Docker 네트워크 별칭을 사용하는 경우) `ui_base_url`을 별도로 설정하세요. `base_url`은 패키지 관리자와 메타데이터 재작성이 사용하는 URL이고, `ui_base_url`은 웹 UI를 방문하는 사용자에게 표시되는 URL(정식/`og:url` 태그 및 설치 가이드 배너)입니다:```yaml
base_url: "http://pkg-proxy:8080"        # internal alias for build machines
ui_base_url: "https://proxy.example.com/ui"  # public UI URL

ui_base_url가 설정되지 않으면 기본값은 base_url입니다.

경고: 프록시는 UI와 패키지 엔드포인트를 동일한 리스너에서 제공합니다. ui_base_url을 설정하는 것은 UI가 사람들에게 알리는 URL만 변경할 뿐, 동일한 호스트명과 포트에서 패키지 엔드포인트에 접근할 수 없게 만드는 것은 아닙니다. 공용 리버스 프록시로 프록시를 앞에 둘 때는 공용 경로를 PathPrefix(/ui)(또는 프록시에 해당하는 항목)로 제한하세요. 그렇지 않으면 /npm, /pypi 및 기타 패키지 엔드포인트가 UI와 함께 노출된 상태로 유지됩니다.

공용 호스트를 UI로 제한하면서 패키지 엔드포인트는 내부 리스너에서만 접근 가능하도록 하는 nginx 예시:```nginx server { listen 443 ssl; server_name proxy.example.com;

location /ui/ {
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_buffering off;
}

location / {
    return 404;
}

}

Traefik 예시에서 `PathPrefix(/ui)`를 사용하여 공용 라우터가 UI 트래픽만 일치하도록 합니다:```yaml
labels:
  traefik.enable: "true"
  traefik.http.services.pkg-proxy.loadbalancer.server.port: "8080"
  traefik.http.routers.pkg-proxy.rule: "Host(`proxy.example.com`) && PathPrefix(`/ui`)"
  traefik.http.routers.pkg-proxy.entrypoints: "websecure"

캐시 관리

프록시는 구성된 저장 디렉터리에 아티팩트를 다음 구조로 저장합니다:``` cache/artifacts/ ├── npm/ │ └── lodash/ │ └── 4.17.21/ │ └── lodash-4.17.21.tgz ├── cargo/ │ └── serde/ │ └── 1.0.193/ │ └── serde-1.0.193.crate ├── oci/ │ └── library/nginx/ │ └── sha256:abc123.../ │ └── sha256:abc123... ├── deb/ │ └── nginx/ │ └── 1.18.0-6/ │ └── nginx_1.18.0-6_amd64.deb └── rpm/ └── nginx/ └── 1.24.0-1.fc39/ └── nginx-1.24.0-1.fc39.x86_64.rpm

캐시 메타데이터는 SQLite(기본값) 또는 PostgreSQL에 저장됩니다. 로컬 캐시를 지우려면:```bash
rm -rf ./cache/artifacts/*
rm ./cache/proxy.db

프록시는 다음 시작 시 데이터베이스를 다시 생성합니다.

소스에서 빌드하기

요구 사항:

테스트 실행:```bash
go test ./...

License

GPL-3.0-or-later

카테고리