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>
This commit is contained in:
115
CLAUDE.md
Normal file
115
CLAUDE.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user