Skip to content

English {#english}

Purpose

Single planning document for:

  1. Where tenant signup and orders are stored, how the control plane decides placement, and how provisioning runs through Pulumi (optional), Ansible (Docker / Frappe bench), Zuplo sync, Cloudflare DNS/KV, and POST /internal/provision-complete.
  2. Logpath: correlation IDs and which stores hold provisioning vs runtime traffic logs.
  3. Admin console: unify internal HTML dashboards and simulators under prego-control-plane behind INTERNAL_API_KEY.

Canonical locations (repos)

AreaRepository / store
Tenant master, jobs, traces (D1)prego-control-plane
Product UIPrego monorepo (apps/client-web, etc.)
Images (Docker Hub)prego-docker (e.g. iamfork/prego-repo)
Instance provisioningprego-ansible
Gateway secrets / routingprego-zuplo
Infraprego-pulumi

Full step-by-step and Korean detail: see 한국어 below. Korean mirror of the m admin-web links in Related docs: here.

Implementation status (prego-control-plane)

  • Unified dispatch: POST /v1/tenants triggers the same queue / GitHub workflow_dispatch path as the Stripe webhook. The 202 body includes trace_id (and on idempotent replay when stored) so callers can send x-trace-id on gateway traffic and match Zuplo logs to GET /internal/trace/:trace_id.
  • trace_id in queue + workflow: ProvisionQueueMessage.trace_id is forwarded as a workflow_dispatch input; provision-tenant.yml posts pipeline_started, resolve_server_ok, tenant_dns_finished, ansible_provision_ok, zuplo_sync_ok when trace_id is set (best-effort curl).
  • Failed pipeline callback: callback-failed posts POST /internal/provision-complete with status: Failed, error_message, and failure_stage (not the non-existent /internal/provision-failed).
  • Audit: D1 allocation_decisions, trace_events (order_accepted, pipeline stages via POST /internal/trace-events, provision_completed / provision_failed on callback).
  • Console: GET /internal/console (HTML), GET /internal/console/snapshot, GET /internal/allocation-decisions, GET /internal/tenants-directory, GET /internal/provision-jobs (list items include origin_url and optional origin_url_with_trace, same logic as GET /v1/jobs/:id). The console page surfaces quick-open links (prefers origin_url_with_trace) beside the jobs JSON. Bearer in the browser is kept in sessionStorage under cp_internal_key, shared with onboarding/metrics/AI-flow/RAG/placement-simulation/resource-pool-simulation/email-flow-dashboard internal pages (metrics also keeps legacy prego_dashboard_apikey). If the token field is cleared, Load snapshot… still uses stored cp_internal_key when present.
  • Billing test: GET /internal/billing-test serves an HTML shell (no Bearer on document load); the table is loaded with GET /internal/billing-test-data and the same cp_internal_key / Bearer pattern as other internal UIs. Scripts may call billing-test-data directly with Authorization: Bearer.
  • Metrics dashboard: GET /internal/dashboard prefills the key field from cp_internal_key or legacy prego_dashboard_apikey when the input is empty.
  • Shared nav strip: internal-tools-nav-html.ts (INTERNAL_TOOLS_NAV_HTML) is embedded under the title on /internal/console and every other internal tool page; add new destinations there. Legacy HTML tools are also listed in prego-control-plane src/cp-legacy-tools.ts (navStripHref, internalPath) — keep both in sync; Vitest cp-legacy-nav-coverage.test.ts asserts navStripHref appears in the strip.
  • Smoke script: Prego scripts/smoke-verify-control-plane.sh loops PUBLIC_HTML: /internal/console, /internal/dashboard, /internal/onboarding-dashboard, /internal/placement-simulation, /internal/resource-pool-simulation, /internal/email-flow-dashboard, /internal/billing-test (200 without Bearer on each document GET), then PUBLIC_CP_SPA (React bundle shells — canonical list prego-control-plane src/cp-spa-smoke-paths.ts), then GET /internal/billing-test-data (Bearer), then GET /internal/trial-access-review/requests?page=1&pageSize=1 (Bearer, manual trial approval list), plus R5/R7/R1. Add new public operator HTML paths there, in internal-tools-nav-html.ts, and (legacy HTML tools) in prego-control-plane src/cp-legacy-tools.ts. AI flow / RAG operator HTML lives on prego_ai prego-ai-operator (not CP PUBLIC_HTML).
  • Onboarding status: GET /internal/onboarding-status returns the same additive trace_id, origin_url, origin_url_with_trace fields when the job or tenant row resolves; GET /internal/onboarding-dashboard (HTML) shows a tenant open link using origin_url_with_trace when present.
  • Gateway logs (prego-zuplo): Frappe forward, Assistant UI, demo, External/Agent API, Google OAuth errors, prompt filter, MCP (SSE + OAuth discovery/callback/token), email OTP, billing (e.g. Stripe portal errors), rate-limit and quota policies (Upstash Redis helper warnings; explicit warn on 429 denials for general rate limit, AI rate limit, and quota), and related handlers attach tenant_id (from X-Tenant-Id when present), prego_company_id (path routing key), and cf_ray / http_request_id / optional trace_id (from x-trace-id when the client forwards the provisioning trace); cf_ray also falls back to the Workers request.cf ray id when the cf-ray header is missing — via gateway-runtime-log-fields.ts for Workers analytics / export.
  • Admin onboarding (apps/admin-web): Persists trace_id from free-tenant signup, sends x-trace-id on GET /v1/jobs/:id, optional prego_provision_* sessionStorage resume after refresh, and Open tenant uses origin_url_with_trace from the job API when present (else appends prego_trace). The same trace_id is on the job JSON for welcome-email or other deep links.
  • GET /v1/jobs/:id (prego-control-plane): Response includes canonical origin_url, optional additive origin_url_with_trace (same URL + prego_trace when trace_id is set), and trace_id may be null for legacy rows.
  • Product app (apps/client-web): ProvisionTraceCapture (root layout) reads ?prego_trace= once into sessionStorage (prego_provision_trace_id), strips the query param, and defaultApiHeaders, fetchCurrentUser, auth/onboarding flows, and profile gateway calls attach x-trace-id when present.
  • Regression checks (prego-control-plane repo): npm run verify runs tsc --noEmit and vitest (jobOriginFieldsFromRow / prego_trace URL shaping; internal-billing-test.test.ts; internal-tools-nav-pages.test.ts — shared nav on all smoke PUBLIC_HTML tool pages; cp-legacy-nav-coverage.test.tsINTERNAL_TOOLS_NAV_HTML vs cp-legacy-tools.ts; internal-console-html.test.tsTool links 섹션 미복귀). .github/workflows/ci.yml runs verify, then npm ci --prefix admin, npm run lint --prefix admin, and npm run admin:build for the React operator UI in admin/, on push to main and on pull requests.
  • Still operational: enabling Logpush (or equivalent) to your bucket and downstream SIEM remains a Zuplo / Cloudflare dashboard step, not defined in this repo.

Apply D1 migration 0022_allocation_decisions.sql after deploy.


한국어 {#korean}

문서 목적

  1. Free/Paid 신청부터 Frappe 런타임·Zuplo·Cloudflare까지 실제 코드베이스 기준 흐름을 단계별로 정리한다.
  2. logpath(추적 단위) 를 정의해 프로비저닝·환경 설정·런타임 트랜잭션 조사가 끊기지 않게 한다.
  3. prego-control-plane통합 모니터링·관리자 UI 방향을 제시한다.

영문 Related docs 절과 같은 계열: m.pregoi.com admin-web UX implementation, m company profile · footer/cookie, m onboarding step connector. m.pregoi.com 기획 A–D: Stripe·Pay · 퍼널 관측 · logpath 전 구간 · Activate·포털.

저장 위치 (신청·사용자·주문)

플랫폼 정본: prego-control-plane의 Cloudflare D1

  • tenants_master — 테넌트 ID, 상태, 리전, plan_tier, 서브도메인, canonical_hostname, 회사·관리자·additional_users
  • provision_jobsjob_id, tenant_id, trace_id, status, infra_mode, plan_tier
  • Stripe 멱등: provider_events; 유료: billing_customers, subscriptions
  • 프로비저닝 완료 후: tenant_runtime, secret_refs, KV TENANT_ORIGINS
  • 관측·할당 보조: nodes, server_metrics, tenant_allocations, usage 스냅샷 테이블 등

Prego 모노레포의 UI 가입 화면과 D1 마스터는 연결은 HTTP 계약으로 유지한다 (repo responsibility matrix).

Free vs Paid

경로설명
FreePOST /v1/tenantstenants_master + provision_jobs (Pending), 배치는 decidePlacement; 202 응답에 trace_id 포함(클라이언트가 x-trace-id 로 게이트웨이에 전달 가능)
PaidPOST /v1/checkout → Stripe → checkout.session.completed 웹훅 → 동일 계열 INSERT + billing. 메타데이터에 tenant_id가 없으면 웹훅에서 새 ID 생성할 수 있음

운영 주의: Free(POST /v1/tenants)·Paid(Stripe) 모두 동일한 triggerProvisionPipeline 경로를 쓰지만, PROVISION_QUEUE 또는 GITHUB_TOKEN+GITHUB_WORKFLOW_DISPATCH_URL이 없으면 D1에만 쌓이고 GitHub 워크플로는 실행되지 않는다.

리소스 할당 로직 (Control Plane)

  • decidePlacement: nodes + server_metrics(노드 푸시 스냅샷), 플랜(Enterprise → dedicated 등), PLACEMENT_MEMORY_PCT_MAX / PLACEMENT_MAX_TENANTS_PER_NODE 등으로 기존 노드 vs create_new_server 결정.
  • 근거 로그: allocation-decision-log.ts 등 분석 로직은 있으나, 매 결정을 D1에 구조화해 남기는 것은 별도 마이그레이션·API로 보완하는 편이 좋다 (후보, 점수, 거절 사유, 정책 버전).

실행 파이프라인 (Docker Hub → 인스턴스 → Ansible → Zuplo → Cloudflare → 콜백)

  1. 트리거: GitHub Actions workflow_dispatch 또는 prego-provision-queue 소비 후 멱등 workflow_dispatch (workflow_dispatch_log).
  2. Pulumi(선택): 신규 서버 시 IP 확보 → POST /internal/nodes 등으로 노드 등록.
  3. Ansible (prego-ansible): Docker, MariaDB/Redis 역할, frappe_bench + frappe_site, 산출물 artifacts/tenant_api_key.txt.
  4. Docker Hub (prego-docker): 커스텀 이미지 빌드·푸시; 인스턴스는 docker pull로 수급 — “Hub 통해 Frappe 설치”는 이미지 공급 + Ansible 컨테이너 기동으로 이해한다.
  5. Zuplo: zuplo_sync 등으로 테넌트 API 키 등록; 원문 키는 로그에 남기지 않음.
  6. Cloudflare: 서브도메인·DNS 설계는 tenant-subdomain-dns-design 참고; KV 오리진은 provision-complete로 동기화. DNS 레코드 자동화 담당 컴포넌트는 trace stage로 명시하는 것이 안전하다.
  7. POST /internal/provision-complete: D1·KV 최종 반영, Bearer INTERNAL_API_KEY.

Logpath 기획

상관 ID

  • trace_id — 한 온보딩·프로비저닝 세션 (provision_jobs에 저장)
  • job_id — 큐·GitHub 멱등·재시도 단위
  • tenant_id — 장기 식별·과금·게이트웨이
  • gh_run_id — Actions 로그와 조인 ( trace_events 등에 기록 권장 )

D1

  • traces / trace_events, audit_logs, workflow_dispatch_log
  • GET /internal/trace/:trace_id로 조회

남는 갭: provision-tenant.ymltrace_id가 있을 때 resolve·DNS·Ansible·Zuplo 단계를 trace_events에 남긴다. 수동 workflow_dispatch(빈 trace_id)이나 큐에 오래된 메시지(필드 누락)는 타임라인이 얇을 수 있다.

런타임(사용자 트랜잭션)

  • prego-zuplo: Frappe/Assistant UI/데모/External·Agent API·Google OAuth·prompt filter·MCP·email OTP·billing·rate-limit·quota(Redis) 등 context.logtenant_id·prego_company_id·cf_ray(헤더 또는 request.cfhttp_request_id(선택)·x-trace-idtrace_id(선택)를 붙임(modules/gateway-runtime-log-fields.ts). 일반·AI rate limitquota 429 거부 시 추가 warn 로그로 상관 가능. Logpush·버킷 적재는 운영에서 연결.
  • 모노레포 UI: apps/admin-web — Free 온보딩에서 trace_id 보관, 잡 폴링 시 x-trace-id, 새로고침 시 prego_provision_* 세션 복구, 완료 시 GET /v1/jobs/:id 응답의 origin_url_with_trace(없으면 클라이언트에서 prego_trace 추가). apps/client-web — **ProvisionTraceCapture**가 **?prego_trace=**를 **prego_provision_trace_id**에 저장 후 URL에서 제거; defaultApiHeaders·인증·온보딩·프로필 Zuplo 호출에 x-trace-id 병합.
  • Zuplo Logpush, Workers 로그, Frappe/호스트 로그 등은 D1 밖 저장소가 주력 — tenant_id·cf_ray/http_request_id·(클라이언트가 넘기면) x-trace-id 로 D1 trace_id 와 조인 가능.
  • 노드 리소스: POST /internal/server-metrics 및 (기획) 시계열 스냅샷 테이블로 확장 (resource-observability-and-cost-planning 참고).
  • 온보딩 단계 조회: GET /internal/onboarding-status JSON에 trace_id, origin_url, origin_url_with_trace 포함(잡/테넌트 조인 가능 시); GET /internal/onboarding-dashboard HTML에서 테넌트 URL이 있으면 열기 링크 표시.
  • 내부 HTML Bearer: 브라우저 sessionStoragecp_internal_key — 콘솔·온보딩·메트릭 대시보드·AI flow·RAG·placement/resource-pool 시뮬레이션·이메일 플로 대시보드 등에서 동기화(메트릭은 레거시 prego_dashboard_apikey 병행). /internal/console 는 Bearer 입력란이 비어 있어도 저장된 키로 스냅샷·조회 요청 가능. /internal/dashboard 는 입력란이 비면 위 두 키로 API 키 칸을 프리필한다.
  • 내부 HTML 공통 네비: internal-tools-nav-html.ts (INTERNAL_TOOLS_NAV_HTML)가 /internal/console 포함 모든 내부 도구 HTML 상단에 붙는다. 레거시 HTML 도구는 prego-control-plane src/cp-legacy-tools.ts (navStripHref, internalPath)와 함께 갱신; Vitest **cp-legacy-nav-coverage.test.ts**가 스트립과 정합성 검사.

통합 모니터링 관리자 페이지 (prego-control-plane)

목표: /internal/billing-test, placement·resource-pool 시뮬레이션, 이메일/AI 플로 대시보드 등 분산 HTML한 네비게이션(예: /internal/console) 으로 묶는다.

탭 예시

  • 홈: 신규/ Pending·Failed job, 포화 노드, 웹훅 건강도
  • 테넌트 디렉터리: tenants_master 검색, billing·runtime·allocations
  • 잡/파이프라인: job_id, dispatch 로그, (가능 시) GitHub run 링크
  • Trace: trace_id 타임라인
  • 시뮬레이터: 기존 placement / resource-pool (D1 비쓰기 모드 UI 표기 유지)
  • 할당 근거: (향후) allocation_decisions 또는 표준화된 JSON

보안: 전 구간 INTERNAL_API_KEY; API Key·시크릿은 ref만 표시.

구현 우선순위 제안

1–4는 prego-control-plane + provision-tenant.yml 기준으로 반영됨(dispatch 통일, trace_events, allocation_decisions, /internal/console).
5. Zuplo Logpush 등 런타임 로그에 tenant_id 일관 주입은 prego-zuplo/관측 스택에서 진행.


Implementation status (prego-control-plane 레포)

기획 항목상태비고
Free/Paid 동일 Queue·workflow_dispatch적용됨POST /v1/tenantstriggerProvisionPipeline (provision-pipeline-trigger.ts)
allocation_decisions + order_accepted trace적용됨마이그레이션 0022_allocation_decisions.sql, provision-order-audit.ts
POST /internal/trace-events적용됨파이프라인 단계 append; 워크플로에서 trace_id 입력 시 단계별 POST
Queue·dispatch에 trace_id적용됨ProvisionQueueMessage → GitHub inputs
실패 콜백적용됨provision-complete + Failed + failure_stage (구 /internal/provision-failed 제거)
provision-complete → trace적용됨provision_completed / provision_failed
/internal/console적용됨스냅샷·할당·잡·테넌트·trace 조회 UI (internal-console-html.ts); 최근 잡에 대해 origin_url_with_trace 우선 빠른 열기 링크; Bearer 칸이 비어 있어도 cp_internal_key 로 폴백
/internal/console/snapshot적용됨24h 실패 건수 필드 추가
테넌트 디렉터리·잡 목록 API적용됨GET /internal/tenants-directory, GET /internal/provision-jobs — 후자는 항목마다 origin_url / origin_url_with_trace (GET /v1/jobs/:id와 동일한 jobOriginFieldsFromRow)
GET /v1/jobs/:idorigin_url_with_trace적용됨jobs.ts: prego_trace 보조 필드, trace_id는 null 가능(레거시 행)
GET /internal/onboarding-status — origin·trace적용됨internal.ts: jobOriginFieldsFromRow 공유; 온보딩 대시보드 HTML 링크
Zuplo 런타임 로그 필드부분 적용prego-zuplo gateway-runtime-log-fields.ts — Frappe/Assistant UI·OAuth·prompt filter·MCP·email OTP·billing·rate-limit·quota(Redis) 등 + cf_ray(헤더 또는 request.cfhttp_request_id(선택)·trace_id(x-trace-id). Logpush는 포털·인프라 별도 연결
admin-web 온보딩 trace / 잡 폴링 / prego_trace적용됨Prego apps/admin-webx-trace-id on job poll, session resume, Open tenant query handoff
client-web trace capture / gateway 헤더적용됨Prego apps/client-webProvisionTraceCapture, api-client provisionTraceHeaders / gatewayJsonHeaders, defaultApiHeaders
jobOriginFieldsFromRow 테스트 + GitHub CI적용됨npm run verify (lint + vitest); .github/workflows/ci.yml
Placement·resource-pool·이메일 플로 등 내부 HTML — cp_internal_key적용됨/internal/placement-simulation, /internal/resource-pool-simulation, /internal/email-flow-dashboard에서 입력 필드 비어 있을 때 저장된 키로 Bearer 복원·재저장(콘솔·RAG 등과 동일 패턴)
/internal/billing-test + billing-test-data적용됨HTML 껍데기는 인증 없이 제공; 테이블 데이터는 GET /internal/billing-test-data(Bearer). 브라우저는 cp_internal_key
메트릭 대시보드 API 키 프리필적용됨/internal/dashboard 입력란이 비면 cp_internal_key·prego_dashboard_apikey 로 채움
스모크 스크립트 (HTML 셸·billing·trial review)적용됨smoke-verify-control-plane.shPUBLIC_HTML 9경로(무인증 GET 200) + PUBLIC_CP_SPA 4경로(React /cp 셸) + /internal/billing-test-data(Bearer) + /internal/trial-access-review/requests?page=1&pageSize=1(Bearer) + R5/R7/R1; 신규 공개 HTML은 배열·internal-tools-nav-html.ts·(레거시 HTML 도구) src/cp-legacy-tools.ts 동시 갱신
billing-test* Vitest적용됨internal-billing-test.test.ts — Bearer 401/200, JSON { items }, HTML 셸에 billing-test-data·cp_internal_key 문자열 포함
내부 도구 HTML 공유 네비 Vitest적용됨internal-tools-nav-pages.test.ts — 스모크 PUBLIC_HTML 와 동일한 HTML 소스에 공통 스트립; cp-legacy-nav-coverage.test.ts — 스트립 vs cp-legacy-tools.ts; internal-console-html.test.tsTool links 섹션 미복귀
내부 HTML 공통 네비적용됨internal-tools-nav-html.ts/internal/console 및 기타 내부 HTML 상단 동일 스트립; 레거시 HTML 도구는 cp-legacy-tools.ts (navStripHref)와 함께 갱신

배포 시 D1에 0022 마이그레이션 적용 필요 (npm run db:migrate).


작성 기준: Prego 모노레포·prego-control-plane·prego-ansible·prego-docker 저장소 상태 (코드 변경 시 이 문서의 “갭” 단락을 갱신할 것).

Help