Skip to content

fix(realm): reconcile application RPs at runtime - #83

Draft
seonghobae wants to merge 2 commits into
mainfrom
codex/remove-portable-runtime-rps
Draft

fix(realm): reconcile application RPs at runtime#83
seonghobae wants to merge 2 commits into
mainfrom
codex/remove-portable-runtime-rps

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Buyer-visible defect

The portable Keycloak realm still embedded ecosystem-rp-template and naruon-web after #72 made application relying parties runtime desired state. A clean import could therefore recreate application clients outside Keyverse reconciliation and split ownership between realm source and durable desired state.

Closes #71
Tracks #2

Test-first repair

The focused RED contract failed because both application clients remained in realm-cwl.json and the validator did not reject them. This change now:

  • keeps only account-unification-svc in the portable realm;
  • preserves the portable basic, profile, and email scopes;
  • rejects known or unknown application clients in the realm validator;
  • proves the rendered oidc-rp-naruon.json profile recreates one exact in-sync client from an empty application-client state through Keyverse desired-state reconciliation;
  • records startup order: service bootstrap, RP reconciliation, confidential secret placement where applicable, then controlled login acceptance;
  • updates canonical architecture/operability/onboarding/doctoring/passwordless/deployment docs and CHANGELOG.

Exact-head evidence

Current head: 30eeadecad5c453c032a08fe503591712817a765 on protected base 214afb444e64038906a03556e411583b2f58ee71.

  • focused realm/template/reconciliation suite: 26 passed;
  • complete account-unification suite: 743 passed;
  • production coverage: 2,734 statements / 738 branches, exact 100%;
  • Ruff: passed;
  • Interrogate docstrings: 100%;
  • Python compilation: passed;
  • realm validator and JSON parser: passed;
  • root documentation contract: 5 passed;
  • sdist and wheel build: passed;
  • git diff --check: passed.

The first full local run inherited an ambient SOCKS proxy and failed 13 HTTPX transport tests because socksio is intentionally not a project dependency. Re-running the same full boundary in the CI-equivalent proxy-free environment passed; no dependency or product gate was weakened. Docker Compose is not installed in this execution environment, so hosted CI remains authoritative for Compose/template validation.

Safety and completion boundary

No secret, provider credential, password authenticator, dependency, database schema, admin bypass, release, or review credential changes. This remains Draft until exact-head CI, CodeQL, Semgrep, Security Scan, independent review, zero unresolved findings, and protected merge requirements are satisfied.

Summary by CodeRabbit

  • 변경 사항

    • 휴대용 Keycloak realm에서 애플리케이션 RP를 제거하고, 제어 영역 서비스 클라이언트만 포함하도록 변경했습니다.
    • 애플리케이션 RP는 런타임 desired state를 통해 생성·관리되도록 절차를 명확히 했습니다.
    • 휴대용 realm에 애플리케이션 클라이언트가 포함될 경우 검증에서 거부합니다.
  • 문서

    • RP 온보딩, 복구, 인증 흐름 및 권한 경계를 최신 운영 방식에 맞게 갱신했습니다.
  • 테스트

    • 휴대용 realm 구성과 런타임 RP 재조정 시나리오에 대한 검증을 추가했습니다.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@seonghobae, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 44 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b548e999-371f-4030-98c0-a69eeac2a162

📥 Commits

Reviewing files that changed from the base of the PR and between 30eeade and f5bea8e.

📒 Files selected for processing (2)
  • docs/doctoring/oidc-rp-claim-mapper-profile.md
  • services/account_unification/tests/test_realm_policy.py
📝 Walkthrough

Walkthrough

포터블 realm에서 ecosystem-rp-templatenaruon-web을 제거했다. Validator는 account-unification-svc 외 클라이언트를 거부한다. Naruon RP는 런타임 desired state reconciliation으로 생성하며, 온보딩과 권한 경계 문서를 갱신했다.

Changes

포터블 Realm 정책과 검증

Layer / File(s) Summary
포터블 Realm 정책과 validator 갱신
deploy/keycloak/realm-cwl.json, scripts/validate_realm.py, services/account_unification/tests/test_realm_policy.py, deploy/keycloak/README.md
Realm에서 애플리케이션 RP를 제거했다. account-unification-svc와 기본 client scope를 유지했다. Validator는 허용되지 않은 클라이언트를 거부한다. 관련 정책 테스트를 추가하고 기존 RP 설정 테스트를 제거했다.

런타임 RP 복구와 reconciliation 검증

Layer / File(s) Summary
Naruon 런타임 RP reconciliation 검증
services/account_unification/tests/test_relying_party_mapper_reconciliation.py, deploy/keycloak/README.md
배포 realm의 기본 클라이언트를 확인한다. Naruon 템플릿으로 relying-party를 생성한다. 결과 상태가 IN_SYNC이고 영수증이 일치하며 생성된 RP 클라이언트가 하나인지 검증한다.

온보딩과 권한 경계 문서

Layer / File(s) Summary
온보딩 순서와 권한 경계 문서 갱신
deploy/templates/README.md, docs/OPERABILITY.md, docs/rp-onboarding.md, docs/adr/0008-keyverse-rp-authorization-boundary.md, docs/doctoring/oidc-rp-claim-mapper-profile.md, docs/passwordless-policy.md, README.md, CHANGELOG.md
포터블 realm의 애플리케이션 RP 제외 정책을 문서화했다. 서비스 부트스트랩, RP reconciliation, confidential secret 배치, 통제된 로그인 검증 순서를 추가했다. 런타임 claim과 권한 검증 범위를 갱신했다.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Operator
  participant Keycloak
  participant AccountUnification
  participant DesiredState
  participant NaruonRP

  Operator->>Keycloak: 포터블 realm import
  Keycloak->>AccountUnification: account-unification-svc 부트스트랩
  DesiredState->>AccountUnification: 런타임 RP desired state 제공
  AccountUnification->>NaruonRP: Naruon RP reconciliation 실행
  NaruonRP-->>Keycloak: relying-party 클라이언트 생성 또는 동기화
  Operator->>Keycloak: 통제된 로그인 검증
Loading

Possibly related issues

  • ContextualWisdomLab/keyverse issue 70: 애플리케이션 RP를 포터블 realm에서 제거하고 런타임 desired-state reconciliation으로 관리하는 변경과 직접 연결됩니다.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 애플리케이션 RP를 런타임에서 조정하는 주요 변경을 정확하고 간결하게 설명합니다.
Linked Issues check ✅ Passed 변경 사항은 #71의 애플리케이션 RP 제거, 검증 강화, 복구 테스트, 문서 갱신 요구를 충족합니다.
Out of Scope Changes check ✅ Passed 변경 사항은 #71의 portable realm 소유권과 런타임 RP 조정 범위에 직접 관련됩니다.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/remove-portable-runtime-rps

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@OpenCode review

Please review exact head 30eeadecad5c453c032a08fe503591712817a765. Focus on portable-realm ownership, fail-closed rejection of application clients, clean-realm desired-state recovery, startup/rollback ordering, and stale documentation claims. Predecessor-head evidence must not transfer.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

@seonghobae I will review pull request #83 at exact head 30eeadecad5c453c032a08fe503591712817a765. I will assess only evidence from this head and the requested focus areas.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
services/account_unification/tests/test_realm_policy.py (1)

87-97: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

알 수 없는 애플리케이션 클라이언트의 거부 경로를 테스트하십시오.

현재 테스트는 ecosystem-rp-templatenaruon-web만 확인합니다. elif client_id != "account-unification-svc" 경로는 검증하지 않습니다. 이 경로가 회귀하면 알려진 두 클라이언트의 전용 오류는 계속 발생하므로 테스트가 통과할 수 있습니다. 알 수 없는 clientId와 일반 거부 오류를 추가로 확인하십시오.

수정 예시
         [
             {"clientId": "ecosystem-rp-template"},
             {"clientId": "naruon-web", "publicClient": True},
+            {"clientId": "unmanaged-web", "publicClient": True},
         ]
     )
 
     errors = validator.validate(realm)
 
     assert any("runtime application client 'ecosystem-rp-template'" in error for error in errors)
     assert any("runtime application client 'naruon-web'" in error for error in errors)
+    assert any(
+        "portable realm may contain only the account-unification-svc" in error
+        for error in errors
+    )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@services/account_unification/tests/test_realm_policy.py` around lines 87 -
97, Extend the test around validator.validate(realm) to include an unknown
clientId and assert that validation returns the general application-client
rejection error for it. Keep the existing dedicated assertions for
ecosystem-rp-template and naruon-web, ensuring the unknown client exercises the
elif client_id != "account-unification-svc" branch.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/doctoring/oidc-rp-claim-mapper-profile.md`:
- Around line 118-120: Update the portable-realm statement in the OIDC RP
claim-mapper profile to explicitly preserve the account-unification-svc
control-plane client while stating that only runtime application RP clients are
removed; retain the existing deterministic clean-realm and Keyverse
synchronization claims.

---

Nitpick comments:
In `@services/account_unification/tests/test_realm_policy.py`:
- Around line 87-97: Extend the test around validator.validate(realm) to include
an unknown clientId and assert that validation returns the general
application-client rejection error for it. Keep the existing dedicated
assertions for ecosystem-rp-template and naruon-web, ensuring the unknown client
exercises the elif client_id != "account-unification-svc" branch.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6093b08a-425f-4fe1-a41c-d648c97fe548

📥 Commits

Reviewing files that changed from the base of the PR and between 214afb4 and 30eeade.

📒 Files selected for processing (13)
  • CHANGELOG.md
  • README.md
  • deploy/keycloak/README.md
  • deploy/keycloak/realm-cwl.json
  • deploy/templates/README.md
  • docs/OPERABILITY.md
  • docs/adr/0008-keyverse-rp-authorization-boundary.md
  • docs/doctoring/oidc-rp-claim-mapper-profile.md
  • docs/passwordless-policy.md
  • docs/rp-onboarding.md
  • scripts/validate_realm.py
  • services/account_unification/tests/test_realm_policy.py
  • services/account_unification/tests/test_relying_party_mapper_reconciliation.py
💤 Files with no reviewable changes (1)
  • deploy/keycloak/realm-cwl.json

Comment thread docs/doctoring/oidc-rp-claim-mapper-profile.md Outdated

Copy link
Copy Markdown
Contributor Author

Addressed the exact-head review on f5bea8e7b93a9058f2204607522f9e9f01d8a621:

  • added an unmanaged application client to the portable-realm validator regression and asserted the generic fail-closed rejection path;
  • clarified that the portable realm preserves account-unification-svc while removing runtime application RP clients.

Focused realm and relying-party mapper verification: 46 passed; Ruff clean; diff check clean. Please re-review this current head only.

@coderabbitai review
@OpenCode review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Checkout =='
git rev-parse HEAD
git status --short
git show -s --format='%H%n%P%n%s' HEAD

echo '== PR metadata =='
gh pr view 83 --repo ContextualWisdomLab/keyverse --json baseRefOid,headRefOid,files \
  --jq '{baseRefOid, headRefOid, files: [.files[].path]}'

echo '== Diff summary from PR base =='
base="$(gh pr view 83 --repo ContextualWisdomLab/keyverse --json baseRefOid --jq .baseRefOid)"
git diff --stat "$base...HEAD"
git diff --name-only "$base...HEAD"

echo '== Validator outline and relevant content =='
ast-grep outline scripts/validate_realm.py --items all || true
rg -n -C 5 'account-unification-svc|application|client|forbidden|reject|allowed' scripts/validate_realm.py

echo '== Realm client and scope declarations =='
rg -n -C 4 '"clientId"|"clientScopes"|"basic"|"profile"|"email"|naruon|ecosystem' deploy/keycloak/realm-cwl.json

echo '== Changed test outlines and relevant assertions =='
ast-grep outline services/account_unification/tests/test_realm_policy.py --items all || true
ast-grep outline services/account_unification/tests/test_relying_party_mapper_reconciliation.py --items all || true
rg -n -C 6 'portable|unmanaged|application.client|application client|naruon|IN_SYNC|realm-cwl' \
  services/account_unification/tests/test_realm_policy.py \
  services/account_unification/tests/test_relying_party_mapper_reconciliation.py

echo '== Changed documentation references =='
git diff --unified=30 "$base...HEAD" -- \
  CHANGELOG.md README.md deploy/keycloak/README.md deploy/templates/README.md \
  docs/OPERABILITY.md docs/adr/0008-keyverse-rp-authorization-boundary.md \
  docs/doctoring/oidc-rp-claim-mapper-profile.md docs/passwordless-policy.md \
  docs/rp-onboarding.md

Length of output: 72925


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Full validator policy section =='
sed -n '60,175p' scripts/validate_realm.py

echo '== Exact updated tests =='
sed -n '1,115p' services/account_unification/tests/test_realm_policy.py
sed -n '1,95p' services/account_unification/tests/test_relying_party_mapper_reconciliation.py

echo '== Test fixture definitions and in-memory client initialization =='
rg -n -C 8 --type py \
  'def api\b|`@pytest`\.fixture|class .*Api|relying_party_clients|InMemory.*Api|Fake.*Api' \
  services/account_unification/tests services/account_unification/app

echo '== Reconciliation entry points =='
rg -n -C 8 --type py \
  'def put_registration\b|def reconcile_all\b|def get_registration\b|class RelyingPartyService' \
  services/account_unification

echo '== Rollback and startup instructions =='
rg -n -i -C 5 \
  'clean realm|realm import|bootstrap|reconcile|rollback|roll back|remote-first|route traffic' \
  docs deploy/keycloak README.md CHANGELOG.md

echo '== Potential stale portable-realm ownership statements =='
rg -n -i -C 3 \
  'portable realm.*(naruon|application|RP|client)|realm.*(naruon-web|ecosystem-rp-template)|embedded.*(RP|client|naruon)|concrete Naruon' \
  --glob '!CHANGELOG.md' \
  .

echo '== Portable realm JSON structural checks without executing repository code =='
python3 - <<'PY'
import json
from pathlib import Path

realm = json.loads(Path("deploy/keycloak/realm-cwl.json").read_text(encoding="utf-8"))
clients = realm.get("clients")
scopes = realm.get("clientScopes")
print("clients_type:", type(clients).__name__)
print("client_ids:", [item.get("clientId") if isinstance(item, dict) else type(item).__name__ for item in clients])
print("default_scopes:", realm.get("defaultDefaultClientScopes"))
print("declared_scopes:", [item.get("name") if isinstance(item, dict) else type(item).__name__ for item in scopes])
PY

Length of output: 228560


@seonghobae I reviewed only f5bea8e7b93a9058f2204607522f9e9f01d8a621.

I found one documentation issue.

  • docs/TRD.md, Line 20: The statement that the portable realm “may contain public client … definitions” is now incorrect. It permits public application clients in the committed realm. The current realm policy permits only account-unification-svc; all application RPs must use runtime desired-state reconciliation. Update this statement to preserve the portable-realm ownership boundary.

The validator regression now covers named legacy clients and an arbitrary unmanaged client. The clean-realm regression starts with no runtime application client and recreates naruon-web from the rendered desired-state template. The startup, recovery, deletion, and rollback order is documented consistently in the RP runbooks.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(realm): remove runtime application RPs from the portable import

1 participant