Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,20 @@ FRONTEND_PORT=5173

# Production edge port used by compose.prod.yaml (Traefik).
TRAEFIK_HTTP_PORT=8080
# Required by compose.prod.yaml: the minimal direct-peer /32 or /128 CIDR(s)
# Traefik observes for the host-local TLS terminator. With Docker NAT this is
# commonly the bridge gateway, not the terminator process's original address.
# Inspect the deployment-specific peer before starting production-style Compose.
TRAEFIK_TRUSTED_PROXY_CIDRS=

# Backend app secret (used for encrypting stored connection DSNs)
APP_SECRET=change-me-long-random

# Required for target introspection. Empty fails closed and rejects all targets.
# Use comma-separated exact hostnames/IPs or narrow wildcard domains such as
# *.db.example.com; never use a broad production wildcard.
DB_INTROSPECTION_ALLOWED_HOSTS=

# Frontend -> backend
VITE_API_BASE_URL=http://localhost:8000

Expand All @@ -23,10 +33,15 @@ CORS_ORIGINS=http://localhost:5173
API_RATE_LIMIT_ENABLED=true
API_RATE_LIMIT_REQUESTS=120
API_RATE_LIMIT_WINDOW_SECONDS=60
# Set true only behind trusted proxy/ingress that sanitizes X-Forwarded-For.
# Set true only behind a trusted proxy/ingress that sanitizes X-Forwarded-For.
# HOPS counts from the right side of that header. The local/development default
# is one; compose.prod.yaml overrides it to two for client -> TLS terminator ->
# Traefik -> backend.
API_RATE_LIMIT_TRUST_X_FORWARDED_FOR=false
API_RATE_LIMIT_TRUSTED_PROXY_HOPS=1
API_RATE_LIMIT_MAX_KEYS=10000
# Tighter unauthenticated throttle for public share-link reads/exports.
SHARE_LINK_TTL_HOURS=168
SHARE_LINK_RATE_LIMIT_ENABLED=true
SHARE_LINK_RATE_LIMIT_REQUESTS=30
SHARE_LINK_RATE_LIMIT_WINDOW_SECONDS=60
Expand Down
17 changes: 15 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ agent (Claude, Codex, Cursor, opencode, …). This is a Python backend
(`frontend/`), shipped via Docker Compose (`compose.yaml`, `compose.prod.yaml`)
behind Traefik (`deploy/traefik/`).

## Documentation authority

Start at [`docs/README.md`](docs/README.md) for the canonical PRD, TRD,
Architecture, ADR, UML, ERD, API, security, test, operations, release, and
traceability graph. Do not present `planned`, `research_only`, `downstream`, or
`active_pr` behavior as shipped on `main`.

Update the governing document and traceability when a change affects product
acceptance (PRD), technical contracts or quality attributes (TRD/API), system
boundaries (Architecture), durable decisions (ADR), behavior/data shape
(UML/ERD), threat controls, verification strategy, operations, or release and
recovery evidence. A documentation-only follow-up is not a substitute for
keeping the authority graph consistent in the same change.

<!-- BEGIN cwl-agent-guidance -->
## Agent guidance (CWL governance)

Expand All @@ -22,8 +36,7 @@ behind Traefik (`deploy/traefik/`).
gate:
- Vulnerable dependency: bump it. Python lives in `backend/requirements.lock`
/ `backend/requirements-dev.lock` (hash-locked) and `backend/pyproject.toml`;
frontend in `frontend/package.json` + `frontend/package-lock.json` /
`frontend/pnpm-lock.yaml`.
frontend in `frontend/package.json` + `frontend/package-lock.json`.
- Container/config misconfig: fix the offending `backend/Dockerfile`,
`frontend/Dockerfile`, `frontend/Dockerfile.prod`, the `compose*.yaml`
files, or `deploy/traefik/dynamic.yaml`.
Expand Down
164 changes: 164 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# pg-erd-cloud Architecture

Status date: 2026-08-09
Authority: protected `main` plus explicitly labelled `active_pr` evidence
Default lifecycle labels: `implemented_on_main`, `active_pr`, `planned`,
`research_only`, `downstream`, `deprecated`, and `out_of_scope`

This document is the architectural entry point. It deliberately separates
running behavior from the production Forward Engineering target. A Figma
screen, pull-request body, or planned data model is not evidence that a
capability is shipped.

## System context

```mermaid
flowchart LR
actor["Engineer or data architect"]
idp["OIDC identity provider"]
app["pg-erd-cloud"]
appdb[("Application PostgreSQL")]
target[("Target database")]
llm["OpenAI-compatible provider"]

actor -->|"HTTPS"| app
app -->|"OIDC verification"| idp
app -->|"metadata, jobs, encrypted DSNs"| appdb
app -->|"guarded introspection or legacy client-authored DDL"| target
app -.->|"authenticated reversing/index-design drafts only"| llm
```

The product is independently deployable. Other ContextualWisdomLab services
are optional integrations through versioned HTTP or artifact contracts; they
never share pg-erd-cloud's private tables or credentials.

## Runtime components and deployment units

| Component | Responsibility | Lifecycle | Evidence |
| --- | --- | --- | --- |
| React/Vite SPA | Workspace, ERD canvas, local model editing and exports are the base SPA; the read-only public viewer and Figma-aligned shell are the current change | Base SPA `implemented_on_main`; public viewer and Figma alignment `active_pr` | `frontend/src`, PR #824 |
| FastAPI API | Authentication, authorization, project resources, snapshots, exports and share APIs | `implemented_on_main`; share hardening is `active_pr` | `backend/app/api`, PR #824 |
| FastAPI lifespan worker (in-process) | Each API process starts a worker that claims persistent queued rows with `FOR UPDATE SKIP LOCKED`; no lease/reclaim exists for stranded running work | `implemented_on_main` with recovery gap | `backend/app/main.py`, `backend/app/jobs/worker.py` |
| Application PostgreSQL | Tenant metadata, encrypted target connections, snapshots and job source of truth | `implemented_on_main` | `backend/app/models.py`, Alembic migrations |
| Target connectors | PostgreSQL current path and optional Snowflake extra; MySQL/MariaDB adapter code lacks packaged driver/UI/complete dialect contract | PostgreSQL `implemented_on_main`; MySQL `research_only` | `backend/app/db_introspect.py` |
| Traefik edge | Production-style HTTP routing and edge security headers; TLS termination is external | `implemented_on_main` with deployment requirement | `compose.prod.yaml`, `deploy/traefik` |
| Governed Forward Engineering planner/executor | Versioned model-to-plan compilation, isolated dry-run, approval, apply and convergence proof | `planned` | [ADR-0004](docs/adr/0004-server-authoritative-forward-engineering.md), [TRD](docs/TRD.md) |

## Runtime component model

```mermaid
flowchart TB
subgraph edge["Public edge"]
traefik["Traefik"]
end
subgraph product["pg-erd-cloud trust domain"]
spa["React SPA"]
subgraph api_process["FastAPI process"]
api["HTTP API"]
worker["Lifespan job worker"]
end
metadata[("Application PostgreSQL")]
wake["Optional Valkey wake signal"]
end
target[("Customer target database")]

traefik --> spa
traefik --> api
spa -->|"JSON/exports"| api
api --> metadata
worker --> metadata
api -.-> wake
wake -.-> worker
worker -->|"SSRF-guarded introspection"| target
api -->|"legacy synchronous apply"| target
```

Valkey is only a wake-up optimization. PostgreSQL remains authoritative for
job state. The current synchronous `/api/connections/{id}/apply-sql` path is
not the target commercial architecture: it accepts client-authored text after
a conservative validator and performs transactional execution against the
target. It does not bind a plan to a stored model revision, durable job,
approval, target fingerprint, statement digest, or convergence result.

## Primary data flows

### Reverse engineering (`implemented_on_main`)

1. An editor stores a target DSN; application code encrypts it before writing
`db_connection`.
2. Snapshot creation writes `schema_snapshot` and a queued `job_queue` row.
3. A worker claims the job, decrypts the DSN in memory, applies the connector's
network/SSRF guard, introspects catalog metadata, and writes
`schema_snapshot_data`.
4. The SPA renders the stored snapshot and may add transient local graph edits.
Backend DDL/diff/migration/spec artifacts continue to derive from stored
snapshots; frontend DBML/Mermaid/Prisma/dictionary outputs may derive from
the current local graph, and `/api/dbml/convert` accepts separate
design-first input. None is a durable desired-model revision today.

### Public sharing (`active_pr` in PR #824)

1. A project owner creates a bearer share link with a configurable expiry and
may revoke it early through the authenticated project API.
2. `/share/{id}` loads the public API and renders a read-only ERD.
3. Public APIs return only successful snapshots and redact comments,
`example_value`, and failure diagnostics.
4. Public callers cannot trigger the paid live-LLM draft path.

### Forward engineering (`planned` target)

The server will persist an editable model revision, compile it and the observed
target fingerprint into an immutable structured plan, dry-run that plan in an
isolated compatible PostgreSQL environment, obtain explicit authorization,
recheck target drift and privileges, execute through a durable serialized job,
and re-introspect to prove semantic convergence. See the [TRD](docs/TRD.md),
[UML](docs/UML.md), and [ERD](docs/ERD.md).

## Trust boundaries and invariants

- Project membership and role checks precede access to connections, snapshots,
views, annotations, and share creation.
- DSNs and encryption keys never enter logs or public responses. Runtime
migration from environment-backed settings to a credential registry remains
`planned` and is tracked in `AGENTS.md`.
- The browser is presentation and editing state, not DDL execution authority.
- Public share links are bearer capabilities and expose a deliberately smaller
DTO/export surface than authenticated project APIs.
- Public bearer existence and expiry checks use the primary session; optional
read-replica routing cannot extend a revoked link during replication lag.
- Schema metadata can contain PII or commercially sensitive names. Current
controls provide authorization, encrypted DSN storage, and selective public
disclosure. Retention schedules and tamper-evident auditability remain
planned rather than current product guarantees.
- PostgreSQL DDL can lock, scan, rewrite, or partially commit. A transaction
followed by rollback is not evidence of harmless production safety.
- New database objects use descriptive two-or-more-word `snake_case` names.
Legacy single-word columns are inventoried in [ERD](docs/ERD.md) and require
a compatibility migration rather than an undocumented destructive rename.

## Design authority

The authoritative design file is `csnpEEJfmqFWB0vNUoTkWA`. Its live page
inventory and precedence rules are recorded in
[`docs/ui-ux/figma-contract.md`](docs/ui-ux/figma-contract.md). Concrete screen
nodes override free-standing tokens unless Developer Handoff explicitly maps
the property. Accessibility corrections override visually supplied values that
fail WCAG 2.2.

## Architecture decisions and evidence

- [ADR index](docs/adr/README.md)
- [Product requirements](docs/PRD.md)
- [Technical requirements](docs/TRD.md)
- [API contract and compatibility](docs/API.md)
- [Forward Engineering capability matrix](docs/forward-engineering-support-matrix.md)
- [UML and behavior diagrams](docs/UML.md)
- [Current and target logical ERDs](docs/ERD.md)
- [Threat model](docs/threat-model.md)
- [Test strategy](docs/test-strategy.md)
- [Operations runbook](docs/operations-runbook.md)
- [Release and recovery plan](docs/release-plan.md)
- [Traceability matrix](docs/traceability-matrix.md)
- [Documentation coverage](docs/documentation-coverage-matrix.md)
- [Commercial work-loop automation contract](docs/automation-contract.md)
- [APA 7 references](docs/references.md)
29 changes: 25 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,36 @@
# Changelog

## Unreleased
- [Docs/Figma] 🔎 **현재 Figma 권위 재검증**: 2026-08-21 Plugin API가 확인한 파일 ID·`Cover` 페이지·현재 프레임을 ADR과 계약에 반영하고, 이전 18페이지/화면 노드 목록은 역사적 감사 증거로 명확히 구분했습니다.
- [BE] 🧹 **검증 실행 정합화**: backend 테스트 import 경로를 프로젝트 설정에 선언하고 사용하지 않는 관계 추론 지역 변수를 제거했습니다.
- [FE] 🧪 **UI 테스트 실행 안정화**: 자원 경합 환경에서 frontend 상호작용 회귀가 조기 timeout으로 오판되지 않도록 기본 테스트 제한을 15초로 조정했습니다.
- [FE] 🧪 **Frontend 테스트 결정성 보강**: 무거운 React/DOM 테스트 파일을 직렬 실행해 공유 jsdom 자원 경합으로 인한 간헐적 timeout·상태 미반영 실패를 제거했습니다.

- [Docs] ADR/PRD/TRD/Architecture/UML/ERD, API·Forward Engineering matrix, threat model, test/operations/release strategy, traceability, references, lifecycle vocabulary, and machine-checked documentation links were added as the canonical authority graph.
- [Security] Strix가 확인한 `nanoid<3.3.17` 개발 의존성 경로를 `3.3.17`로 고정하고 전체 lockfile audit을 0건으로 복구했습니다.
- [Security] host-local TLS terminator가 Docker NAT를 거친 뒤 Traefik에 보이는 최소 direct-peer CIDR만 신뢰하고, 백엔드 rate limit·관측 로그가 `client, trusted-peer` 체인의 실제 client hop을 공통 해석하도록 고정했습니다.
- [Security] bearer share SPA 문서는 정적 서버에서도 `Cache-Control: no-store`로 전달해 브라우저·중간 캐시 저장을 금지했습니다.
- [BE] CORS method allowlist를 실제 API의 `PUT`/`DELETE`까지 맞추고 preflight 회귀 테스트를 추가했습니다.
- [CI] 스키마 드리프트 스크립트를 존재하지 않는 세션 쿠키 인증에서 OIDC/API-key Bearer 인증으로 교정하고, 스냅샷 생성은 별도 pipeline 단계임을 명시했습니다.
- [FE] 🎨 **Live Figma alignment**: authoritative screen nodes and Developer Handoff variables now drive the workspace/editor shell, right-side properties inspector, compact toolbar, responsive 767px behavior, modal sizes, Korean copy, and shared light/dark token layer.
- [FE] ♿ **Modal and destructive-action hardening**: normalized dialogs on a shared shell with labelled modal semantics, initial focus, focus containment, Escape/focus return (including nested SVG interaction targets), explicit backdrop behavior, scroll-safe bodies and footers, keyboard-operable group color radios, and single confirmation at the application mutation boundary.
- [FE/BE] 🔗 **Read-only public share route**: share links now resolve to `/share/{id}` and render only successful, path-scoped allowlisted ERD snapshots while keeping `/api/share/{id}` as the backend data endpoint; newly created links receive a configurable expiry, owners can revoke them through the authenticated API, public capability checks remain primary-consistent, and the UI discloses that it has no revoke button yet.
- [FE] 🌗 **Canvas and typography alignment**: editor and shared canvases follow the system color mode, form controls plus React Flow handles, controls, and relationship lines use accessible semantic boundary tokens, the public viewer cannot re-enable editing, status/navigation text meets contrast requirements, and bundled Inter weights plus a Noto Sans KR fallback keep the Korean UI readable on hosts without CJK system fonts.
- [FE] 🔄 **Project-scoped async isolation**: project transitions clear stale data, expose distinct connection/snapshot loading states, preserve each project's in-flight create operation, serialize snapshot polling, and ignore late metadata, clipboard, and mutation completions outside their owning project.
- [Docs] Replaced deleted Figma node `29:143` as current evidence with the live node inventory and an explicit source-precedence/QA record.

- [BE] 🔒 **Cryptography 50+ 보안 경계 갱신**: `pyproject.toml`과 두 hash-locked 요구사항 파일을 동일한 Cryptography 50+ 해석으로 정합화하여 PKCS#7 오류·타이밍 구분으로 인한 CVE-2026-69247 완화를 실제 설치·검증 경로에 반영했습니다.
- [FE] ⚡ **검색 노드 참조 안정화 및 순차 스냅샷 폴링**: 같은 정규화 검색어와 원본 테이블 데이터에는 장식된 `node.data` 참조를 재사용하여 드래그 중 불필요한 하위 렌더링과 할당을 줄입니다. 스냅샷 폴링은 이전 요청이 끝난 뒤에만 다음 요청을 예약하며, 선택 변경·언마운트 후 도착한 오래된 성공 또는 실패 응답을 무시합니다.

- [BE] 🔒 **공유 export 전 경로 redaction**: 공개 share의 SQL / index-design / reversing-spec export에서 코멘트·`example_value`를 제거합니다. 단위 테스트로 누출을 차단합니다.
- [BE] 🔒 **공개 스냅샷 경계 강화**: 공유 목록·상세·모든 export를 성공 상태로 제한하고 공개 DTO에서 원본 스냅샷 오류 진단을 제거합니다.
- [BE] 🔒 **공개 LLM 비용 경계**: bearer 공유 경로에서는 결정적 Markdown과 LLM prompt만 허용하고, 외부 provider를 호출하는 live draft는 인증된 프로젝트 경로에만 유지합니다.
- [BE] 🛠️ **함수 인덱스 중복 오탐 수정**: `lower(email)` 등 expression index를 평문 컬럼 인덱스의 중복으로 잘못 판단하지 않도록 괄호 파서를 강화했습니다.
- [Docs] README를 상용 기준 기능 설명으로 갱신 (MVP skeleton 표현 제거, share redaction·diff/export 반영).
- [BE] 🔒 **백엔드 공급망 잠금파일 재생성 (CVE 제거 + 드리프트 정합화)**: `backend/requirements.lock`·`backend/requirements-dev.lock`을 `pyproject.toml`에 맞춰 재컴파일했습니다. `pyasn1` 0.6.3→0.6.4 (PYSEC-2026-3455/3456/3457), `pydantic-settings` 2.12.0→2.14.2 (GHSA-4xgf-cpjx-pc3j, `pyproject.toml`이 이미 `>=2.14.2` 선언)을 제거하고, 잠금파일이 누락하고 있던 직접 의존성(`pyjwt`·`aiohttp`·`requests`·`python-multipart` 등)을 정합화했습니다. `pip-audit`: 수정 버전이 없는 `ecdsa` PYSEC-2026-1325(사이드채널, 업스트림이 범위 외로 명시)만 잔존. hash-locked 재설치 계약 유지.
- [FE] 🪄 **관계 자동 추론 기능 추가**: 컬럼 이름(e.g. `user_id`)을 분석하여 연관된 테이블에 자동으로 Foreign Key Edge를 연결하는 버튼을 ERD 편집기 툴바에 추가했습니다.
- [FE] 🗑️ **모든 노드 지우기 기능 추가**: 캔버스의 모든 테이블 노드와 관계를 한 번에 초기화하는 버튼을 툴바에 추가했습니다.
- [FE] 🪄 **관계 자동 추론 기능 추가**: 컬럼 이름(e.g. `user_id`)을 분석하여 연관된 테이블에 Foreign Key Edge를 자동 연결하는 기능을 오른쪽 properties inspector에 추가했습니다.
- [FE] 🗑️ **모든 노드 지우기 기능 추가**: 캔버스의 모든 테이블 노드와 관계를 한 번에 초기화하는 기능을 오른쪽 properties inspector에 추가했습니다.
- [FE] 📋 **테이블 복제 기능 추가**: 편집 모달 내에서 기존 테이블의 구조(컬럼 정보 포함)를 그대로 복사하여 새 테이블 노드로 생성하는 '복제' 버튼을 추가했습니다.
- [FE] `autoInfer.ts`에 대한 단위 테스트 및 UI 컴포넌트 단위 테스트를 추가하여 100% 테스트 커버리지를 유지합니다.
- [FE] ⬇️ **DBML Export**: ERD 다이어그램을 DBML (Database Markup Language) 형식으로 내보낼 수 있는 기능을 추가했습니다. 상단의 DBML 버튼을 클릭하여 다운로드할 수 있습니다.
- [FE] `autoInfer.ts`와 관련 UI 컴포넌트의 회귀 테스트를 추가했습니다. 현재 CI에는 전체 production coverage 임계값이 없으므로 100% 보장을 주장하지 않습니다.
- [FE] ⬇️ **DBML Export**: ERD 다이어그램을 DBML (Database Markup Language) 형식으로 내보낼 수 있는 기능을 추가했습니다. 현재 UI에서는 공유/내보내기 modal의 “기타 산출물”에서 다운로드합니다.
- [FE] 📚 **Data Dictionary Export**: ERD 테이블/컬럼 메타데이터를 CSV 및 Markdown으로 내보내며, CSV formula injection과 Markdown 렌더링 escape를 적용했습니다.
Loading
Loading