# KeyCape *Prepare for Keycloak without Keycloak* KeyCape is the lightweight IAM component of [NetKingdom](../net-kingdom/). It implements the **NetKingdom IAM Profile** — a versioned OIDC/PKCE contract — by orchestrating Authelia, LLDAP, and privacyIDEA. The same profile is implemented by Keycloak in expanded-mode deployments. Applications integrate against the profile, not against Keycape internals. This makes the lightweight → expanded migration a tested, automated operation rather than a rewrite. ## Status **Implementation complete (v0.1).** All 23 workplan tasks implemented and tested. 21 test packages, all green. See `workplans/KEY-WP-0001-keycape-implementation.md`. ## Architecture ``` Application │ (NetKingdom IAM Profile) ▼ KeyCape ←── profile enforcement, claim normalization, telemetry / | \ Auth LLDAP privacyIDEA elia ``` **Expanded mode:** Replace KeyCape with Keycloak. Same profile, same tests pass. ## Quick Start ```bash # Start the dev stack (KeyCape + LLDAP + Authelia + privacyIDEA) make dev # Build the server binary make build # Run all tests make test ``` ## Configuration KeyCape uses a YAML config file. See `config/dev-config.yaml` for a full example. ```yaml issuer: "https://auth.netkingdom.local" port: 8080 tokenLifetime: "15m" privateKeyPem: "/etc/keycape/key.pem" environment: "production" lldap: url: "ldap://lldap:389" bindDN: "cn=admin,dc=netkingdom,dc=local" bindPW: "secret" baseDN: "dc=netkingdom,dc=local" authelia: baseURL: "https://authelia.local" clientId: "keycape" clientSecret: "secret" redirectURI: "https://auth.netkingdom.local/authorize/callback" privacyidea: baseURL: "https://privacyidea.local" adminToken: "secret" realm: "netkingdom" clients: - clientId: "my-app" displayName: "My Application" redirectUris: ["https://myapp.local/callback"] allowedScopes: ["openid", "profile", "email", "groups"] grantTypes: ["authorization_code"] clientType: "public" ``` Config is validated at startup — the server exits 1 with validation errors if config is invalid. ## Endpoints | Endpoint | Description | |---|---| | `GET /.well-known/openid-configuration` | OIDC discovery document | | `GET /jwks` | RS256 public key in JWK Set format | | `GET /authorize` | Authorization endpoint (PKCE required) | | `GET /authorize/callback` | Authelia callback handler | | `POST /token` | Token exchange (authorization_code only) | | `GET /userinfo` | Userinfo endpoint (Bearer token required) | | `GET /healthz` | Health check → `{"status":"ok","version":"0.1.0"}` | ## Profile Constraints KeyCape enforces the NetKingdom IAM Profile. Violations return structured errors: | Error type | Meaning | |---|---| | `feature_not_supported_by_profile` | Feature is outside the profile entirely | | `available_in_keycloak_mode_only` | Available in expanded mode, not lightweight | | `rejected_for_profile_safety` | Would weaken security guarantees | | `invalid_profile_usage` | Supported feature used incorrectly | Enforced boundaries: no implicit flow, no wildcard redirect URIs, no dynamic client registration, no identity brokering, PKCE S256 required. ## Migration Tools KeyCape ships migration tools for the two orthogonal migration dimensions: **IAM migration (KeyCape → Keycloak):** ```bash # Export canonical data from LLDAP ./lldap-export --url ldap://lldap:389 --bind-dn cn=admin,... --output canonical-export.yaml # Transform to Keycloak realm import ./keycape-to-keycloak --input canonical-export.yaml --realm netkingdom --output keycloak-realm-import.json ``` **Directory migration (LLDAP → OpenLDAP / 389DS / AD):** ```bash ./lldap-to-ldap --input canonical-export.yaml --target openldap --base-dn dc=netkingdom,dc=local --output migration.ldif ``` Both migrations are independent. Perform either or both without affecting privacyIDEA MFA enrollment. ## LDAP Schema Validator ```bash # Validate in CI mode (strict) ./validator --mode ci --input directory-snapshot.yaml # Validate before provisioning ./validator --mode provisioning --input users.yaml ``` Validates: DN structure, required attributes, no unknown attributes, user references, no cyclic groups, username uniqueness, email format. ## Repo Structure ``` src/ cmd/ # Binary entrypoints keycape/ # Main server validator/ # LDAP schema validator lldap-export/ # Migration: LLDAP → canonical keycape-to-keycloak/ # Migration: canonical → Keycloak lldap-to-ldap/ # Migration: canonical → LDIF internal/ config/ # Config loading and validation domain/ # Canonical identity model (Go types) errors/ # Profile error taxonomy adapters/ # Backend adapters (Authelia, LLDAP, privacyIDEA) server/ # OIDC handlers + telemetry + enforcement migration/ # Migration logic validator/ # LDAP schema validation tests/ profile/ # Scenario A: lightweight baseline negative/ # Scenario D: unsupported feature rejection migration/ # Scenarios B & C: replacement tests spec/ canonical-model.yaml # Source of truth for all identity data ldap-schema.yaml # Canonical LDAP schema rules docs/adr/ # Architecture Decision Records workplans/ # Implementation workplans wiki/ # Specifications ``` ## Key Documents - `wiki/KeyCapeSpecification_v0.1.md` — Architecture, design intent, objectives - `wiki/KeyCapeSpecificationPack_v0.1.md` — Normative implementation spec - `docs/adr/ADR-0001-choose-go-for-keycape.md` — Language decision (Go vs Rust) ## Domain Part of the **NetKingdom** domain. Tracked in the Custodian State Hub under domain `netkingdom`, repo slug `key-cape`. See `CLAUDE.md` for agent session protocol and workplan conventions.