diff --git a/docs/getting-started/tutorial.md b/docs/getting-started/tutorial.md index 58aa52b..737ff2f 100644 --- a/docs/getting-started/tutorial.md +++ b/docs/getting-started/tutorial.md @@ -157,7 +157,7 @@ devslab: In production: set a real `identity.jwt.secret`, and for the bootstrap either set a strong `admin-password` (with `must-change-password: true`) or leave it blank to have a random one generated and logged once. See - [Configuration](../reference/configuration.md#first-admin-bootstrap-devslabkitbootstrap). + [Configuration](../reference/configuration.md#bootstrap). --- diff --git a/docs/guides/admin-console.ko.md b/docs/guides/admin-console.ko.md index 24fe537..355514c 100644 --- a/docs/guides/admin-console.ko.md +++ b/docs/guides/admin-console.ko.md @@ -74,7 +74,7 @@ kit의 **적용된 설정**(`devslab.kit.*`)을 읽기 전용으로 보는 화 감사, 캐시. 시크릿은 마스킹. 실행 중인 앱이 실제로 무엇을 로드했는지 확인용. (값 변경은 `application.yml` 에서 — [설정](../reference/configuration.md) 참고.) -### Config Sync (설정 동기화) +### Config Sync (설정 동기화) { #config-sync } 정의성 설정(권한·역할·메뉴)을 환경 간 승격. **기본 off**, 운영 프로파일에선 거부됨([Config Sync 가이드](config-sync.md) 참고). diff --git a/docs/guides/bootstrap.ko.md b/docs/guides/bootstrap.ko.md index 42efee0..e8b7164 100644 --- a/docs/guides/bootstrap.ko.md +++ b/docs/guides/bootstrap.ko.md @@ -1,54 +1,91 @@ # 최초 관리자 부트스트랩 빈 데이터베이스에는 사용자가 없습니다 — 그럼 영구 백도어 없이 처음에 어떻게 로그인할까요? -kit의 **최초 관리자 부트스트랩**이 첫 부팅 시 사용 가능한 관리자를 프로비저닝합니다. -opt-in이며 프로퍼티 기반입니다(배경: [ADR 0001](../adr/0001-bootstrap-admin.md)). +kit의 **최초 관리자 부트스트랩**이 첫 부팅 때 쓸 수 있는 관리자를 만들어 줍니다: opt-in, +속성 기반, 멱등(idempotent)입니다(배경: [ADR 0001](../adr/0001-bootstrap-admin.md)). -## 하는 일 +처음이면 [튜토리얼](../getting-started/tutorial.md)이 바로 이걸로 로그인까지 데려갑니다 — 이 +가이드는 그 단계의 레퍼런스입니다. -`bootstrap.enabled = true`이면 시작 시 kit이 (없을 때만) **멱등하게** 생성합니다: +## 무엇을 하나 + +`bootstrap.enabled = true`면, 시작 시 kit이 **멱등하게** (없을 때만) 생성합니다: 1. 테넌트 `bootstrap.tenant-id`, -2. 전체 `admin.*` 권한 세트를 가진 `PLATFORM_ADMIN` 역할, -3. 그 테넌트에 그 역할을 가진 관리자 사용자(`bootstrap.admin-login-id`). +2. 전체 `admin.*` 권한을 가진 `PLATFORM_ADMIN` 역할, +3. 그 역할을 가진 관리자 사용자(`bootstrap.admin-login-id`)를 해당 테넌트에. -멱등이므로 켜둔 채로 둬도 안전합니다 — 이후 부팅은 레코드를 찾고 아무것도 하지 않습니다. +멱등이므로 켜 둬도 안전합니다 — 이후 부팅은 레코드를 찾고 아무것도 하지 않습니다. ## 설정 ```yaml +# src/main/resources/application.yml devslab: kit: bootstrap: enabled: true tenant-id: default admin-login-id: admin - admin-password: ${DEVSLAB_BOOTSTRAP_ADMIN_PASSWORD:} # 비우면 랜덤, 한 번 로깅 + admin-password: ${DEVSLAB_BOOTSTRAP_ADMIN_PASSWORD:} # 비우면 → 랜덤, 한 번만 로그 must-change-password: true ``` -모든 키는 [설정 레퍼런스](../reference/configuration.md)를 참고하세요. +모든 키는 [설정 레퍼런스](../reference/configuration.md#bootstrap) 참고. ## 비밀번호 -- 알려진 값이 필요하면 **명시적으로 설정**(예: 로컬 개발 `admin`/`admin`). -- **비우면** kit이 강력한 랜덤 비밀번호를 생성해 시작 시 **한 번** 로깅합니다 — - 로그에서 복사하면 이후엔 사라집니다. +- 알려진 값이 필요하면 **명시 설정**(예: 로컬 개발 `admin`/`admin`). +- **비워 두면** kit이 강한 랜덤 비밀번호를 생성해 시작 시 **한 번만** 로그에 찍습니다 — + 로그에서 복사하면 그 뒤엔 사라집니다. - `prod` / `production` 프로파일에서는 약한 부트스트랩 비밀번호로 **시작을 거부**하므로, - placeholder가 운영에 새지 않습니다. + 플레이스홀더가 운영에 새어 들어가지 않습니다. + +알려진 비밀번호는 `must-change-password: true`와 함께 써서 운영자가 첫 로그인 때 교체하게 +하세요. + +## 처음 로그인하기 + +위 설정(`admin`/`admin`)으로 앱을 시작한 뒤: + +=== "관리자 콘솔" + + 1. [관리자 콘솔](admin-console.md)을 띄우고 브라우저에서 엽니다. + 2. `admin` / `admin`(테넌트 `default`)으로 로그인. + 3. 이제 `PLATFORM_ADMIN`을 가졌으니 모든 화면을 쓸 수 있습니다. -알려진 비밀번호는 `must-change-password: true`와 함께 써서 운영자가 첫 로그인 시 -교체하게 하세요. +=== "REST API" -## 최초 실행 감지 + ```bash + # 자격 증명을 JWT로 교환: + curl -X POST localhost:8080/admin/api/v1/auth/login \ + -H 'Content-Type: application/json' \ + -d '{"tenantId":"default","loginId":"admin","rawPassword":"admin"}' + # → { "token": "eyJ…" } — `Authorization: Bearer eyJ…`로 전송 + ``` -비인증 엔드포인트 `GET /admin/api/v1/bootstrap/status`가 `{ "initialized": boolean }`을 -반환합니다. 설정 마법사나 랜딩 페이지가 이를 기준으로 분기할 수 있습니다 — 예: 갓 배포된 -환경을 로그인 폼 대신 "관리자 생성" 플로우로 보냄. +## 첫 실행 감지 + +인증 없이 호출하는 `GET /admin/api/v1/bootstrap/status`는 `{ "initialized": boolean }`을 +반환합니다. 셋업 마법사나 랜딩 페이지가 이걸로 분기할 수 있습니다 — 예: 갓 배포된 인스턴스를 +로그인 폼 대신 "관리자 만들기" 흐름으로 보냅니다: + +```bash +curl localhost:8080/admin/api/v1/bootstrap/status +# → { "initialized": false } (아직 관리자 없음) / true (프로비저닝됨) +``` + +일부러 공개입니다 — 마법사는 누가 인증하기 *전에* 이걸 호출합니다. ## 운영 가이드 -실제 환경에서는 다음 중 하나를 권장합니다: +실제 환경에서는 둘 중 하나를 권장합니다: + +- 강한 `admin-password`(시크릿으로 주입) + `must-change-password: true`, 또는 +- `enabled: false`로 두고 최초 관리자를 외부에서 프로비저닝(SQL/마이그레이션/운영 도구). + +## 더 보기 -- 강력한 `admin-password`(시크릿으로 주입) + `must-change-password: true`, 또는 -- `enabled: false`로 두고 최초 관리자를 out-of-band(SQL/마이그레이션/운영 도구)로 프로비저닝. +- [튜토리얼](../getting-started/tutorial.md) — 실행 중인 앱에서의 부트스트랩. +- [Access (RBAC + ABAC)](access.md) — `PLATFORM_ADMIN`과 `admin.*`이 부여하는 것. +- [설정 레퍼런스](../reference/configuration.md#bootstrap) — 모든 키. diff --git a/docs/guides/bootstrap.md b/docs/guides/bootstrap.md index 9bc99ea..dc9ff63 100644 --- a/docs/guides/bootstrap.md +++ b/docs/guides/bootstrap.md @@ -2,7 +2,11 @@ A fresh database has no users — so how do you log in the first time without a permanent backdoor? The kit's **first-admin bootstrap** provisions a usable admin -on first boot, opt-in and property-driven (background: [ADR 0001](../adr/0001-bootstrap-admin.md)). +on first boot: opt-in, property-driven, and idempotent (background: +[ADR 0001](../adr/0001-bootstrap-admin.md)). + +New here? This is exactly what the [Tutorial](../getting-started/tutorial.md) uses to +get you logged in — this guide is the reference behind that step. ## What it does @@ -19,6 +23,7 @@ nothing. ## Configuration ```yaml +# src/main/resources/application.yml devslab: kit: bootstrap: @@ -29,7 +34,7 @@ devslab: must-change-password: true ``` -See the [Configuration reference](../reference/configuration.md) for every key. +See the [Configuration reference](../reference/configuration.md#bootstrap) for every key. ## The password @@ -42,12 +47,39 @@ See the [Configuration reference](../reference/configuration.md) for every key. Pair a known password with `must-change-password: true` so the operator rotates it on first login. +## Log in for the first time + +With the config above (`admin`/`admin`), start the app, then: + +=== "Admin console" + + 1. Start the [admin console](admin-console.md) and open it in the browser. + 2. Log in with `admin` / `admin` (tenant `default`). + 3. You now hold `PLATFORM_ADMIN` — every screen is available. + +=== "REST API" + + ```bash + # exchange credentials for a JWT: + curl -X POST localhost:8080/admin/api/v1/auth/login \ + -H 'Content-Type: application/json' \ + -d '{"tenantId":"default","loginId":"admin","rawPassword":"admin"}' + # → { "token": "eyJ…" } — send it as `Authorization: Bearer eyJ…` + ``` + ## First-run detection The unauthenticated endpoint `GET /admin/api/v1/bootstrap/status` returns `{ "initialized": boolean }`. A setup wizard or landing page can branch on it — e.g. send a brand-new deployment to a "create your admin" flow instead of a login -form. +form: + +```bash +curl localhost:8080/admin/api/v1/bootstrap/status +# → { "initialized": false } (no admin yet) / true (provisioned) +``` + +It's public on purpose — the wizard calls it *before* anyone can authenticate. ## Production guidance @@ -55,3 +87,9 @@ For real environments, prefer one of: - a strong `admin-password` (injected as a secret) + `must-change-password: true`, or - `enabled: false` and provision the first admin out-of-band (SQL/migration/ops tooling). + +## See also + +- [Tutorial](../getting-started/tutorial.md) — bootstrap in a running app. +- [Access (RBAC + ABAC)](access.md) — what `PLATFORM_ADMIN` and `admin.*` grant. +- [Configuration reference](../reference/configuration.md#bootstrap) — every key. diff --git a/docs/guides/config-sync.ko.md b/docs/guides/config-sync.ko.md index a12fdd2..e52d96d 100644 --- a/docs/guides/config-sync.ko.md +++ b/docs/guides/config-sync.ko.md @@ -1,95 +1,125 @@ # 설정 동기화 (Config Sync) **정의성 플랫폼 설정** — 권한, 역할(과 그 역할이 부여하는 권한 코드), 메뉴 — 을 대상 DB를 -직접 손대지 않고, 이식 가능한 코드 기준 번들로 한 환경에서 다른 환경으로 승격합니다. +일일이 손으로 고치는 대신, 휴대 가능한 **코드 기반 번들**로 한 환경에서 다른 환경으로 +승격합니다. -admin 콘솔은 RBAC 그래프 전체를 런타임에 관리할 수 있지만, 그 설정은 코드가 아니라 -**데이터베이스**에 있습니다. 팀은 로컬에서 설계한 뒤 dev / staging / 운영 — 각자 자기 DB — -에 *같은* 구조가 필요해집니다. 설정 동기화가 이를 옮기는 일급 수단입니다. 배경과 설계는 -[ADR 0003](../adr/0003-config-sync.md) 참고. +관리자 콘솔은 RBAC 그래프 전체를 런타임에 관리할 수 있지만, 그 설정은 코드가 아니라 +**데이터베이스**에 있습니다. 팀이 로컬에서 설계한 뒤, 각자 DB를 가진 dev / staging / +production에 *같은* 구조가 필요해집니다. 설정 동기화가 이를 옮기는 일급 수단입니다. 근거와 +설계는 [ADR 0003](../adr/0003-config-sync.md) 참고. -!!! warning "기본 off, 운영에선 금지" - 설정 동기화는 **dev/staging 편의 도구**로, 명시적으로 켜기 전까지 비활성이며 `prod` / - `production` 프로파일에서는 **기동을 거부**합니다. 운영 설정은 배포 시 git 에 커밋된 번들을 - 적용해 승격하지, 라이브 시스템에 즉석으로 push 하지 않습니다. +!!! warning "기본 off, 운영에서는 절대 안 됨" + 설정 동기화는 **dev/staging 편의 기능**으로, opt-in 하지 않으면 비활성이고, `prod` / + `production` 프로파일에서는 **시작을 거부**합니다. 운영 설정은 라이브 시스템에 즉석으로 + 푸시하는 게 아니라, git에 커밋된 번들을 배포 시 적용해서 승격합니다. ## 활성화 ```yaml +# src/main/resources/application.yml devslab: kit: config-sync: - enabled: true # 기본 false — 끄면 엔드포인트·UI 전체가 비활성 + enabled: true # 기본 false — 아니면 엔드포인트+UI 전체가 비활성 ``` -`enabled=true` 인데 `prod`/`production` 프로파일이 활성이면, 기능을 조용히 끄는 대신 명확한 -메시지와 함께 기동 단계에서 즉시 실패합니다. +`prod`/`production` 프로파일이 활성인데 `enabled=true`면, 조용히 비활성화하지 않고 시작 시 +명확한 메시지와 함께 fail-fast 합니다. ## 번들에 담기는 것 | 포함(정의성) | 제외 | | --- | --- | -| 권한 (`code`, 설명) | 감사 로그 (이력) | -| 역할 (`code`, 이름, **권한 코드**) | ABAC 정책 (이건 데이터가 아니라 *코드*) | -| 메뉴 (`code`, 부모 코드, 라벨, 경로, 아이콘, 필요 권한 코드, 순서) | 비밀번호 / 시크릿 | -| 사용자 — **옵트인 시에만**, login id 기준, 역할 코드 + **비밀번호 없음** | | +| 권한(`code`, 설명) | 감사 로그(이력) | +| 역할(`code`, 이름, **권한 코드들**) | ABAC 정책(이건 *코드*지 데이터가 아님) | +| 메뉴(`code`, 부모 코드, 라벨, 경로, 아이콘, 필요 권한 코드, 순서) | 비밀번호 / 시크릿 | +| 사용자 — **opt-in 시에만**, login id 기준, 역할 코드 포함, **비밀번호 없음** | | -모든 것이 DB UUID 가 아니라 **자연 코드**로 키잉되므로, id 가 다른 다른 환경에도 그대로 -적용됩니다. +모든 것은 DB UUID가 아니라 **자연 코드**로 키잉되므로, 한 환경에서 export한 번들이 id가 다른 +다른 환경에도 깔끔하게 적용됩니다. ## 엔드포인트 | | | | --- | --- | -| `GET /admin/api/v1/config/export?tenantId={t}&includeUsers=false` | 번들을 JSON 으로 반환. | +| `GET /admin/api/v1/config/export?tenantId={t}&includeUsers=false` | 번들을 JSON으로 반환. | | `POST /admin/api/v1/config/import?mode=merge&dryRun=true&includeUsers=false` | 번들 적용; 섹션별 diff 반환. | +## 전체 왕복(round trip) + +로컬에서 staging으로 설정 옮기기 — 복붙: + +```bash +# 1. 로컬 — 정의성 번들을 파일로 export +curl -G localhost:8080/admin/api/v1/config/export \ + -H 'Authorization: Bearer ' \ + --data-urlencode 'tenantId=default' \ + --data-urlencode 'includeUsers=false' \ + -o config-bundle.json + +# 2. 커밋해서 승격을 리뷰 가능·버전 관리되게 +git add config-bundle.json && git commit -m "chore: rbac bundle" + +# 3. staging — 먼저 DRY-RUN(아무것도 안 쓰고 diff 반환) +curl -X POST 'https://staging.example.com/admin/api/v1/config/import?mode=merge&dryRun=true' \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + --data-binary @config-bundle.json + +# 4. diff 검토 후 실제 적용 +curl -X POST 'https://staging.example.com/admin/api/v1/config/import?mode=merge&dryRun=false' \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + --data-binary @config-bundle.json +``` + ## 모드 -- **`merge`**(기본) — 추가형. 생성·수정만 하고 **삭제하지 않으며**, 역할의 기존 권한도 회수하지 - 않음. 멱등: 같은 번들을 다시 적용해도 변화 없음. -- **`mirror`** — 대상을 *번들과 정확히 일치*시킴. merge 에 더해 역할 권한을 재조정(번들에 없는 - 권한 회수)하고, 번들에 없는 정의성 엔터티를 **삭제**: - - **메뉴**는 leaf-first(자식 먼저, 부모 나중)로 삭제; - - **사용자에게 할당된 역할은 skip** — mirror 가 사용자 역할을 함부로 떼지 않음; - - **권한**은 테넌트 역할에서 회수 후 삭제. +- **`merge`**(기본) — 추가형. 생성·갱신만 하고 **삭제하지 않으며**, 역할의 기존 부여도 회수하지 + 않습니다. 멱등: 같은 번들을 다시 적용해도 아무것도 안 바뀜. +- **`mirror`** — 대상을 *번들과 정확히 일치*시킵니다. merge에 더해 각 역할의 부여를 조정하고 + (번들에 없는 권한 회수), 번들에 없는 정의성 엔터티를 **삭제**합니다: + - **메뉴**는 leaf부터 삭제(자식 먼저, 부모 나중); + - **사용자에게 아직 배정된 역할은 건너뜀** — mirror는 사용자의 역할을 벗기지 않음; + - **권한**은 테넌트의 역할들에서 회수한 뒤 삭제. -!!! danger "미러는 삭제합니다" - `mirror` 는 항목을 제거합니다. 적용 전 항상 **dry-run** diff 를 확인하고, 단일 테넌트 배포 - 환경에서만 권장합니다(권한은 전역). +!!! danger "Mirror는 삭제한다" + `mirror`는 무언가를 제거합니다. 적용 전 항상 **dry-run** diff를 검토하고, 배포당 단일 + 테넌트 구성에서만 쓰세요(권한은 전역입니다). -## 먼저 dry-run +## Dry-run 먼저 -`dryRun=true` 가 **기본**입니다. import 는 전체 diff 를 계산하고 아무것도 기록하지 않습니다. -결과는 섹션별(`permissions` / `roles` / `menus` / `users`)로 다음을 보고합니다: +`dryRun=true`가 **기본**입니다. import는 전체 diff를 계산하고 아무것도 쓰지 않습니다. 결과는 +섹션별(`permissions` / `roles` / `menus` / `users`)로 무엇이 다음이 될지 보고합니다: -- **생성(created)**, **수정(updated)**, **삭제(deleted, 미러 전용)**, **건너뜀(skipped — 사용 - 중인 역할, 또는 이미 존재하는 사용자)**. +- **created**, **updated**, **deleted**(mirror만), **skipped**(사용 중인 역할, 또는 기존 + 사용자). -미리보기가 의도와 맞으면 `dryRun=false` 로 실제 적용합니다. +미리보기가 의도와 맞으면 `dryRun=false`로 실제 적용. -## 사용자 동기화 (옵트인) +## 사용자 동기화 (opt-in) -`includeUsers=true` 시: +`includeUsers=true`면: -- **export** 는 사용자를 login id 기준으로 — 이메일·상태·역할 코드 — 내보내되 **비밀번호는 - 절대 포함하지 않음**. -- **import** 는 **생성 전용**: 없는 사용자만 사용 불가 비밀번호 + `mustChangePassword` 로 생성한 - 뒤 코드로 역할을 할당. **기존 사용자는 절대 덮어쓰지 않음**(`skipped` 로 보고). 비밀번호는 - 이후 admin 콘솔에서 설정. +- **export**는 사용자를 login id로 담습니다 — 이메일, 상태, 역할 코드 — 하지만 **비밀번호는 + 절대** 안 담음. +- **import**는 **생성 전용**: 없는 사용자는 사용 불가 비밀번호 + `mustChangePassword`로 + 생성된 뒤 역할이 코드로 배정됩니다. **기존 사용자는 절대 덮어쓰지 않음**(`skipped`로 보고). + 비밀번호는 이후 관리자 콘솔에서 설정. -사용자는 운영 데이터입니다. 새 환경에 계정을 시드할 의도가 아니면 `includeUsers` 는 꺼 두세요. +사용자는 운영 데이터입니다; 새 환경에 계정을 심을 의도가 아니면 `includeUsers`는 꺼 두세요. ## 권장 워크플로 -1. 로컬 DB 에서 설정 설계(admin 콘솔 또는 API). +1. 로컬 DB에서 설정 설계(관리자 콘솔 또는 API). 2. 번들 **export**(정의성만이면 `includeUsers=false`). -3. 번들 JSON 을 git 에 커밋 — 이제 리뷰·버전 관리되는 설정. -4. 대상에서 import **dry-run** 후 diff 확인. -5. 적용(`dryRun=false`). 추가/수정은 `merge`, 대상을 번들과 정확히 일치시킬 때만 `mirror`. +3. 번들 JSON을 git에 커밋 — 이제 리뷰 가능·버전 관리되는 설정. +4. 대상에서 import **dry-run** 후 diff 검토. +5. 적용(`dryRun=false`). 추가/갱신은 `merge`; 대상을 번들과 정확히 일치시킬 의도일 때만 `mirror`. -## Admin 콘솔 +## 관리자 콘솔 -[admin 콘솔](https://github.com/devslab-kr/devslab-kit-admin-ui)에 전체 흐름을 다루는 **Config -Sync** 페이지가 있습니다: export(보기 / 다운로드 / 복사), import(붙여넣기·업로드 → dry-run -diff → 적용), `merge`/`mirror` 전환, 사용자 동기화 토글. +[관리자 콘솔](admin-console.md#config-sync)에는 전체 흐름을 다루는 **Config Sync** 페이지가 +있습니다: export(보기 / 다운로드 / 복사), import(붙여넣기 또는 업로드 → dry-run diff → 적용), +`merge`/`mirror` 스위치, 사용자 동기화 토글. diff --git a/docs/guides/config-sync.md b/docs/guides/config-sync.md index ac8991b..5a17b8b 100644 --- a/docs/guides/config-sync.md +++ b/docs/guides/config-sync.md @@ -17,6 +17,7 @@ to move it. See [ADR 0003](../adr/0003-config-sync.md) for the rationale and des ## Enable it ```yaml +# src/main/resources/application.yml devslab: kit: config-sync: @@ -45,6 +46,34 @@ environment applies cleanly to another whose ids differ. | `GET /admin/api/v1/config/export?tenantId={t}&includeUsers=false` | Returns the bundle as JSON. | | `POST /admin/api/v1/config/import?mode=merge&dryRun=true&includeUsers=false` | Applies a bundle; returns a per-section diff. | +## A full round trip + +Move config from your local box to staging — copy-paste: + +```bash +# 1. on LOCAL — export the definitional bundle to a file +curl -G localhost:8080/admin/api/v1/config/export \ + -H 'Authorization: Bearer ' \ + --data-urlencode 'tenantId=default' \ + --data-urlencode 'includeUsers=false' \ + -o config-bundle.json + +# 2. commit it so the promotion is reviewable + versioned +git add config-bundle.json && git commit -m "chore: rbac bundle" + +# 3. on STAGING — DRY-RUN first (writes nothing, returns the diff) +curl -X POST 'https://staging.example.com/admin/api/v1/config/import?mode=merge&dryRun=true' \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + --data-binary @config-bundle.json + +# 4. review the diff, then apply for real +curl -X POST 'https://staging.example.com/admin/api/v1/config/import?mode=merge&dryRun=false' \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + --data-binary @config-bundle.json +``` + ## Modes - **`merge`** (default) — additive. Creates and updates; **never deletes**, and never revokes @@ -94,6 +123,6 @@ accounts into a fresh environment. ## Admin console -The [admin console](https://github.com/devslab-kr/devslab-kit-admin-ui) has a **Config Sync** -page that drives the whole flow: export (view / download / copy), import (paste or upload → -dry-run diff → apply), the `merge`/`mirror` switch, and the user-sync toggle. +The [admin console](admin-console.md#config-sync) has a **Config Sync** page that drives the +whole flow: export (view / download / copy), import (paste or upload → dry-run diff → apply), +the `merge`/`mirror` switch, and the user-sync toggle. diff --git a/docs/reference/configuration.ko.md b/docs/reference/configuration.ko.md index a9a9ed5..8075a21 100644 --- a/docs/reference/configuration.ko.md +++ b/docs/reference/configuration.ko.md @@ -83,7 +83,7 @@ [캐시 가이드](../guides/cache.md), [ADR 0002](../adr/0002-distributed-cache.md) 참고. -## 최초 관리자 부트스트랩 — `devslab.kit.bootstrap.*` +## 최초 관리자 부트스트랩 — `devslab.kit.bootstrap.*` { #bootstrap } 빈 데이터베이스에 최초 관리자를 프로비저닝(ADR 0001). **기본 OFF**이며 명시적으로 켭니다. diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index 4d9489d..94a92d6 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -87,7 +87,7 @@ See the [Menus guide](../guides/menus.md). See the [Caching guide](../guides/cache.md) and [ADR 0002](../adr/0002-distributed-cache.md). -## First-admin bootstrap — `devslab.kit.bootstrap.*` +## First-admin bootstrap — `devslab.kit.bootstrap.*` { #bootstrap } Provisions the first admin on a fresh database (ADR 0001). **Off by default**; opt in explicitly.