- 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>
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
httpxto 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
httpxclient must accept averify=PULP_CA_FILEoption 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:
- 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. - Deploy (create Publication →
PATCHthe Distribution'spublication): 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-confirmor 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_validationand the Repositoryrepo_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)
- Skeleton: FastAPI + Jinja2 + httpx,
pulp_client.pystub,/healthzhitting Pulp status. - Dashboard (read-only repo list/cards).
- Sync:
POSTsync → task_href → polling progress fragment. - Version list + verification badges.
- Deploy: confirm modal → create publication → PATCH distribution → poll → audit log.
- 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.