Files
pulp-console/CLAUDE.md
Hyemin Lee 53f7d1d8bb docs: add project spec, build plan, and contributor/design guidance
- pulp-console-spec.md: 실행 스펙 (확정 스택, Pulp API, 안전 모델)
- PLAN.md: 단계별 구현 계획 (체크박스 진행 추적)
- CLAUDE.md: future Claude 세션용 아키텍처/제약 안내
- ref/design-ref.md: Toss 스타일 디자인 레퍼런스

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 10:54:39 +09:00

5.4 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

What this is

A thin, self-hosted web admin console for Pulp 3, a Linux patch-repository server. Pulp has a full REST API (/pulp/api/v3/) but no usable official web UI, so this project builds a focused operator console on top of that API.

Context: it runs in an air-gapped (closed) network at the Bank of Korea IT Strategy Dept. cloud team. The console lets operators mirror, snapshot, verify, and deploy RPM/DEB patch repositories by clicking instead of running CLI commands.

The full implementation spec is pulp-console-spec.md (written in Korean, addressed to the implementing AI). It is the source of truth — read it before making design decisions. The codebase itself does not yet exist; the spec describes what to build and in what order.

Stack (decided — do not substitute)

  • Backend / BFF: FastAPI (Python), using httpx to call Pulp.
  • Frontend: HTMX + Jinja2 templates. No build step. Buttons trigger partial-HTML swaps; progress polling is done with hx-trigger="every 3s".
  • Styling: Pico.css (preferred for air-gap simplicity) or Tailwind standalone.

Hard constraints (these are the point of the project)

  • The browser never calls the Pulp API directly. All traffic goes through the FastAPI BFF, which holds Pulp Basic Auth credentials and proxies requests. This protects credentials and avoids CORS exposure.
  • No heavy frontend tooling (React, Vite, npm build chains). HTML + HTMX only.
  • Air-gap first: minimize CDN dependence. Bundle CSS / HTMX JS under static/ so the app runs with no internet access.
  • View routes return HTML (full pages or fragments), not JSON — HTMX consumes HTML.
  • Self-signed CA support from day one: the httpx client must accept a verify=PULP_CA_FILE option for the internal TLS CA (boknet CA family).

Architecture

Browser (HTMX + Jinja)  ──form/button──►  FastAPI BFF  ──Basic Auth──►  Pulp REST API (/pulp/api/v3/)
        ◄──── partial HTML ────                ◄──────── JSON ────────

The BFF receives Pulp JSON, renders it into Jinja partial templates, and returns HTML fragments that HTMX swaps into the page.

Pulp object model (essential to understand the flow)

Remote (source def) → Repository (container) → RepositoryVersion (snapshot) → Publication (deployable form) → Distribution (public URL)

The URL operators point yum/dnf baseurl at is the Distribution.

Two operations matter:

  1. Sync (POST .../repositories/rpm/rpm/{uuid}/sync/): pulls from an external mirror and creates a new RepositoryVersion snapshot. Relatively safe — does not change what production servers receive. Returns an async task.
  2. Deploy (create Publication → PATCH the Distribution's publication): dangerous — changes the version production actually fetches.

Pulp objects reference each other by pulp_href (a path string); use href, not name, in code. Sync and deploy return async tasks — capture task_href and poll GET {task_href} (state: waiting/running/completed/failed) for completion. RPM paths use rpm/rpm; the DEB equivalent swaps that segment for deb/apt.

Safety model (deploy is gated)

  • Deploy must go through a confirmation modal (hx-confirm or an explicit step).
  • The deploy button is disabled for versions that aren't verified (GPG / checksum passed). Verification comes from the Remote's gpgkey/tls_validation and the Repository repo_config (gpgcheck: 1, repo-gpgcheck: 1) — this is the basis for the "verified " badge.
  • Every deploy is written to an audit log (who / when / which repo / which version).

Planned route surface (BFF)

GET  /                       dashboard (full page)
GET  /repos                  repo list fragment
POST /repos/{uuid}/sync      start sync, return progress-bar fragment
GET  /repos/{uuid}/progress  progress-bar fragment (polled every 3s)
GET  /repos/{uuid}/versions  version list fragment/page
POST /repos/{uuid}/deploy    confirm deploy (publication + distribution), result fragment
GET  /healthz                proxy Pulp status

Pulp access is centralized in a pulp_client.py helper (get/post/patch with Basic Auth, list_repos, sync_repo, list_versions, create_publication, update_distribution, single task fetch — the page does the polling, not the client).

Configuration (env vars)

PULP_BASE_URL, PULP_USERNAME, PULP_PASSWORD (secret), PULP_VERIFY_TLS, PULP_CA_FILE (path to internal CA pem).

Build order (from the spec — build in working-screen increments)

  1. Skeleton: FastAPI + Jinja2 + httpx, pulp_client.py stub, /healthz hitting Pulp status.
  2. Dashboard (read-only repo list/cards).
  3. Sync: POST sync → task_href → polling progress fragment.
  4. Version list + verification badges.
  5. Deploy: confirm modal → create publication → PATCH distribution → poll → audit log.
  6. Styling, bundle static assets for air-gap, containerize.

Each step should produce a working screen. Do not build everything at once.

Conventions

  • Python 3.14 is installed locally. Once code exists, prefer a virtualenv and pin dependencies (requirements.txt / lockfile) so they can be carried into the air-gapped network via pip.