Skip to content

FractalOps 운영 매뉴얼 (한글)

이 문서는 운영자용 미러다. 제품 법의 정본은 영어 constitution 문서에 있고, 제품 정의의 정본은 영어 canonical architecture 문서에 있다.

  • 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 는 compact Agent 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 경로:

  1. API에서 잡 생성
  2. JobServiceenqueue_job(...) 호출
  3. fractalops-job-workflow 시작
  4. 워크플로가 활동(Activity)에서 동일 도메인 작업 실행

즉, durable queue/retry/schedule은 Temporal로 고정하고 비즈니스 로직은 동일하게 유지한다.

LangGraph 경로:

  1. Studio가 agent session/runtime spec을 만든다.
  2. Temporal activity가 Studio run execute job을 호출할 수 있다.
  3. Studio가 LangGraph agent server에 thread/run을 시작한다.
  4. LangGraph는 frontier, fanout, checkpoint, HITL graph state만 관리한다.
  5. 실제 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가 아니다.

  • 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
  • FRACTALOPS_IDP_BASE_URL
  • FRACTALOPS_IDP_REALM
  • Keycloak 인증(둘 중 하나):
    • FRACTALOPS_IDP_CLIENT_ID + FRACTALOPS_IDP_CLIENT_SECRET
    • FRACTALOPS_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-mapping
    • PUT /v1/admin/policy-canvas/authority-group-mapping/from-graph
  • 저장 방식:
    • canonical YAML (yaml_text)이 SSOT
    • React Flow 그래프(graph.nodes/edges)는 YAML에서 파생/동기화
  • 포털 편집 UX:
    • /admin/authority-mapping (요청 헤더 X-FractalOps-Tenant-Id 사용)
    • super_admin 권한에서만 접근 가능
  • API 경로:
    • GET /v1/admin/policy-canvas/team-access-workbench
    • PUT /v1/admin/policy-canvas/team-access-workbench
    • PUT /v1/admin/policy-canvas/team-access-workbench/from-graph
  • 포털 편집 UX:
    • /admin/access-workbench
    • super_admin 권한에서만 접근 가능
  • 관리 대상:
    • group
    • project
    • secret ref
    • stack
  • secret 정책:
    • 원문 secret 저장 금지
    • backendopenbao | github_actions만 허용
    • secret_ref는 필수
  • Workbench 결과는 semantics -> DataHub -> Valkey projection에 포함된다.
  • FRACTALOPS_TEMPORAL_EXECUTOR_ADDRESS=<temporal-host>:7233 (정본 contract)
  • FRACTALOPS_TEMPORAL_ADDRESS=<temporal-host>:7233 (legacy fallback)
  • FRACTALOPS_TEMPORAL_NAMESPACE=fractalops
  • FRACTALOPS_TEMPORAL_TASK_QUEUE=fractalops-jobs
  • FRACTALOPS_TEMPORAL_RECONCILE_SCHEDULE_ENABLED=true
  • FRACTALOPS_TEMPORAL_RECONCILE_INTERVAL_SECONDS=3600

4.3 OpenBao(Vault 호환) 시크릿 백엔드

Section titled “4.3 OpenBao(Vault 호환) 시크릿 백엔드”
  • FRACTALOPS_SECRET_BACKEND=openbao
  • FRACTALOPS_OPENBAO_ADDR=http://<openbao-host>:8200
  • FRACTALOPS_OPENBAO_TOKEN=<token> 또는 FRACTALOPS_OPENBAO_TOKEN_FILE=<token-file-path>
  • FRACTALOPS_OPENBAO_MOUNT=kv
  • FRACTALOPS_OPENBAO_PREFIX=fractalops
  • FRACTALOPS_OPENBAO_VERIFY_TLS=true

조회 우선순위:

  1. API 요청 SSOT payload
  2. OpenBao scope (connectors, connectors/<provider>, runtime/<service>)
  3. 환경변수

민감키 강제 모드(권장):

  • FRACTALOPS_SECRET_REQUIRE_REF_FOR_SENSITIVE=true
  • 이 값을 켜면 TOKEN/SECRET/PASSWORD 계열 env 평문은 무시하고 ref: 또는 OpenBao 조회값만 사용한다.

.env 값을 OpenBao scope로 동기화:

Terminal window
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 동기화):

Terminal window
make infra-ssot-harden-env ENV_FILE=.env TENANT_ID=default

호스트/LXC/VM 잔존 민감값 점검:

Terminal window
make infra-sensitive-audit

Terminal window
make infra-validate
Terminal window
make infra-generate-env
make infra-env-check
make infra-cloudflare-plan
make infra-cloudflare-apply-dry-run
make infra-check
.tools/uv/uv run python ops/lxc/preflight.py --env-file build/.env.embedded

infra-check 규칙:

  • FRACTALOPS_DATABASE_URL이 비어 있으면 Postgres 포트 체크를 생략한다.
  • .env 값을 기본으로 읽고, 셸 환경변수가 있으면 셸 환경변수가 우선한다.

실환경 프로파일을 바꿀 때:

Terminal window
make infra-validate TOPOLOGY=ops/infra/topology/lxc-pve-lab.yaml

기본 프로파일은 ops/infra/topology/lxc-pve-lab.yaml 이다.

토폴로지 선택형 항목:

  • identity_plane.session_trust.enabled
    • false: session_trust 미사용(필수 토큰 체크 생략)
    • true + provider=pomerium: Pomerium 인증 SSOT + (선택) Cloudflare tunnel/DNS 자동화
  • runtime_dependencies.openbao
    • OpenBao CT, address, TLS 검증 여부를 topology SSOT로 고정
    • make infra-generate-envFRACTALOPS_OPENBAO_ADDR, FRACTALOPS_OPENBAO_PCT_VMID, FRACTALOPS_OPENBAO_VERIFY_TLS가 자동 생성됨

Cloudflare 토큰까지 검증하려면:

Terminal window
make infra-cloudflare-plan-verify TOPOLOGY=ops/infra/topology/lxc-pve-lab.yaml

Cloudflare 실반영은 명시적으로만 수행:

Terminal window
# 기본은 dry-run
make infra-cloudflare-apply-dry-run TOPOLOGY=ops/infra/topology/lxc-pve-lab.yaml
# 실제 write
make infra-cloudflare-apply TOPOLOGY=ops/infra/topology/lxc-pve-lab.yaml

SSOT 하드코딩 후보 점검:

Terminal window
make infra-ssot-scan
make infra-ssot-scan-strict

솔루션 Operation Fabric:

Terminal window
make operation-list
make operation-audit
make operation-run OP_ID=configure_github_idp DRY_RUN=1
make operation-run OP_ID=configure_github_idp CONFIRM_UPDATE=1 SSOT_JSON_FILE=./ssot/github-idp.json

Nexus 인증 팩(Pomerium brokered registry):

Terminal window
# 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 E2E
FRACTALOPS_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=200
  • nexus-publish-sample 결과 publishStatus=201, metadataStatus=200, tarballStatus=200

실행 결과 파일(실행 호스트):

  • /root/output/nexus_publish/*.json

Nexus publish 검증(서비스 토큰 env 파일 사용, provider-agnostic):

Terminal window
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_HEADER
  • FRACTALOPS_SERVICE_TOKEN_CLIENT_ID 또는 FRACTALOPS_SERVICE_TOKEN_CLIENT_IDS

개발자 CLI 사용법(gh auth 유사):

Terminal window
# 사용자명 힌트 생성(이메일 전체 대신 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도 실패하지 않음
Terminal window
cp .env.example .env
make uv-bootstrap
make install
make migrate
make api
make temporal-worker

Alembic 리비전 생성은 수동 편집 대신 코드젠(autogenerate) 사용:

Terminal window
make db-revision MSG="add audit events"
make db-revision-local MSG="add audit events"
make migrate

Codegen(OpenAPI/Contracts/Portal/Connectors) 재생성 및 검증:

Terminal window
make codegen-openapi
make codegen-contract-models
make codegen-connectors
make codegen-portal-client
make codegen-check

검증 스택:

Terminal window
make test-unit
make test-contract
make test-integration

원칙:

  • test-unit은 quiet/fail-only unittest_runner를 사용한다
  • test-contract는 curated Schemathesis contract suite다
  • test-integration은 shared runtime smoke만 사용한다
  • shared service 5xx는 local Docker fallback이 아니라 skip + report로 처리한다

msgraphrelay 전략일 때 명령 템플릿을 먼저 지정:

Terminal window
export FRACTALOPS_RELAY_CMD_TEMPLATE='relay-compiler --src {output} --schema {spec}'

msgraph 커넥터 readiness를 ready로 올리려면:

Terminal window
export FRACTALOPS_MSGRAPH_TENANT_ID=...
export FRACTALOPS_MSGRAPH_CLIENT_ID=...
export FRACTALOPS_MSGRAPH_CLIENT_SECRET=...

  1. GET /healthz 200
  2. GET /v1/connectors/capabilities 200
  3. POST /v1/jobs/sync/access 큐잉 성공
  4. GET /v1/jobs/{job_id} 상태 전이 (queued -> running -> succeeded|failed)
  5. make test-unit 통과
  6. make test-contract 통과
  7. make test-integration 통과 또는 shared outage 기준 skip + report
  8. make codegen-check 통과
  9. POST /v1/events/webhook/{provider} 시그니처 검증 + 이벤트 처리 (github 지원, /v1/events/github 호환 유지)
    • X-GitHub-Event 헤더 필수(헤더 누락 시 400)
  10. PUT/GET/DELETE /v1/connectors/bindings/* 정상 동작
  11. POST/PUT/GET/DELETE /v1/connectors/capabilities/stored/* 정상 동작
  12. PUT/GET/DELETE /v1/sync/checkpoints* 정상 동작
  13. POST/GET /v1/audit/events* 정상 동작
  14. GET /v1/wizard/profiles + POST /v1/wizard/try 정상 동작
  15. POST /v1/connectors/preflight/{connector_id} 정상 동작

SCIM 토큰을 켠 경우:

Terminal window
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:Error
  • detail.scimTypeinvalidToken | invalidSyntax | invalidValue | noTarget | uniqueness 중 하나

그룹 조회/필터:

Terminal window
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)로 보정
Terminal window
cp .env.example .env
make uv-bootstrap
make install
make unit-install
systemctl daemon-reload
systemctl enable --now fractalops-api fractalops-temporal-worker

원클릭 실행:

Terminal window
make bootstrap-lxc

현재 canonical runtime:

  • fractalops-api, fractalops-worker, fractalops-portalk8s/Argo 배포를 기본으로 본다.
  • systemd/LXC 경로는 복구, 재현, 레거시 점검용으로만 유지한다.

Temporal 잡 경로 스모크:

Terminal window
make smoke-temporal

커넥터 executor 준비상태 확인:

Terminal window
make check-readiness

PVE OIDC 사용자 로그인/권한 점검(비밀번호 티켓 검증 제외):

Terminal window
FRACTALOPS_E2E_PVE_USERID=user@example.com@keycloak \
make check-pve-oidc-access

원칙:

  • openid realm 사용자는 /access/ticket 비밀번호 검증을 사용하지 않는다.
  • 검증 경로는 Cloudflare Access → Microsoft → PVE OpenID redirect 기준으로 본다.

배포 성공 게이트(health/readiness/systemd):

Terminal window
make deploy-gate

호스트에서 systemd 검사 없이 점검하려면:

Terminal window
FRACTALOPS_GATE_SKIP_SYSTEMD=true make deploy-gate

PVE OIDC 사용자까지 게이트에 포함하려면:

Terminal window
FRACTALOPS_E2E_PVE_USERID=user@example.com@keycloak make deploy-gate

커넥터 실운영 검증 스위트(성공/실패/재시도):

Terminal window
make connector-suite

빠른 로컬 검증(권장):

Terminal window
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:

Terminal window
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 템플릿 조회:

Terminal window
curl -s -X GET "${FRACTALOPS_BASE_URL}/v1/wizard/template/msgraph" \
-H "X-Api-Key: ${FRACTALOPS_API_TOKEN}"

wizard binding upsert(SSOT) 실행:

Terminal window
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 단축 실행:

Terminal window
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=forceupdated
  • 동일 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):

Terminal window
FRACTALOPS_REQUIRED_CONNECTORS=github,pve ./.tools/uv/uv run python ops/lxc/check_connector_readiness.py --fail-on-unready

잡 큐잉 단계에서 미준비 커넥터를 차단하려면:

Terminal window
export FRACTALOPS_REQUIRED_CONNECTORS=github,pve
export FRACTALOPS_FAIL_ON_UNREADY=true
export 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건):

Terminal window
make e2e-request

실구동 reconcile 검증(실제 큐잉/실행/완료):

Terminal window
make live-reconcile

GitHub 웹훅 인바운드 검증(서명 검증 포함):

Terminal window
export FRACTALOPS_GITHUB_WEBHOOK_SECRET='replace-me'
body='{"zen":"keep it logically awesome","hook_id":1}'
sig=$(python - <<'PY'
import hmac, hashlib
secret='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별):

Terminal window
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 영속 검증(카탈로그 동기화 + 수동 덮어쓰기):

Terminal window
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"

동기화 체크포인트 영속 검증(델타/폴링 커서):

Terminal window
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 스케줄(최신 기능) 제어:

Terminal window
make temporal-schedule-sync
make temporal-schedule-trigger
make temporal-schedule-delete

기본은 reconcile + delta_cursor를 함께 처리한다. delta_cursorFRACTALOPS_TEMPORAL_DELTA_CURSOR_SCHEDULE_ENABLED=true일 때만 생성된다. 특정 스케줄만 제어하려면:

Terminal window
.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로 원격 배포:

Terminal window
export FRACTALOPS_TARGET_CT=131
export FRACTALOPS_TARGET_PATH=/opt/fractalops
export FRACTALOPS_RUNTIME_PATH=/opt/fractalops/current
export FRACTALOPS_ENV_FILE_TARGET=/opt/fractalops/shared/.env
make deploy-ct

배포는 release 전환 방식으로 동작한다.

  • 소스 동기화: /opt/fractalops/releases/<release-id>
  • 런타임 고정 경로: /opt/fractalops/current (심링크 전환)
  • 공통 환경파일: /opt/fractalops/shared/.env
  • 실패 시 직전 release로 자동 롤백 시도

수동 롤백:

Terminal window
export FRACTALOPS_TARGET_CT=131
export FRACTALOPS_TARGET_PATH=/opt/fractalops
make deploy-ct-rollback

특정 release-id 롤백:

Terminal window
export FRACTALOPS_ROLLBACK_RELEASE_ID=20260227-115756-b11d5af
make deploy-ct-rollback

CT Temporal 작업(배포 + 서비스 확인 + 스케줄 sync/trigger + E2E)을 한 번에 재현:

Terminal window
export FRACTALOPS_TARGET_CT=131
export FRACTALOPS_TARGET_PATH=/opt/fractalops
make repro-ct-temporal TOPOLOGY=ops/infra/topology/lxc-pve-lab.yaml

실행 E2E 최소 액션 수를 조정하려면:

Terminal window
export FRACTALOPS_E2E_MIN_ACTIONS=1
make repro-ct-temporal

재현 실행 로그는 기본적으로 ops/output/repro-ct<CTID>-temporal-<timestamp>.log에 저장된다. 로그 저장 경로를 바꾸려면:

Terminal window
export FRACTALOPS_REPRO_OUTPUT_DIR=$(pwd)/ops/output
make repro-ct-temporal

참고: 일부 환경에서 Temporal CLI 실행 후 PyGILState_Release 종료 버그가 발생할 수 있다. repro-ct-temporal은 성공 메시지가 확인된 경우 이를 경고로 처리하고 다음 단계로 진행하도록 보정되어 있다. 또한 실행 시작 시 GitHub executor 토큰 누락 여부를 자동 점검해서 pending 가능성을 경고한다.

delta_cursor 요청(meta.lastModified 기반 변경분 반영 + 체크포인트 자동 기록):

Terminal window
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 폴링을 쓰려면 아래 값도 같이 켠다:

Terminal window
export FRACTALOPS_TEMPORAL_DELTA_CURSOR_SCHEDULE_ENABLED=true
export FRACTALOPS_DELTA_CURSOR_ENABLED=true
export FRACTALOPS_DELTA_CURSOR_SCOPE=groups:default
export FRACTALOPS_DELTA_CURSOR_GROUP_PATH_PREFIX=/org/external-mirror

실환경 토폴로지 지정:

Terminal window
make deploy-ct TOPOLOGY=ops/infra/topology/lxc-pve-lab.yaml

토폴로지 외 시크릿 오버레이:

Terminal window
export FRACTALOPS_ENV_OVERLAY_FILE=ops/infra/env/lab.secrets.env
make deploy-ct TOPOLOGY=ops/infra/topology/lxc-pve-lab.yaml

기존 .env를 토폴로지 기준으로 강제 재생성:

Terminal window
export FRACTALOPS_FORCE_RENDER_ENV=true
make deploy-ct TOPOLOGY=ops/infra/topology/lxc-pve-lab.yaml

CT Postgres 계정/DB 불일치 자동 복구:

Terminal window
export FRACTALOPS_BOOTSTRAP_AUTO_POSTGRES=true
make deploy-ct

드리프트 검사:

Terminal window
make drift-check

OPA 정책 번들(버전/배포/롤백):

Terminal window
make policy-bundle-build
make policy-bundle-list
FRACTALOPS_OPA_URL=http://<opa-host>:8181 make policy-bundle-apply
FRACTALOPS_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 비교 / 번들 영향도):

Terminal window
MODE=request PAYLOAD_FILE=docs/examples/policy/runtime-request.example.json make policy-runtime-compare
FRACTALOPS_POLICY_BUNDLE_ID=20260227-120000-manual-abcdef123456 make policy-bundle-impact
FRACTALOPS_POLICY_BUNDLE_ID=20260227-120000-manual-abcdef123456 \
FRACTALOPS_POLICY_PROBES_FILE=docs/examples/policy/probes.example.json \
make policy-bundle-impact

JSON 아티팩트 출력:

Terminal window
.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.json

IP만 임시 예외 처리:

Terminal window
.tools/uv/uv run python ops/lxc/drift_check.py ops/infra/topology/lxc-pve-lab.yaml --ignore-network

드리프트 자동 보정:

Terminal window
export FRACTALOPS_TARGET_CT=131
make drift-remediate

CT 내부 자동 복구 타이머 설치:

Terminal window
export FRACTALOPS_TARGET_CT=131
export FRACTALOPS_TARGET_PATH=/opt/fractalops
make self-heal-install

API/워크플로 스펙 자동생성:

Terminal window
make spec-export

현재 팀 규칙:

  • 기능 수정 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(비개발 페르소나)

적용 커맨드:

Terminal window
make infra-apply-principal-profiles BASE_URL=https://id.example.com

안전 적용(특정 주체만, 비파괴):

Terminal window
PRINCIPAL_KEYS=battlecruser_ultra_admin NO_RECONCILE_MANAGED=1 \
make infra-apply-principal-profiles BASE_URL=https://id.example.com

그룹 자동 생성이 필요할 때만(기본 비활성):

Terminal window
CREATE_MISSING_GROUPS=1 make infra-apply-principal-profiles BASE_URL=https://id.example.com

드라이런:

Terminal window
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/솔루션으로 직접 릴레이하지 않는다.

FractalOps는 OpenBao를 secret SSOT로 사용하고, k3s 런타임 전달은 ESO(External Secrets Operator)로 수행한다.

핵심 자산:

  • platform/k8s/bootstrap_secret_delivery.sh
  • platform/k8s/lib/openbao_env.sh
  • platform/k8s/apps/*/external-secret*.yaml

운영 규칙:

  • 민감값은 env 평문이나 manifest 상수로 두지 않는다.
  • connector/runtime/app scope에 OpenBao로 동기화한 뒤 ESO가 클러스터 Secret으로 투영한다.
  • 회전 순서는 OpenBao 업데이트 -> reconcile/sync -> ESO 재동기화 -> workload 재검증 이다.

예시:

Terminal window
./platform/k8s/bootstrap_secret_delivery.sh
make infra-openbao-sync SCOPE=connectors/daytona KEYS="FRACTALOPS_DAYTONA_API_TOKEN"

커넥터 시크릿 관제의 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다.

예시:

Terminal window
uv run python -m fractalops.cli connectors reconcile-ssot \
--topology ops/infra/topology/lxc-pve-lab.yaml \
--env-file .env \
--tenant-id default \
--dry-run

  • 증상: temporalio dependency is not installed
  • 조치: make install 재실행
  • FRACTALOPS_TEMPORAL_EXECUTOR_ADDRESS 확인
  • 방화벽/라우팅 확인 (서비스 간 내부 대역 또는 내부 DNS 경로)
  • namespace/task queue 오타 확인
  • Namespace ... is not found 발생 시(CT132):
Terminal window
temporal operator namespace create --namespace fractalops --retention 72h
temporal operator namespace list
  • Keycloak client 권한 확인 (view-users, query-users, view-groups, query-groups)
  • realm/group 경로가 /org/... 정규 경로인지 확인
  • RBAC 정책 파일(rbac-v1.json) 패턴 확인
  • 증상: 한 커넥터 오류가 있어도 잡은 succeeded 또는 부분 실패 결과를 포함해 완료
  • 확인: /v1/jobs/{job_id}report.results에서 connectorId별 실패만 필터링
  • 조치:
    • 실패 커넥터의 binding/env만 수정
    • request 모드로 해당 target만 재실행해서 빠르게 수복

  1. Connector capability를 DB로 영속화(tenant별 enable/disable)
  2. Temporal 스케줄(주기 reconcile) 이전
  3. 커넥터 E2E 테스트 자동화(드라이런/실행 모드 동등성)

10. 인사 이동/승진/강등 운영 API

Section titled “10. 인사 이동/승진/강등 운영 API”

super_admin 권한에서만 호출 가능:

  • GET /v1/admin/principal-assignments
  • POST /v1/admin/principal-assignments
  • POST /v1/admin/principal-assignments/revoke
  • POST /v1/admin/principal-assignments/lifecycle/apply

lifecycle/apply는 현재 활성 권한과 목표 권한을 diff 계산해:

  1. 신규 부여(create)
  2. 권한 회수(delete)
  3. 직급/타입 변경 시 revoke + assign

를 자동 적용하고, 즉시 access sync job enqueue까지 연결한다.

예시:

Terminal window
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-pomerium plugin이 담당한다.
  • browser 진입은 Portal session login으로 이어지고, stock login/adduser UX는 GitOps web override로 감춘다.
  • machine mint 경로는 portal /v1/portal/registry/tokens/npm/admin-issue exact-prefix route로 분리한다.
  • Portal의 FRACTALOPS_NEXUS_URLFRACTALOPS_REGISTRY_TOKEN_SIGNING_KEY는 Nexus runtime secret과 일치해야 한다.
  • 현재 public npm publish는 auth가 아니라 upload transport(request size did not match content length)를 추가 점검 중이다.