-
Notifications
You must be signed in to change notification settings - Fork 0
docs: establish identity product and architecture baseline #75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
2644054
d427218
43d6002
084cef8
77d132d
2a7a024
64efc5a
57a13cd
34848dc
967303a
9c833b5
b120c09
54bb818
9330856
e5c336d
a5c3a8b
1bf79ae
ee38d88
7ce6512
c10d05a
54edc0c
f1c1569
e8fa0de
5b718b3
14dec35
5987c8d
a6cf52c
0a1d441
4d28410
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # Keyverse Documentation Map | ||
|
|
||
| Keyverse already has strong feature-specific specifications, doctoring, federation/onboarding, topology, and operations records. This index makes the cross-cutting product and architecture graph explicit without replacing those slice documents. | ||
|
|
||
| | Area | Canonical document | | ||
| |---|---| | ||
| | Product requirements | [`docs/PRD.md`](docs/PRD.md) | | ||
| | Technical requirements | [`docs/TRD.md`](docs/TRD.md) | | ||
| | Architecture | [`ARCHITECTURE.md`](ARCHITECTURE.md) | | ||
| | Topology | [`docs/topology.md`](docs/topology.md) | | ||
| | UML/runtime/authority flows | [`docs/UML.md`](docs/UML.md) | | ||
| | Logical/physical ERD | [`docs/ERD.md`](docs/ERD.md) | | ||
| | Threat model | [`docs/THREAT_MODEL.md`](docs/THREAT_MODEL.md) | | ||
| | Test strategy | [`docs/TEST_STRATEGY.md`](docs/TEST_STRATEGY.md) | | ||
| | Operability/recovery/release | [`docs/OPERABILITY.md`](docs/OPERABILITY.md) | | ||
| | Requirements/evidence traceability | [`docs/TRACEABILITY.md`](docs/TRACEABILITY.md) | | ||
| | Architecture decisions | [`docs/adr/README.md`](docs/adr/README.md) | | ||
| | Federation onboarding | [`docs/federation-onboarding.md`](docs/federation-onboarding.md) | | ||
| | RP onboarding | [`docs/rp-onboarding.md`](docs/rp-onboarding.md) | | ||
| | Account merge/unification | [`docs/merge-unification-flow.md`](docs/merge-unification-flow.md) | | ||
| | Standards/APA 7 evidence | [`docs/doctoring/`](docs/doctoring/) and [`docs/papers/`](docs/papers/) | | ||
| | Operations | [`docs/operations/`](docs/operations/) | | ||
| | Security reporting | [`SECURITY.md`](SECURITY.md) | | ||
| | Agent instructions | [`AGENTS.md`](AGENTS.md) | | ||
| | Agent context | [`CLAUDE.md`](CLAUDE.md) | | ||
| | Product overview | [`README.md`](README.md) | | ||
| | Change history | [`CHANGELOG.md`](CHANGELOG.md) | | ||
|
|
||
| ## Maturity vocabulary | ||
|
|
||
| - **implemented-main** — present on protected main with source/tests. | ||
| - **active-PR** — implemented only on an open PR and not yet a protected-main claim. | ||
| - **deployment-owned** — private tenant/customer secret/configuration behavior owned by deployment controller/secret store. | ||
| - **external-system** — Keycloak/ADFS/LDAP/external OIDC/HR/IGA behavior not implemented by Keyverse itself. | ||
| - **planned** — accepted target without executable implementation. | ||
|
|
||
| Open PR #72 OIDC RP claim mapper profile and PR #74 hourly GitHub API remediation remain active-PR until merged. Keyverse's current protected-main desired-state/reconciliation capabilities are documented independently from those changes. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,189 @@ | ||
| # Keyverse Logical and Persistence ERD | ||
|
|
||
| **Status:** Accepted cross-cutting data model. Exact Keycloak internal schema remains Keycloak-owned. | ||
| **Last reviewed:** 2026-08-09 | ||
|
|
||
| Keyverse persists its own configuration, desired-state, receipts, merge audit, and user-operation locks while Keycloak/PostgreSQL owns canonical IdP users/sessions/clients/federation runtime state. This ERD models Keyverse-owned durable records and their relation to external Keycloak identities without pretending to own Keycloak's internal tables. | ||
|
|
||
| ```mermaid | ||
| erDiagram | ||
| IDP_CONFIG_ENTRY }o--|| TENANT_DEPLOYMENT : scoped_to | ||
| FEDERATION_SOURCE }o--|| TENANT_DEPLOYMENT : scoped_to | ||
| DIRECTORY_FEDERATION_SOURCE }o--|| TENANT_DEPLOYMENT : scoped_to | ||
| RELYING_PARTY_SOURCE }o--|| TENANT_DEPLOYMENT : scoped_to | ||
|
|
||
| FEDERATION_SOURCE ||--o{ FEDERATION_APPLY_RECEIPT : produces | ||
| DIRECTORY_FEDERATION_SOURCE ||--o{ DIRECTORY_FEDERATION_APPLY_RECEIPT : produces | ||
| RELYING_PARTY_SOURCE ||--o{ RELYING_PARTY_APPLY_RECEIPT : produces | ||
|
|
||
| KEYCLOAK_USER_REFERENCE ||--o{ ACCOUNT_MERGE_AUDIT : survivor_or_duplicate | ||
| KEYCLOAK_USER_REFERENCE ||--o| USER_OPERATION_LOCK_STATE : guarded_by | ||
| KEYCLOAK_USER_REFERENCE ||--o{ EXTERNAL_IDENTITY_LINK : owns | ||
| EXTERNAL_IDENTITY_LINK }o--|| FEDERATION_SOURCE : originates_from | ||
|
|
||
| TENANT_DEPLOYMENT { | ||
| uuid tenant_deployment_id PK | ||
| text deployment_name | ||
| text deployment_status_code | ||
| timestamptz created_at | ||
| } | ||
|
|
||
| IDP_CONFIG_ENTRY { | ||
| uuid idp_config_entry_id PK | ||
| uuid tenant_deployment_id FK | ||
| text config_key | ||
| text protected_value_ref | ||
| text config_version | ||
| timestamptz updated_at | ||
| } | ||
|
|
||
| FEDERATION_SOURCE { | ||
| uuid federation_source_id PK | ||
| uuid tenant_deployment_id FK | ||
| text federation_alias | ||
| text protocol_code | ||
| jsonb secret_free_desired_state | ||
| text desired_state_hash | ||
| text lifecycle_status_code | ||
| timestamptz updated_at | ||
| } | ||
|
|
||
| FEDERATION_APPLY_RECEIPT { | ||
| uuid federation_apply_receipt_id PK | ||
| uuid federation_source_id FK | ||
| uuid apply_attempt_id UK | ||
| text desired_state_hash | ||
| text keycloak_resource_id | ||
| text observed_state_hash | ||
| text apply_outcome_code | ||
| timestamptz observed_at | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| DIRECTORY_FEDERATION_SOURCE { | ||
| uuid directory_federation_source_id PK | ||
| uuid tenant_deployment_id FK | ||
| text directory_alias | ||
| jsonb private_desired_state | ||
| text desired_state_hash | ||
| text lifecycle_status_code | ||
| timestamptz updated_at | ||
| } | ||
|
|
||
| DIRECTORY_FEDERATION_APPLY_RECEIPT { | ||
| uuid directory_federation_apply_receipt_id PK | ||
| uuid directory_federation_source_id FK | ||
| uuid apply_attempt_id UK | ||
| text desired_state_hash | ||
| text keycloak_component_id | ||
| text observed_state_hash | ||
| text apply_outcome_code | ||
| timestamptz observed_at | ||
| } | ||
|
|
||
| RELYING_PARTY_SOURCE { | ||
| uuid relying_party_source_id PK | ||
| uuid tenant_deployment_id FK | ||
| text client_id | ||
| jsonb secret_free_desired_state | ||
| text desired_state_hash | ||
| text lifecycle_status_code | ||
| timestamptz updated_at | ||
| } | ||
|
|
||
| RELYING_PARTY_APPLY_RECEIPT { | ||
| uuid relying_party_apply_receipt_id PK | ||
| uuid relying_party_source_id FK | ||
| uuid apply_attempt_id UK | ||
| text desired_state_hash | ||
| text keycloak_client_uuid | ||
| text observed_state_hash | ||
| text apply_outcome_code | ||
| timestamptz observed_at | ||
| } | ||
|
|
||
| KEYCLOAK_USER_REFERENCE { | ||
| uuid keycloak_user_reference_id PK | ||
| uuid tenant_deployment_id FK | ||
| text keycloak_user_uuid | ||
| text lifecycle_status_code | ||
| } | ||
|
|
||
| EXTERNAL_IDENTITY_LINK { | ||
| uuid external_identity_link_id PK | ||
| uuid keycloak_user_reference_id FK | ||
| uuid federation_source_id FK | ||
| text external_subject_hash | ||
| boolean email_verified | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| ACCOUNT_MERGE_AUDIT { | ||
| uuid account_merge_audit_id PK | ||
| uuid survivor_user_reference_id FK | ||
| uuid duplicate_user_reference_id FK | ||
| text match_evidence_code | ||
| text operation_outcome_code | ||
| uuid actor_identity_id | ||
| timestamptz occurred_at | ||
| } | ||
|
|
||
| USER_OPERATION_LOCK_STATE { | ||
| uuid user_operation_lock_state_id PK | ||
| uuid keycloak_user_reference_id FK | ||
| text operation_type_code | ||
| text lock_owner_token | ||
| timestamptz acquired_at | ||
| timestamptz lease_expires_at | ||
| } | ||
| ``` | ||
|
|
||
| ## Logical uniqueness constraints | ||
|
|
||
| UUID primary identifiers are globally unique. Human/provider identifiers are scoped to the owning tenant or federation source and MUST NOT be interpreted as global keys. | ||
|
|
||
| | Entity | Required logical uniqueness | | ||
| |---|---| | ||
| | `IDP_CONFIG_ENTRY` | `(tenant_deployment_id, config_key)` | | ||
| | `FEDERATION_SOURCE` | `(tenant_deployment_id, federation_alias)` | | ||
| | `DIRECTORY_FEDERATION_SOURCE` | `(tenant_deployment_id, directory_alias)` | | ||
| | `RELYING_PARTY_SOURCE` | `(tenant_deployment_id, client_id)` | | ||
| | `KEYCLOAK_USER_REFERENCE` | `(tenant_deployment_id, keycloak_user_uuid)` | | ||
| | `EXTERNAL_IDENTITY_LINK` | `(federation_source_id, external_subject_hash)` | | ||
|
|
||
| `federation_source_id` defines the identity-provider scope for the external-subject uniqueness rule. Within one federation source, one normalized/hashed external subject may link to at most one Keycloak user reference. This prevents one issuer/provider subject from being attached to multiple users while still allowing unrelated providers to use the same subject string. | ||
|
|
||
| Physical migrations must enforce these constraints in the owning Keyverse store. Documentation labels such as `client_id`, `federation_alias`, or Keycloak UUID never authorize cross-tenant lookup by themselves. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== docs/ERD.md relevant sections =="
sed -n '120,175p' docs/ERD.md | cat -n -v
echo
echo "== Search related table definitions/constraints =="
rg -n "EXTERNAL_IDENTITY_LINK|ACCOUNT_MERGE_AUDIT|tenant_deployment_id|client_id|federation_alias|keycloak_user_reference_id|federation_source_id" docs -S || true
echo
echo "== Search tenant references for related entities =="
rg -n "tenant_deployment_id|tenant_id|MULTI_TENANT|tenant" . -S --glob '!node_modules' --glob '!dist' --glob '!build' | head -200Repository: ContextualWisdomLab/keyverse Length of output: 14893 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== docs/ERD.md table definitions =="
sed -n '1,120p' docs/ERD.md | cat -n -v
echo
echo "== Full references to ACCOUNT_MERGE_AUDIT / EXTERNAL_IDENTITY_LINK =="
rg -n "ACCOUNT_MERGE_AUDIT|EXTERNAL_IDENTITY_LINK|survivor_user_reference_id|duplicate_user_reference_id|keycloak_user_reference_id|federation_source_id|tenant_deployment_id" . \
-S --glob '!node_modules' --glob '!dist' --glob '!build' \
| sed -n '1,240p'
echo
echo "== Read-only check: ERD FK constraints and tenant columns for target tables =="
python3 - <<'PY'
from pathlib import Path
p=Path('docs/ERD.md')
text=p.read_text()
targets = ['FEDERATION_SOURCE','KEYCLOAK_USER_REFERENCE','EXTERNAL_IDENTITY_LINK','ACCOUNT_MERGE_AUDIT']
in_table=False
current=None
for line in text.splitlines():
if re := any(f' {{{t}' in line for t in targets):
m = next(t for t in targets if f' {{{t}' in line)
current=m
in_table=True
print(f'\n[{current}]')
continue
if in_table:
if line.startswith(' }'):
in_table=False
current=None
continue
print(line)
PYRepository: ContextualWisdomLab/keyverse Length of output: 7406 테넌트 간 관계를 데이터베이스 제약으로 차단하세요.
🤖 Prompt for AI Agents |
||
|
|
||
| ## Identity and authorization rules | ||
|
|
||
| - Keycloak UUIDs, federation aliases, RP client IDs, email values, and external subjects are data identifiers, not authorization by themselves. | ||
| - Exact external identity key is `(identity_provider, subject)`; verified email may support matching under policy but unverified email never authorizes linking. | ||
| - `tenant_deployment_id` is explicit in Keyverse-owned records; deployment/customer separation must not be inferred from realm/resource names. | ||
| - Secrets are referenced through protected values/handles where possible; secret-free desired-state tables must never gain client/bind credentials accidentally. | ||
|
|
||
| ## Desired-state and receipt invariant | ||
|
|
||
| ```mermaid | ||
| flowchart LR | ||
| PRIVATE[Private rendered input] | ||
| VALID[Preflight validation] | ||
| INTENT[Versioned desired-state source] | ||
| REMOTE[Keycloak live state] | ||
| RECEIPT[Version-bound apply receipt] | ||
|
|
||
| PRIVATE --> VALID | ||
| VALID --> INTENT | ||
| INTENT --> REMOTE | ||
| REMOTE --> RECEIPT | ||
| ``` | ||
|
|
||
| Every apply receipt records the exact `desired_state_hash` that was acted on as well as the canonical `observed_state_hash`, outcome, unique `apply_attempt_id`, and observation time. A receipt is current for a source only when its `desired_state_hash` equals that source's current desired-state hash. The latest current receipt is the greatest `observed_at` among receipts for that exact desired-state hash; a receipt for an older hash is historical evidence and cannot establish convergence for a newer desired state. | ||
|
|
||
| Retry handling is idempotency-aware. Reusing the same `apply_attempt_id` must return/reuse the same receipt rather than create a second logical attempt. A retry under a new attempt ID may create another receipt, but it remains a distinct attempt and must still bind to the exact desired-state hash. Delete flows that require remote-first semantics cannot remove local desired state before remote deletion succeeds. | ||
|
|
||
| ## Keycloak ownership | ||
|
|
||
| Users, sessions, roles, groups, credentials, WebAuthn material, IdP runtime representation, LDAP storage components, and RP clients ultimately live in Keycloak's schema/API. Keyverse stores controlled references/intent/receipts but does not duplicate or directly edit unsupported Keycloak internal tables. | ||
|
|
||
| ## Migration acceptance | ||
|
|
||
| Changes to Keyverse-owned persistence require migrations/rollback, transaction/concurrency tests, indexes/constraints, tenant isolation, secret/logging tests, backup/restore impact, and ERD/operability/ADR synchronization. Keycloak upgrades require supported schema migration through Keycloak, not custom manipulation of its private database tables. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| # Keyverse Operability, Recovery, and Release Guide | ||
|
|
||
| **Status:** Accepted cross-cutting operating baseline | ||
| **Last reviewed:** 2026-08-09 | ||
|
|
||
| Feature-specific procedures under `docs/operations/`, federation/RP onboarding, and deployment READMEs remain authoritative for their slices. This guide defines the shared operating model and evidence needed before declaring the identity platform healthy or release-ready. | ||
|
|
||
| ## Health model | ||
|
|
||
| Distinguish these conditions: | ||
|
|
||
| 1. **process liveness:** Keycloak/admin process responds; | ||
| 2. **component readiness:** database/config/bootstrap and core dependencies are usable; | ||
| 3. **desired-state convergence:** configured federation/directory/RP state matches Keycloak; | ||
| 4. **protocol acceptance:** controlled login/logout/token/SCIM/bind/search behavior succeeds; | ||
| 5. **downstream authorization acceptance:** RP accepts expected issuer/audience/claims and applies its own authorization policy. | ||
|
|
||
| A lower-level green state never implies a higher-level state. | ||
|
|
||
| ## Key SLIs | ||
|
|
||
| - Keycloak/admin readiness and latency; | ||
| - login/passkey success/error rates; | ||
| - SCIM mutation success/conflict/retry/lock contention; | ||
| - account merge/link outcomes and rollback/tombstone anomalies; | ||
| - desired-state drift and reconciliation age; | ||
| - federation/LDAP/RP apply/re-observation failures; | ||
| - duplicate remote resource detections; | ||
| - user-operation lock wait/expiry/recovery; | ||
| - token issuer/audience/claim acceptance failures; | ||
| - database/storage availability and transaction errors; | ||
| - secret/config bootstrap failures; | ||
| - hourly governance run outcomes without false-green classification. | ||
|
|
||
| Do not put raw tokens, secrets, passwords/bind credentials, protected private payloads, or unnecessary PII into metrics/logs. | ||
|
|
||
| ## Federation onboarding runbook | ||
|
|
||
| 1. render private tenant configuration from approved KV/secret source; | ||
| 2. run authenticated side-effect-free Keyverse preflight; | ||
| 3. review exact policy result; | ||
| 4. persist/apply desired state through the owning reconciliation path; | ||
| 5. verify exact post-mutation Keycloak state/receipt; | ||
| 6. for LDAP/AD perform controlled bind/search/login acceptance after explicit apply; | ||
| 7. for SAML/OIDC perform controlled login/issuer/subject/email/trust checks; | ||
| 8. monitor convergence/errors; | ||
| 9. retain rollback data until acceptance criteria expire. | ||
|
|
||
| ## RP onboarding runbook | ||
|
|
||
| 1. submit secret-free client representation; | ||
| 2. preflight redirects/origins/logout/PKCE/scopes/type; | ||
| 3. reconcile exact Keycloak client and receipt; | ||
| 4. provision confidential secret through the separate secret-management path if needed; | ||
| 5. configure RP securely; | ||
| 6. run authorization-code/PKCE login/logout/token audience/claim acceptance; | ||
| 7. validate downstream authorization separately from authentication. | ||
|
|
||
| PR #72's mapper profile requires the same acceptance after merge: operators must test the **Naruon** product login/token/authorization journey using the `naruon-web` RP client ID and verify the expected audience and bounded claims. Mapper unit tests alone do not prove Naruon product authorization readiness. | ||
|
|
||
| ## Account merge recovery | ||
|
|
||
| Merge and SCIM full replacement (`PUT`) must hold the shared operation lock. Protected-main `PATCH active=false` is not currently inside that shared-lock guarantee and must not be treated as transactionally serialized with merge. On failure, classify whether state changed in Keycloak, Keyverse audit, linked identities, or tombstone status. Re-observe before retry. Never infer a retry is safe solely from the previous HTTP response. Preserve survivor and duplicate lineage in audit. | ||
|
|
||
| ## Desired-state recovery | ||
|
|
||
| On controller/API crash after intent but before receipt: | ||
|
|
||
| - read persisted desired state; | ||
| - query exact remote Keycloak state; | ||
| - classify converged, absent, duplicate, or drifted; | ||
| - reconcile idempotently; | ||
| - write receipt only after exact re-observation and bind it to the desired-state version/hash that was applied. | ||
|
|
||
| On delete, keep local intent until remote-first deletion has succeeded where required. | ||
|
|
||
| ## Database/backup | ||
|
|
||
| Back up Keycloak PostgreSQL and Keyverse-owned configuration/audit/intent/receipt state according to deployment RPO/RTO. Restore through supported Keycloak/database procedures, then run reconciliation and controlled authentication/provisioning acceptance. Do not edit unsupported Keycloak internal tables as a normal recovery technique. | ||
|
|
||
| ## Upgrade/rollback | ||
|
|
||
| - review Keycloak release/migration notes and Keyverse CHANGELOG/ADRs; | ||
| - rehearse database migration and Helm/Compose upgrade; | ||
| - validate realm/config/template compatibility; | ||
| - run merge/SCIM/federation/RP suites; | ||
| - canary controlled login/provisioning; | ||
| - roll back application/config where safe and use supported DB backup/restore for incompatible schema migrations; | ||
| - re-run convergence and protocol acceptance after rollback. | ||
|
|
||
| ## Automation incident RCA | ||
|
|
||
| PR #74 demonstrates that a workflow can appear successful while doing no useful work if a GitHub API gate fails open. Scheduled governance must classify transport failure separately from a valid empty/unhealthy result, fit its time budget, keep provider secrets in the broker phase only, and require exact `success` for protected evidence. After PR #74 merges, operational closure requires a real protected-main scheduled/manual run. | ||
|
|
||
| ## Release gate | ||
|
|
||
| Release only after protected-head CI/security/review, 100% coverage/docstrings, realm/package/deployment validation, migrations/rollback/backup, passkey/federation/SCIM/RP controlled acceptance, secret scan, SBOM/provenance/image digest, runbooks/support, and CHANGELOG/version artifacts are coherent. A merged PR is not a release by itself. |
Uh oh!
There was an error while loading. Please reload this page.