FractalOps 운영 매뉴얼 (한글)
FractalOps 운영 매뉴얼 (한글)
Section titled “FractalOps 운영 매뉴얼 (한글)”1. 먼저 볼 문서
Section titled “1. 먼저 볼 문서”- 제품 헌법: FractalOps Constitution
- 제품 정의: FractalOps Canonical Architecture
- 런타임 토폴로지: Architecture Overview
- Ouroboros/HUD 운영: Ouroboros Studios
이 문서는 운영자용 미러다. 제품 법의 정본은 영어 constitution 문서에 있고, 제품 정의의 정본은 영어 canonical architecture 문서에 있다.
2. 운영 핵심
Section titled “2. 운영 핵심”Portal이 기본 인간 workflow surface다.Proposal Plane이 non-read mutation gate다.Semantics graph + ClickHouse warehouse + Chronicle evidence가 proof path다. DataHub는 프로젝트 RDF 관리 에이전트가 보고한 catalog/lineage 누적면이고, 같은 수명주기는 ClickHouse event/fact로도 쌓인다.Ouroboros는 compactAgent HUD로 Portal을 다시 개선하는 루프다.- API는 큐잉만 하고, durable job 실행은 Temporal이 맡는다.
- LangGraph는 agent graph/thread/checkpoint 연속성만 맡는다.
- Studio는 run/session/report/activity/inbox 상태의 정본이다.
- 실시간 truth는
portal_live_events -> harness-projection이다. - Ouroboros continuity의 공개 계약은
fresh | resume이다. - FractalOps가 강하게 소유하는 것은 자기 runtime 과 execution substrate다.
portal,api,worker,execution-runtime,Temporal, DB,Hasura,Supabase Realtime,Daytona,PlaywrightGrid
- 그 외 다수 스택은
integration endpoint로 취급한다.- URL
- auth
- health 계약이 우선이며, 기본적으로 별도 child system처럼 다루지 않는다.
- 예외는 영구 정책이 아니라 만료형 부채다.
3. 비동기 백엔드와 agent graph 경계
Section titled “3. 비동기 백엔드와 agent graph 경계”Temporal 경로:
- API에서 잡 생성
JobService가enqueue_job(...)호출fractalops-job-workflow시작- 워크플로가 활동(Activity)에서 동일 도메인 작업 실행
즉, durable queue/retry/schedule은 Temporal로 고정하고 비즈니스 로직은 동일하게 유지한다.
LangGraph 경로:
- Studio가 agent session/runtime spec을 만든다.
- Temporal activity가 Studio run execute job을 호출할 수 있다.
- Studio가 LangGraph agent server에 thread/run을 시작한다.
- LangGraph는 frontier, fanout, checkpoint, HITL graph state만 관리한다.
- 실제 session truth는 Studio run/session/report/activity로 다시 투영된다.
금지:
- Temporal workflow 안에 LangGraph checkpoint/state logic을 넣지 않는다.
- LangGraph를 durable job queue로 쓰지 않는다.
- Studio service가 별도 durable scheduler를 새로 만들지 않는다.
- 운영 CLI loop는 관측/복구 driver일 뿐 canonical queue가 아니다.
4. 필수 환경 변수
Section titled “4. 필수 환경 변수”4.1 공통
Section titled “4.1 공통”FRACTALOPS_API_TOKEN- DB 선택:
- k8s canonical DB 사용:
FRACTALOPS_DATABASE_RUNTIME_TYPE=k8s+FRACTALOPS_DATABASE_K8S_NAMESPACE+FRACTALOPS_DATABASE_K8S_SERVICE+FRACTALOPS_DATABASE_URL - 외부 DB 사용:
FRACTALOPS_DATABASE_RUNTIME_TYPE=external+FRACTALOPS_DATABASE_URL=... - 토폴로지 자동 생성 시:
runtime_dependencies.postgres.runtime_type=k8s|external
- k8s canonical DB 사용:
FRACTALOPS_IDP_BASE_URLFRACTALOPS_IDP_REALM- Keycloak 인증(둘 중 하나):
FRACTALOPS_IDP_CLIENT_ID+FRACTALOPS_IDP_CLIENT_SECRETFRACTALOPS_IDP_ADMIN_USERNAME+FRACTALOPS_IDP_ADMIN_PASSWORD
- (선택)
FRACTALOPS_IDP_LOGIN_REALM(기본:FRACTALOPS_IDP_REALM) FRACTALOPS_ACTIVE_CONNECTORS(예:github,pve,daytona,penpot,nexus, env 검증/런타임 모두 동일 필터)FRACTALOPS_FAIL_ON_EMPTY_DIRECTORY=true(권장, 그룹 0건이면 즉시 실패)
4.4 권한/그룹 YAML 매핑 (FractalOps SSOT)
Section titled “4.4 권한/그룹 YAML 매핑 (FractalOps SSOT)”- API 경로:
GET /v1/admin/policy-canvas/authority-group-mapping(X-FractalOps-Tenant-Id: default헤더 필수)PUT /v1/admin/policy-canvas/authority-group-mappingPUT /v1/admin/policy-canvas/authority-group-mapping/from-graph
- 저장 방식:
- canonical YAML (
yaml_text)이 SSOT - React Flow 그래프(
graph.nodes/edges)는 YAML에서 파생/동기화
- canonical YAML (
- 포털 편집 UX:
/admin/authority-mapping(요청 헤더X-FractalOps-Tenant-Id사용)super_admin권한에서만 접근 가능
4.5 Team Access Workbench
Section titled “4.5 Team Access Workbench”- API 경로:
GET /v1/admin/policy-canvas/team-access-workbenchPUT /v1/admin/policy-canvas/team-access-workbenchPUT /v1/admin/policy-canvas/team-access-workbench/from-graph
- 포털 편집 UX:
/admin/access-workbenchsuper_admin권한에서만 접근 가능
- 관리 대상:
groupprojectsecret refstack
- secret 정책:
- 원문 secret 저장 금지
backend는openbao | github_actions만 허용secret_ref는 필수
- Workbench 결과는
semantics -> DataHub -> Valkeyprojection에 포함된다.
4.2 Temporal 사용 시
Section titled “4.2 Temporal 사용 시”FRACTALOPS_TEMPORAL_EXECUTOR_ADDRESS=<temporal-host>:7233(정본 contract)FRACTALOPS_TEMPORAL_ADDRESS=<temporal-host>:7233(legacy fallback)FRACTALOPS_TEMPORAL_NAMESPACE=fractalopsFRACTALOPS_TEMPORAL_TASK_QUEUE=fractalops-jobsFRACTALOPS_TEMPORAL_RECONCILE_SCHEDULE_ENABLED=trueFRACTALOPS_TEMPORAL_RECONCILE_INTERVAL_SECONDS=3600
4.3 OpenBao(Vault 호환) 시크릿 백엔드
Section titled “4.3 OpenBao(Vault 호환) 시크릿 백엔드”FRACTALOPS_SECRET_BACKEND=openbaoFRACTALOPS_OPENBAO_ADDR=http://<openbao-host>:8200FRACTALOPS_OPENBAO_TOKEN=<token>또는FRACTALOPS_OPENBAO_TOKEN_FILE=<token-file-path>FRACTALOPS_OPENBAO_MOUNT=kvFRACTALOPS_OPENBAO_PREFIX=fractalopsFRACTALOPS_OPENBAO_VERIFY_TLS=true
조회 우선순위:
- API 요청 SSOT payload
- OpenBao scope (
connectors,connectors/<provider>,runtime/<service>) - 환경변수
민감키 강제 모드(권장):
FRACTALOPS_SECRET_REQUIRE_REF_FOR_SENSITIVE=true- 이 값을 켜면
TOKEN/SECRET/PASSWORD계열 env 평문은 무시하고ref:또는 OpenBao 조회값만 사용한다.
.env 값을 OpenBao scope로 동기화:
make infra-openbao-sync SCOPE=connectors/daytona KEYS="FRACTALOPS_DAYTONA_API_TOKEN,FRACTALOPS_DAYTONA_ACCESS_URL"make infra-openbao-sync SCOPE=connectors/msgraph KEYS="FRACTALOPS_MSGRAPH_CLIENT_ID,FRACTALOPS_MSGRAPH_CLIENT_SECRET"make infra-openbao-sync SCOPE=runtime/postgres KEYS="username,password,dbname,host,port,superuser_username,superuser_password,FRACTALOPS_DATABASE_URL".env 평문을 ref:로 일괄 치환(+OpenBao 동기화):
make infra-ssot-harden-env ENV_FILE=.env TENANT_ID=default호스트/LXC/VM 잔존 민감값 점검:
make infra-sensitive-audit5. 실행 절차
Section titled “5. 실행 절차”5.0 토폴로지 검증(필수)
Section titled “5.0 토폴로지 검증(필수)”make infra-validatemake infra-generate-envmake infra-env-checkmake infra-cloudflare-planmake infra-cloudflare-apply-dry-runmake infra-check.tools/uv/uv run python ops/lxc/preflight.py --env-file build/.env.embeddedinfra-check 규칙:
FRACTALOPS_DATABASE_URL이 비어 있으면 Postgres 포트 체크를 생략한다..env값을 기본으로 읽고, 셸 환경변수가 있으면 셸 환경변수가 우선한다.
실환경 프로파일을 바꿀 때:
make infra-validate TOPOLOGY=ops/infra/topology/lxc-pve-lab.yaml기본 프로파일은 ops/infra/topology/lxc-pve-lab.yaml 이다.
토폴로지 선택형 항목:
identity_plane.session_trust.enabledfalse: session_trust 미사용(필수 토큰 체크 생략)true+provider=pomerium: Pomerium 인증 SSOT + (선택) Cloudflare tunnel/DNS 자동화
runtime_dependencies.openbao- OpenBao CT, address, TLS 검증 여부를 topology SSOT로 고정
make infra-generate-env시FRACTALOPS_OPENBAO_ADDR,FRACTALOPS_OPENBAO_PCT_VMID,FRACTALOPS_OPENBAO_VERIFY_TLS가 자동 생성됨
Cloudflare 토큰까지 검증하려면:
make infra-cloudflare-plan-verify TOPOLOGY=ops/infra/topology/lxc-pve-lab.yamlCloudflare 실반영은 명시적으로만 수행:
# 기본은 dry-runmake infra-cloudflare-apply-dry-run TOPOLOGY=ops/infra/topology/lxc-pve-lab.yaml# 실제 writemake infra-cloudflare-apply TOPOLOGY=ops/infra/topology/lxc-pve-lab.yamlSSOT 하드코딩 후보 점검:
make infra-ssot-scanmake infra-ssot-scan-strict솔루션 Operation Fabric:
make operation-listmake operation-auditmake operation-run OP_ID=configure_github_idp DRY_RUN=1make operation-run OP_ID=configure_github_idp CONFIRM_UPDATE=1 SSOT_JSON_FILE=./ssot/github-idp.jsonNexus 인증 팩(Pomerium brokered registry):
# 1) edge pass-through + plugin auth 적용FRACTALOPS_NEXUS_CT_ID=113 \FRACTALOPS_NEXUS_NODE=pve0 \FRACTALOPS_NEXUS_AUTH_MODE=pomerium_proxy \FRACTALOPS_NEXUS_AUTH_PLUGIN=nexus-pomerium \make nexus-plugin-pack
# 2) 서비스 토큰/registry JWT 인증 경로 E2E (whoami)FRACTALOPS_NEXUS_CT_ID=113 \FRACTALOPS_NEXUS_NODE=pve0 \FRACTALOPS_SERVICE_TOKEN_CLIENT_ID=<service_token_client_id> \make nexus-pomerium-e2e
# 3) publish/install E2EFRACTALOPS_NEXUS_CT_ID=113 \FRACTALOPS_NEXUS_NODE=pve0 \FRACTALOPS_NEXUS_LOCAL_URL=http://127.0.0.1:4873 \FRACTALOPS_SERVICE_TOKEN_CLIENT_ID=<service_token_client_id> \make nexus-publish-sample PACKAGE_NAME=@yamon/fractalops-e2e-20260306 VERSION=1.0.7실검증 통과 기준:
nexus-pomerium-e2e결과ok=true,status_code=200nexus-publish-sample결과publishStatus=201,metadataStatus=200,tarballStatus=200
실행 결과 파일(실행 호스트):
/root/output/nexus_publish/*.json
Nexus publish 검증(서비스 토큰 env 파일 사용, provider-agnostic):
SERVICE_TOKEN_ENV_FILE=/root/keycloakupsert/secrets/nexus-ci-access-token.env \make nexus-publish-sample ENV_FILE=.env직접 환경변수 주입도 가능:
FRACTALOPS_NEXUS_POMERIUM_SERVICE_TOKEN_CLIENT_ID_HEADERFRACTALOPS_SERVICE_TOKEN_CLIENT_ID또는FRACTALOPS_SERVICE_TOKEN_CLIENT_IDS
개발자 CLI 사용법(gh auth 유사):
# 사용자명 힌트 생성(이메일 전체 대신 URL-safe 사용자명)uv run fractalops registry hint \ --registry-url https://nexus.example.com \ --subject-key goliath@example.com \ --username goliath
# FractalOps 디바이스 코드 로그인(권장, 비밀번호 없음)uv run fractalops registry login \ --portal-url https://portal.example.com \ --principal-type human \ --group-paths-csv /org/solutions/nexus/roles/publisher \ --registry-url https://nexus.example.com \ --ttl-seconds 3600
# 자동화/관리자 전용(머신 모드, API 토큰 필요)uv run fractalops registry login-machine \ --portal-url https://portal.yamon.io \ --api-token "$FRACTALOPS_API_TOKEN" \ --subject-key goliath@example.com \ --group-paths-csv /org/solutions/nexus/roles/publisher \ --registry-url http://127.0.0.1:4873주의:
- npm username에 이메일(
user@company.com)을 그대로 넣지 말고 URL-safe 값 사용 registry-login은 사람 전용 디바이스 코드 흐름만 지원하며, 비밀번호 입력은 없다- 자동화는
registry-login-machine으로 분리하여 사용한다 registry-login은//<registry>/:_authToken=<token>형태로.npmrc를 갱신함- 사용자 토큰 경로는 unscoped publish/install 실검증 완료
- scoped publish(
@yamon/pkg)는 CI/자동화에서는 서비스 토큰 헤더 경로 사용 권장 - 결과 파일명은 경로안전 형태로 저장되므로
/포함 package name도 실패하지 않음
5.1 기본(Temporal, uv 실행)
Section titled “5.1 기본(Temporal, uv 실행)”cp .env.example .envmake uv-bootstrapmake installmake migratemake apimake temporal-workerAlembic 리비전 생성은 수동 편집 대신 코드젠(autogenerate) 사용:
make db-revision MSG="add audit events"make db-revision-local MSG="add audit events"make migrateCodegen(OpenAPI/Contracts/Portal/Connectors) 재생성 및 검증:
make codegen-openapimake codegen-contract-modelsmake codegen-connectorsmake codegen-portal-clientmake codegen-check검증 스택:
make test-unitmake test-contractmake test-integration원칙:
test-unit은 quiet/fail-onlyunittest_runner를 사용한다test-contract는 curated Schemathesis contract suite다test-integration은 shared runtime smoke만 사용한다- shared service
5xx는 local Docker fallback이 아니라skip + report로 처리한다
msgraph가 relay 전략일 때 명령 템플릿을 먼저 지정:
export FRACTALOPS_RELAY_CMD_TEMPLATE='relay-compiler --src {output} --schema {spec}'msgraph 커넥터 readiness를 ready로 올리려면:
export FRACTALOPS_MSGRAPH_TENANT_ID=...export FRACTALOPS_MSGRAPH_CLIENT_ID=...export FRACTALOPS_MSGRAPH_CLIENT_SECRET=...6. 검증 체크리스트
Section titled “6. 검증 체크리스트”GET /healthz200GET /v1/connectors/capabilities200POST /v1/jobs/sync/access큐잉 성공GET /v1/jobs/{job_id}상태 전이 (queued -> running -> succeeded|failed)make test-unit통과make test-contract통과make test-integration통과 또는 shared outage 기준skip + reportmake codegen-check통과POST /v1/events/webhook/{provider}시그니처 검증 + 이벤트 처리 (github지원,/v1/events/github호환 유지)X-GitHub-Event헤더 필수(헤더 누락 시 400)
PUT/GET/DELETE /v1/connectors/bindings/*정상 동작POST/PUT/GET/DELETE /v1/connectors/capabilities/stored/*정상 동작PUT/GET/DELETE /v1/sync/checkpoints*정상 동작POST/GET /v1/audit/events*정상 동작GET /v1/wizard/profiles+POST /v1/wizard/try정상 동작POST /v1/connectors/preflight/{connector_id}정상 동작
6.1 SCIM 서버 검증
Section titled “6.1 SCIM 서버 검증”SCIM 토큰을 켠 경우:
export FRACTALOPS_SCIM_BEARER_TOKEN='replace-me'curl -s "${FRACTALOPS_BASE_URL}/scim/v2/ServiceProviderConfig" \ -H "Authorization: Bearer ${FRACTALOPS_SCIM_BEARER_TOKEN}"SCIM 실패 응답 규약:
detail.schemas[0] == urn:ietf:params:scim:api:messages:2.0:Errordetail.scimType는invalidToken | invalidSyntax | invalidValue | noTarget | uniqueness중 하나
그룹 조회/필터:
curl -s "${FRACTALOPS_BASE_URL}/scim/v2/Groups?startIndex=1&count=50&filter=meta.lastModified gt \"2026-02-25T00:00:00Z\"" \ -H "Authorization: Bearer ${FRACTALOPS_SCIM_BEARER_TOKEN}"의도:
- SCIM 지원 솔루션은 FractalOps SCIM 엔드포인트로 바로 연동
- SCIM 미지원 솔루션은 기존 커넥터(delta_cursor/reconcile/request)로 보정
6.2 systemd 배포 (LXC 레거시 경로)
Section titled “6.2 systemd 배포 (LXC 레거시 경로)”cp .env.example .envmake uv-bootstrapmake installmake unit-installsystemctl daemon-reloadsystemctl enable --now fractalops-api fractalops-temporal-worker원클릭 실행:
make bootstrap-lxc현재 canonical runtime:
fractalops-api,fractalops-worker,fractalops-portal은k8s/Argo배포를 기본으로 본다.- systemd/LXC 경로는 복구, 재현, 레거시 점검용으로만 유지한다.
Temporal 잡 경로 스모크:
make smoke-temporal커넥터 executor 준비상태 확인:
make check-readinessPVE OIDC 사용자 로그인/권한 점검(비밀번호 티켓 검증 제외):
FRACTALOPS_E2E_PVE_USERID=user@example.com@keycloak \make check-pve-oidc-access원칙:
openidrealm 사용자는/access/ticket비밀번호 검증을 사용하지 않는다.- 검증 경로는
Cloudflare Access → Microsoft → PVE OpenID redirect기준으로 본다.
배포 성공 게이트(health/readiness/systemd):
make deploy-gate호스트에서 systemd 검사 없이 점검하려면:
FRACTALOPS_GATE_SKIP_SYSTEMD=true make deploy-gatePVE OIDC 사용자까지 게이트에 포함하려면:
FRACTALOPS_E2E_PVE_USERID=user@example.com@keycloak make deploy-gate커넥터 실운영 검증 스위트(성공/실패/재시도):
make connector-suite빠른 로컬 검증(권장):
FRACTALOPS_SUITE_CONNECTORS=github,pve,daytona,penpot,nexus \FRACTALOPS_JOB_TIMEOUT_SECONDS=25 \FRACTALOPS_JOB_POLL_SECONDS=1 \make connector-suite-fast참고:
connector-suite*는 로컬 API(127.0.0.1:8088) 환경에서 API/worker를 임시 기동한다.- 로컬 Temporal 큐를
<기존큐>-local로 분리해 기존 CT worker와 충돌을 방지한다.
바인딩/위저드 통합 preflight:
curl -s -X POST "${FRACTALOPS_BASE_URL}/v1/connectors/preflight/github" \ -H "Content-Type: application/json" \ -H "X-Api-Key: ${FRACTALOPS_API_TOKEN}" \ --data '{"tenant_id":"default","ssot":{"org":"example-org","token":"..."}}'provider별 SSOT 템플릿 조회:
curl -s -X GET "${FRACTALOPS_BASE_URL}/v1/wizard/template/msgraph" \ -H "X-Api-Key: ${FRACTALOPS_API_TOKEN}"wizard binding upsert(SSOT) 실행:
curl -s -X POST "${FRACTALOPS_BASE_URL}/v1/wizard/try" \ -H "Content-Type: application/json" \ -H "X-Api-Key: ${FRACTALOPS_API_TOKEN}" \ --data '{ "tenant_id":"default", "provider":"msgraph", "apply_binding":true, "update_mode":"ask", "confirm_update":false, "strict":true, "ssot":{ "display_name":"microsoft-entra", "tenant_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "client_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "client_secret":"...", "domain_filter":"@example.com", "default_group":"employees", "disable_missing_users":"true", "http_timeout_seconds":"15" } }'Makefile 단축 실행:
make wizard-apply PROVIDER=msgraph SSOT_JSON='{"tenant_id":"...","client_id":"...","client_secret":"..."}'동작 규칙:
- binding이 없으면
created - binding이 있으면 기본
update_mode=ask에서needs_confirmation confirm_update=true또는update_mode=force면updated- 동일 apply 요청은 idempotency key 자동 생성으로 중복 생성 방지
(
strict/update_mode/confirm_update/enabled포함) apply_binding=true는 DB(provider binding 저장소) 연결이 필수이며, 실패 시wizard binding upsert failed: provider binding store unavailable예외를 반환
환경 패키지 최우선:
FRACTALOPS_WIZARD_PACKAGE_<PROVIDER>FRACTALOPS_WIZARD_PACKAGE(provider별 JSON 맵 가능)- 우선순위:
env package > request ssot > defaults - 기본 업데이트 모드:
FRACTALOPS_WIZARD_UPDATE_MODE=ask|force|skip
필수 커넥터를 지정해서 점검(실패 코드 필요 시 --fail-on-unready):
FRACTALOPS_REQUIRED_CONNECTORS=github,pve ./.tools/uv/uv run python ops/lxc/check_connector_readiness.py --fail-on-unready잡 큐잉 단계에서 미준비 커넥터를 차단하려면:
export FRACTALOPS_REQUIRED_CONNECTORS=github,pveexport FRACTALOPS_FAIL_ON_UNREADY=trueexport FRACTALOPS_FAIL_ON_PREFLIGHT=true이 상태에서 POST /v1/jobs/sync/access 호출 시 필수 커넥터가 준비되지 않았으면 409를 반환한다.
요청 바디에서 required_connectors를 추가하면(예: ["github"]) 환경변수와 합집합으로 게이트가 적용된다.
추가로 preflight_ssot를 넣으면 커넥터별 토큰/ID를 preflight에 직접 주입할 수 있다.
입력 키는 provider 템플릿 기준으로 자동 보정되어 잡 payload에 저장된다.
게이트 차단 시 audit에 job.access_sync.blocked 이벤트가 기록된다.
preflight 차단 응답의 detail.details[]에 missing_inputs, permission_hints, ssot_template가 함께 들어온다.
실제 커넥터 액션 E2E(request 모드, 기본: GitHub 1건):
make e2e-request실구동 reconcile 검증(실제 큐잉/실행/완료):
make live-reconcileGitHub 웹훅 인바운드 검증(서명 검증 포함):
export FRACTALOPS_GITHUB_WEBHOOK_SECRET='replace-me'body='{"zen":"keep it logically awesome","hook_id":1}'sig=$(python - <<'PY'import hmac, hashlibsecret='replace-me'.encode()body='{"zen":"keep it logically awesome","hook_id":1}'.encode()print("sha256=" + hmac.new(secret, body, hashlib.sha256).hexdigest())PY)curl -i -X POST "${FRACTALOPS_BASE_URL}/v1/events/github" \ -H "Content-Type: application/json" \ -H "X-GitHub-Event: ping" \ -H "X-GitHub-Delivery: test-delivery-1" \ -H "X-Hub-Signature-256: ${sig}" \ --data "$body"커넥터 바인딩 영속 검증(tenant별):
curl -i -X PUT "${FRACTALOPS_BASE_URL}/v1/connectors/bindings/github" \ -H "Content-Type: application/json" \ -H "X-Api-Key: ${FRACTALOPS_API_TOKEN}" \ -H "X-FractalOps-Tenant-Id: default" \ --data '{"tenant_id":"default","base_url":"'"${FRACTALOPS_GITHUB_API_BASE_URL}"'","enabled":true,"metadata":{"org":"example-org"}}'
curl -s "${FRACTALOPS_BASE_URL}/v1/connectors/bindings" \ -H "X-Api-Key: ${FRACTALOPS_API_TOKEN}" \ -H "X-FractalOps-Tenant-Id: default"커넥터 capability 영속 검증(카탈로그 동기화 + 수동 덮어쓰기):
curl -i -X POST "${FRACTALOPS_BASE_URL}/v1/connectors/capabilities/stored/sync-from-catalog" \ -H "X-Api-Key: ${FRACTALOPS_API_TOKEN}" \ -H "X-FractalOps-Tenant-Id: default"
curl -i -X PUT "${FRACTALOPS_BASE_URL}/v1/connectors/capabilities/stored/github" \ -H "Content-Type: application/json" \ -H "X-Api-Key: ${FRACTALOPS_API_TOKEN}" \ -H "X-FractalOps-Tenant-Id: default" \ --data '{"tenant_id":"default","enabled":true,"source":"manual","capability":{"connector_id":"github","targets":["project"],"role_mapping":{"project":{"maintainer":"admin"}},"role_priority":{"project":["maintainer"]},"action_mapping":{"grant":"grant_role"},"required_metadata":["resource_name"],"missing_metadata_action":"skip"}}'
curl -s "${FRACTALOPS_BASE_URL}/v1/connectors/capabilities/stored" \ -H "X-Api-Key: ${FRACTALOPS_API_TOKEN}" \ -H "X-FractalOps-Tenant-Id: default"동기화 체크포인트 영속 검증(델타/폴링 커서):
CONNECTOR_ID="github"REPOSITORY_FULL_NAME="example-org/agaya"CHECKPOINT_SCOPE="repository_dispatch:${REPOSITORY_FULL_NAME}"
curl -i -X PUT "${FRACTALOPS_BASE_URL}/v1/sync/checkpoints" \ -H "Content-Type: application/json" \ -H "X-Api-Key: ${FRACTALOPS_API_TOKEN}" \ -H "X-FractalOps-Tenant-Id: default" \ --data "{\"tenant_id\":\"default\",\"connector_id\":\"${CONNECTOR_ID}\",\"scope\":\"${CHECKPOINT_SCOPE}\",\"cursor_value\":\"delivery-123\",\"meta\":{\"event_name\":\"repository_dispatch\"}}"
curl -s "${FRACTALOPS_BASE_URL}/v1/sync/checkpoints?connector_id=${CONNECTOR_ID}" \ -H "X-Api-Key: ${FRACTALOPS_API_TOKEN}" \ -H "X-FractalOps-Tenant-Id: default"Temporal 스케줄(최신 기능) 제어:
make temporal-schedule-syncmake temporal-schedule-triggermake temporal-schedule-delete기본은 reconcile + delta_cursor를 함께 처리한다.
delta_cursor는 FRACTALOPS_TEMPORAL_DELTA_CURSOR_SCHEDULE_ENABLED=true일 때만 생성된다.
특정 스케줄만 제어하려면:
.tools/uv/uv run python -m fractalops.contexts.job_orchestration.temporal_schedule sync --target delta_cursor.tools/uv/uv run python -m fractalops.contexts.job_orchestration.temporal_schedule trigger --target reconcile호스트에서 CT로 원격 배포:
export FRACTALOPS_TARGET_CT=131export FRACTALOPS_TARGET_PATH=/opt/fractalopsexport FRACTALOPS_RUNTIME_PATH=/opt/fractalops/currentexport FRACTALOPS_ENV_FILE_TARGET=/opt/fractalops/shared/.envmake deploy-ct배포는 release 전환 방식으로 동작한다.
- 소스 동기화:
/opt/fractalops/releases/<release-id> - 런타임 고정 경로:
/opt/fractalops/current(심링크 전환) - 공통 환경파일:
/opt/fractalops/shared/.env - 실패 시 직전 release로 자동 롤백 시도
수동 롤백:
export FRACTALOPS_TARGET_CT=131export FRACTALOPS_TARGET_PATH=/opt/fractalopsmake deploy-ct-rollback특정 release-id 롤백:
export FRACTALOPS_ROLLBACK_RELEASE_ID=20260227-115756-b11d5afmake deploy-ct-rollbackCT Temporal 작업(배포 + 서비스 확인 + 스케줄 sync/trigger + E2E)을 한 번에 재현:
export FRACTALOPS_TARGET_CT=131export FRACTALOPS_TARGET_PATH=/opt/fractalopsmake repro-ct-temporal TOPOLOGY=ops/infra/topology/lxc-pve-lab.yaml실행 E2E 최소 액션 수를 조정하려면:
export FRACTALOPS_E2E_MIN_ACTIONS=1make repro-ct-temporal재현 실행 로그는 기본적으로 ops/output/repro-ct<CTID>-temporal-<timestamp>.log에 저장된다.
로그 저장 경로를 바꾸려면:
export FRACTALOPS_REPRO_OUTPUT_DIR=$(pwd)/ops/outputmake repro-ct-temporal참고: 일부 환경에서 Temporal CLI 실행 후 PyGILState_Release 종료 버그가 발생할 수 있다.
repro-ct-temporal은 성공 메시지가 확인된 경우 이를 경고로 처리하고 다음 단계로 진행하도록 보정되어 있다.
또한 실행 시작 시 GitHub executor 토큰 누락 여부를 자동 점검해서 pending 가능성을 경고한다.
delta_cursor 요청(meta.lastModified 기반 변경분 반영 + 체크포인트 자동 기록):
curl -X POST "${FRACTALOPS_BASE_URL}/v1/jobs/sync/access" \ -H "Content-Type: application/json" \ -H "X-Api-Key: ${FRACTALOPS_API_TOKEN}" \ --data '{ "tenant_id":"default", "mode":"delta_cursor", "payload":{ "delta":{ "scope":"groups:default", "cursor_value":"2026-02-25T00:00:00Z", "changes":[ { "principalId":"developer@example.com", "principalType":"human", "groupPath":"/org/teams/frontend/roles/team_member", "operation":"create", "entraObjectId":"123e4567-e89b-12d3-a456-426614174000", "msgraphGroupId":"223e4567-e89b-12d3-a456-426614174000" } ] } } }'완료된 잡은 /v1/sync/checkpoints/<connector_id>?scope=groups:default 에서 delta cursor를 조회할 수 있다.
자동 delta 폴링을 쓰려면 아래 값도 같이 켠다:
export FRACTALOPS_TEMPORAL_DELTA_CURSOR_SCHEDULE_ENABLED=trueexport FRACTALOPS_DELTA_CURSOR_ENABLED=trueexport FRACTALOPS_DELTA_CURSOR_SCOPE=groups:defaultexport FRACTALOPS_DELTA_CURSOR_GROUP_PATH_PREFIX=/org/external-mirror실환경 토폴로지 지정:
make deploy-ct TOPOLOGY=ops/infra/topology/lxc-pve-lab.yaml토폴로지 외 시크릿 오버레이:
export FRACTALOPS_ENV_OVERLAY_FILE=ops/infra/env/lab.secrets.envmake deploy-ct TOPOLOGY=ops/infra/topology/lxc-pve-lab.yaml기존 .env를 토폴로지 기준으로 강제 재생성:
export FRACTALOPS_FORCE_RENDER_ENV=truemake deploy-ct TOPOLOGY=ops/infra/topology/lxc-pve-lab.yamlCT Postgres 계정/DB 불일치 자동 복구:
export FRACTALOPS_BOOTSTRAP_AUTO_POSTGRES=truemake deploy-ct드리프트 검사:
make drift-checkOPA 정책 번들(버전/배포/롤백):
make policy-bundle-buildmake policy-bundle-listFRACTALOPS_OPA_URL=http://<opa-host>:8181 make policy-bundle-applyFRACTALOPS_OPA_URL=http://<opa-host>:8181 make policy-bundle-rollback# OPA 장애 시 fail-close(권장 운영): fallback 차단FRACTALOPS_POLICY_FALLBACK_MODE=closed make smoke-api정책 리포트 자동화(dry-run vs enforce 비교 / 번들 영향도):
MODE=request PAYLOAD_FILE=docs/examples/policy/runtime-request.example.json make policy-runtime-compareFRACTALOPS_POLICY_BUNDLE_ID=20260227-120000-manual-abcdef123456 make policy-bundle-impactFRACTALOPS_POLICY_BUNDLE_ID=20260227-120000-manual-abcdef123456 \FRACTALOPS_POLICY_PROBES_FILE=docs/examples/policy/probes.example.json \make policy-bundle-impactJSON 아티팩트 출력:
.tools/uv/uv run python ops/lxc/drift_check.py ops/infra/topology/lxc-pve-lab.yaml \ --summary-file build/drift-report.txt \ --json-out build/drift-report.jsonIP만 임시 예외 처리:
.tools/uv/uv run python ops/lxc/drift_check.py ops/infra/topology/lxc-pve-lab.yaml --ignore-network드리프트 자동 보정:
export FRACTALOPS_TARGET_CT=131make drift-remediateCT 내부 자동 복구 타이머 설치:
export FRACTALOPS_TARGET_CT=131export FRACTALOPS_TARGET_PATH=/opt/fractalopsmake self-heal-installAPI/워크플로 스펙 자동생성:
make spec-export7. 운영 규칙 (LOC/구조)
Section titled “7. 운영 규칙 (LOC/구조)”현재 팀 규칙:
- 기능 수정 7건당 LOC 절감 리팩터 3건 수행
- 신규 커넥터는 우선 선언형(
connector-catalog.json)으로 추가 - 특수 커넥터(GitHub, Proxmox)만 전용 executor 허용
- 레거시 fallback 키(
IAM_SYNC_*) 금지 - 테스트는
patch/mock대신 DI 기반 테스트 더블 또는 실제 통합 경로를 우선 사용 - 커넥터 실행 예외는 반드시 격리:
- 액션 실패는 해당 액션만
failed처리 - 커넥터 배치 실패도 다른 커넥터 실행을 차단하지 않음
- 액션 실패는 해당 액션만
7.1 OPA 권한 모델(솔루션 공통화)
Section titled “7.1 OPA 권한 모델(솔루션 공통화)”FractalOps는 IAM 그룹을 다음 canonical 온톨로지로 고정한다.
- SSOT:
backend/src/fractalops/contexts/access/resources/rbac-v1.json - OPA 정책:
backend/src/fractalops/contexts/access/resources/sync.rego - 인원 프로필 SSOT:
- 기본:
backend/src/fractalops/contexts/access/resources/principal-profiles-v1.json - 운영 오버라이드:
FRACTALOPS_PRINCIPAL_PROFILES_FILE=/path/to/principal-profiles-v1.json
- 기본:
코어 경로:
/org/platform/roles/<admin|super_admin|dev_super_admin|security_admin|policy_admin>/org/platform/personas/<persona>/org/departments/<department>/roles/<super_admin|team_lead|member>/org/domains/<domain>/roles/<admin|member>/org/teams/<team>/roles/<maintainer|member>/org/projects/<project>/roles/<owner|maintainer|contributor|viewer>/org/solutions/<solution>/roles/<admin|owner|user|member|operator|viewer|reader>
Role/Claim 매핑 원칙:
- OPA는
roles+claims를 함께 반환한다. roles: 실행 권한(커넥터 액션 생성 기준)claims: 세션 분기/감사/다운스트림 정책 힌트(예:claim_scope_domain,claim_access_ultra_admin)- PM/개발자 페르소나 충돌 같은 정책은
conflicts로 명시 차단한다. engineering부서team_lead는 개발 페르소나를 자동 포함하고, 타 부서team_lead는 개발 페르소나를 자동 부여하지 않는다.
비정규 경로 처리:
/org/services/...는 입력 시/org/solutions/...로 정규화한다./fractalops-delta-*,/persona_developer,/pve_admin같은 구형 루트 그룹은 정책에서 직접 권한 부여를 금지한다.
커넥터 매핑:
- 커넥터별 최종 권한은
connector-catalog.json의 role/action 매핑으로 결정한다. resource_type=solution은 커넥터 내부에서service매핑으로 자동 접는다(중복 매핑 코드 제거).- 대상 솔루션에 대응 필드가 없으면 해당 필드/권한은 비미러링이 정상이다.
- 예: 대상이 팀 개념 미지원이면 team scope 액션은 생성되지 않는다.
- 이 경우 동기화 실패가 아니라 “지원 스코프 없음”으로 해석한다.
인원 고정 프로필 적용(요청 기반 운영 기본값):
- 김종대: 전 스택 슈퍼 어드민
- 김진오, 이승호: 개발부서 슈퍼 어드민
- 김하성: 개발부서 팀장
- 김인지: PM(비개발 페르소나)
적용 커맨드:
make infra-apply-principal-profiles BASE_URL=https://id.example.com안전 적용(특정 주체만, 비파괴):
PRINCIPAL_KEYS=battlecruser_ultra_admin NO_RECONCILE_MANAGED=1 \make infra-apply-principal-profiles BASE_URL=https://id.example.com그룹 자동 생성이 필요할 때만(기본 비활성):
CREATE_MISSING_GROUPS=1 make infra-apply-principal-profiles BASE_URL=https://id.example.com드라이런:
make infra-apply-principal-profiles BASE_URL=https://id.example.com DRY_RUN=1외부 릴레이 금지 네임스페이스:
FRACTALOPS_EXTERNAL_RELAY_BLOCKED_GROUP_PREFIXES=/org/projects(기본)- 의미:
/org/projects/*는 FractalOps 내부 권한 그래프에서만 해석하고, 외부 IdP/솔루션으로 직접 릴레이하지 않는다.
7.2 OpenBao/ESO 시크릿 전달
Section titled “7.2 OpenBao/ESO 시크릿 전달”FractalOps는 OpenBao를 secret SSOT로 사용하고, k3s 런타임 전달은 ESO(External Secrets Operator)로 수행한다.
핵심 자산:
platform/k8s/bootstrap_secret_delivery.shplatform/k8s/lib/openbao_env.shplatform/k8s/apps/*/external-secret*.yaml
운영 규칙:
- 민감값은 env 평문이나 manifest 상수로 두지 않는다.
- connector/runtime/app scope에 OpenBao로 동기화한 뒤 ESO가 클러스터 Secret으로 투영한다.
- 회전 순서는
OpenBao 업데이트 -> reconcile/sync -> ESO 재동기화 -> workload 재검증이다.
예시:
./platform/k8s/bootstrap_secret_delivery.shmake infra-openbao-sync SCOPE=connectors/daytona KEYS="FRACTALOPS_DAYTONA_API_TOKEN"7.3 Connector/OpenBao SSOT reconcile
Section titled “7.3 Connector/OpenBao SSOT reconcile”커넥터 시크릿 관제의 canonical contract는 CLI reconcile-ssot 이다.
fractalops connectors reconcile-ssot- topology와 env를 읽어 connector scope를 OpenBao에 reconcile하고 self-ref를 정리한 뒤 audit까지 실행한다.
platform/k8s/sync_gitops_secrets_to_openbao.sh,platform/k8s/bootstrap_daytona_penpot_argocd.sh,platform/k8s/bootstrap_datahub.sh같은 쉘 스크립트는 이 계약의 wrapper다.
예시:
uv run python -m fractalops.cli connectors reconcile-ssot \ --topology ops/infra/topology/lxc-pve-lab.yaml \ --env-file .env \ --tenant-id default \ --dry-run8. 트러블슈팅
Section titled “8. 트러블슈팅”8.1 Temporal 큐잉 실패
Section titled “8.1 Temporal 큐잉 실패”- 증상:
temporalio dependency is not installed - 조치:
make install재실행
8.2 Temporal 연결 실패
Section titled “8.2 Temporal 연결 실패”FRACTALOPS_TEMPORAL_EXECUTOR_ADDRESS확인- 방화벽/라우팅 확인 (서비스 간 내부 대역 또는 내부 DNS 경로)
- namespace/task queue 오타 확인
Namespace ... is not found발생 시(CT132):
temporal operator namespace create --namespace fractalops --retention 72htemporal operator namespace list8.3 동기화 결과가 0건
Section titled “8.3 동기화 결과가 0건”- Keycloak client 권한 확인 (
view-users,query-users,view-groups,query-groups) - realm/group 경로가
/org/...정규 경로인지 확인 - RBAC 정책 파일(
rbac-v1.json) 패턴 확인
8.4 특정 커넥터만 실패
Section titled “8.4 특정 커넥터만 실패”- 증상: 한 커넥터 오류가 있어도 잡은
succeeded또는 부분 실패 결과를 포함해 완료 - 확인:
/v1/jobs/{job_id}의report.results에서connectorId별 실패만 필터링 - 조치:
- 실패 커넥터의 binding/env만 수정
request모드로 해당 target만 재실행해서 빠르게 수복
9. 다음 확장 우선순위
Section titled “9. 다음 확장 우선순위”- Connector capability를 DB로 영속화(tenant별 enable/disable)
- Temporal 스케줄(주기 reconcile) 이전
- 커넥터 E2E 테스트 자동화(드라이런/실행 모드 동등성)
10. 인사 이동/승진/강등 운영 API
Section titled “10. 인사 이동/승진/강등 운영 API”super_admin 권한에서만 호출 가능:
GET /v1/admin/principal-assignmentsPOST /v1/admin/principal-assignmentsPOST /v1/admin/principal-assignments/revokePOST /v1/admin/principal-assignments/lifecycle/apply
lifecycle/apply는 현재 활성 권한과 목표 권한을 diff 계산해:
- 신규 부여(
create) - 권한 회수(
delete) - 직급/타입 변경 시
revoke + assign
를 자동 적용하고, 즉시 access sync job enqueue까지 연결한다.
예시:
curl -X POST "$FRACTALOPS_API_URL/v1/admin/principal-assignments/lifecycle/apply" \ -H "Content-Type: application/json" \ -H "X-Api-Key: $FRACTALOPS_API_TOKEN" \ -d '{ "tenant_id": "default", "subject_key": "kimhs@example.com", "principal_type": "human", "target_group_paths": [ "/org/projects/agaya/roles/maintainer", "/org/solutions/daytona/roles/user" ], "reason_code": "promotion", "source": "hr-event" }'운영 메모(2026-03-27):
nexus.yamon.io는 edge에서allow_public_unauthenticated_access로 열고, 인증/리다이렉트는nexus-pomeriumplugin이 담당한다.- browser 진입은
Portal session login으로 이어지고, stocklogin/adduserUX는 GitOps web override로 감춘다. - machine mint 경로는
portal /v1/portal/registry/tokens/npm/admin-issueexact-prefix route로 분리한다. - Portal의
FRACTALOPS_NEXUS_URL과FRACTALOPS_REGISTRY_TOKEN_SIGNING_KEY는 Nexus runtime secret과 일치해야 한다. - 현재 public
npm publish는 auth가 아니라 upload transport(request size did not match content length)를 추가 점검 중이다.