Runner Plugin OCI Distribution

이 문서는 Runner-managed on-demand plugin을 OCI 기반 plugin bundle로 배포하는 권장 모델을 정의합니다.

현재 Runner 구현은 다음 첫 번째 end-to-end OCI install 경로까지 지원합니다.

  • runner-plugin-runtime.example.yaml 형태의 설정 파싱
  • resolution.capability_map 기반 capability index 생성
  • startup 시 preinstall 대상 설치 시도
  • 첫 resolve 시 on-demand install 시도
  • docker pull -> image inspect -> create -> cp -> rm 순서로 plugin bundle unpack
  • runner-plugin.yaml validation
  • 유효한 stdio-json plugin의 on-demand 실행
  • 설치 또는 실행 실패 시 기존 PLUGIN_*_URL HTTP plugin으로 fallback

다만 OCI signature verification은 아직 미구현이고, 기존 HTTP plugin URL은 호환성 경로로 계속 유지됩니다.

왜 OCI를 써야 하나

Runner 호스트에 임의의 폴더를 복사하는 방식보다 OCI 배포가 더 나은 이유는 다음과 같습니다.

  • 버전 관리
  • digest pinning
  • signature verification
  • registry 기반 접근 제어
  • 반복 가능한 설치와 롤백

즉 고객 관리형 Runner 환경의 장기 모델로 더 적합합니다.

권장 모델

권장 배포 흐름은 다음과 같습니다.

  1. plugin author가 plugin bundle 생성
  2. bundle 안에 runner-plugin.yaml 과 runtime 파일 포함
  3. bundle을 OCI artifact 또는 image로 publish
  4. publisher가 artifact에 서명
  5. Runner가 digest 기준으로 pull
  6. Runner가 관리되는 install 디렉터리로 unpack
  7. Runner가 manifest를 읽음
  8. Runner가 capability index 생성
  9. Core는 capability를 참조하고, Runner가 이를 설치된 bundle로 resolve

패키징 형태

현재 Runner install 로직은 image의 Config.WorkingDir 를 관리되는 install 디렉터리로 unpack 합니다. 따라서 호환되는 image는 다음을 만족해야 합니다.

  • 비어 있지 않은 OCI Config.WorkingDir
  • 그 working directory 안의 runner-plugin.yaml
  • runner-plugin.yaml 이 가리키는 실제 entrypoint 파일

OCI로 전달된 plugin은 unpack 후 다음과 같은 디렉터리 구조를 갖는 것이 좋습니다.

<install-root>/<plugin-name>/<digest>/
  runner-plugin.yaml
  package.json
  src/
  shared/

예:

/var/lib/aisopsflow-runner/plugins/gmail/sha256-aaaaaaaa/
  runner-plugin.yaml
  package.json
  src/runner-entrypoint.ts

Runner 디렉터리 레이아웃

권장 로컬 경로:

/var/lib/aisopsflow-runner/
  state/
  plugin-cache/
  plugins/
    gmail/
    microsoft-email/
    microsoft-office/
  manifests/

역할:

  • plugin-cache/
    • 다운로드된 OCI layer 또는 unpack staging 데이터
  • plugins/
    • 실제로 실행 가능한 설치 bundle
  • manifests/
    • 선택적으로 로컬 resolution snapshot 또는 catalog cache

권장 Runner 설정 파일

참조 예시:

이 파일은 이제 현재 Runner 구현에서 capability index, install root, preinstall, on-demand resolution에 실제로 사용됩니다.

Capability resolution 모델

Core는 절대 경로가 아니라 plugin capability를 참조하는 것이 좋습니다.

예시 planned job 조각:

{
  "kind": "plugin_call",
  "plugin_capability": "gmail.read",
  "input": {
    "query": "from:alerts@example.com newer_than:1d"
  }
}

Runner는 이를 다음 순서로 resolve 합니다.

  1. capability map
  2. local install state
  3. manifest validation
  4. local policy

resolve 결과:

  • capability -> OCI ref
  • OCI ref -> installed digest directory
  • digest directory -> runner-plugin.yaml
  • manifest -> entrypoint 와 env allowlist

배포 흐름

Publisher 측

  1. plugin bundle 빌드
  2. runner-plugin.yaml 포함
  3. registry에 publish
  4. artifact 서명
  5. digest reference를 고객 또는 plugin catalog에 제공

Customer / operator 측

  1. Runner config에 trusted registry 허용
  2. pull credential 설정
  3. 정확한 plugin digest pin
  4. plugin ref를 install 또는 preinstall 목록에 추가
  5. Runner 재시작 또는 catalog sync 트리거

Runner 측

현재 구현:

  1. 로컬 docker CLI로 image pull
  2. Config.WorkingDir inspect
  3. 임시 container 생성
  4. working directory를 install root로 복사
  5. 임시 container 제거
  6. runner-plugin.yaml validation
  7. stdio-json 로 plugin on-demand spawn
  8. 실행 실패 시 legacy HTTP plugin URL로 fallback

목표 상태에서 추가될 항목:

  1. signature 검증
  2. manifest 선언 기반 네트워크 정책 강제
  3. 지원 capability에 대한 legacy HTTP fallback 제거

권장 설정 섹션

목표 상태 Runner config에는 다음 섹션이 들어가는 것이 좋습니다.

  • plugin_runtime
  • registry
  • catalog
  • plugins.preinstall
  • resolution
  • secrets
  • policies
  • observability

보안 규칙

현재 코드가 실제로 강제하는 최소 규칙:

  • configured capability map에 있는 capability만 resolve
  • registry.require_digest_pinning 이 켜져 있으면 digest pinning 강제
  • runner-plugin.yaml 이 없거나 invalid면 managed execution 거부
  • allowlist된 env var만 managed plugin에 주입

추가로 권장하는 규칙:

  • allowlist된 registry에서만 pull
  • install 전 signature 검증
  • capability mapping이 없으면 실행 거부

강력 권장:

  • registry credential과 provider credential 분리
  • 설치된 digest와 manifest hash를 Runner telemetry에 기록
  • plugin write는 기본 비활성화
  • write/delete capability는 승인 필요

왜 로컬 폴더 복사보다 낫나

로컬 폴더 복사는 개발용으로는 쓸 수 있지만, 운영에서는 OCI install이 더 낫습니다. 이유는:

  • provenance
  • repeatability
  • 통제된 업데이트
  • 더 쉬운 운영 지원
  • 더 명확한 compatibility 추적

현재 호환성 동작

현재 managed execution은 다음 조건이 모두 맞을 때만 적용됩니다.

  • capability가 resolution.capability_map 에 있어야 함
  • OCI image를 pull할 수 있거나 로컬에 이미 있어야 함
  • image가 유효한 Config.WorkingDir 를 가져야 함
  • unpack된 bundle에 유효한 runner-plugin.yaml 이 있어야 함
  • manifest가 가리키는 entrypoint가 실제로 시작되어야 함

이 중 하나라도 실패하면 현재 코드는 여전히 다음 env var를 사용합니다.

  • PLUGIN_SLACK_URL
  • PLUGIN_EMAIL_URL
  • PLUGIN_TELEGRAM_URL
  • PLUGIN_KAKAO_URL
  • PLUGIN_TEAMS_URL

참조: Config.hs

또한 notify 응답에는 다음 managed-runtime 메타데이터가 함께 담깁니다.

  • transport
  • plugin_ref
  • install_path
  • manifest_path
  • runtime_error

즉 운영자는 Runner가 managed plugin을 실제 실행했는지, 아니면 legacy HTTP 경로로 fallback 했는지 응답만 보고도 확인할 수 있습니다.