All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 39s
KEY-WP-0009-T04 is done. Re-ran verification on main with an explicit Go 1.23.0 toolchain and a writable build cache: contract validator PASS, build, vet and the full test suite pass, git diff --check clean. gofmt had drifted on internal/server/telemetry/events.go and tests/migration/scenario_c_test.go (comment alignment only, no semantic change); both reformatted so the task's formatting claim holds. The four handoffs to netkingdom, secrets-engine, ops-warden and railiance-platform were then actually sent -- the 2026-09-05 audit had shown the earlier claimed delivery had no receipts -- and each receipt id is recorded in the workplan and readable back from the hub. KEY-WP-0013-T02 and KEY-WP-0014-T04 stay in wait. Their remaining work is not ours to do, so what was owed was the request, and it is now delivered: custody admission for the two approval clients to railiance-platform, the exact human client id and callback URI to approval-engine, and the OpenBao-token vs issuer-JWT split plus Qonto rotation execution authority to ops-warden. Each message quotes the existing non-secret packet or reviewed sequence and states what is explicitly not being treated as authorization. No secret was read, no production resource changed, no route retired. SCOPE.md updated to match. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016uV8zoCKpA1WRAxsKRYbdH Assistant: claude-code Assistant-Model: opus Assistant-Process: 1182213@bnt-lap001 Assistant-Session: 966597b9-ae61-46a4-8b9e-1594ab3ec4ad
166 lines
11 KiB
Markdown
166 lines
11 KiB
Markdown
# SCOPE
|
|
|
|
Reviewed 2026-09-05 against source revision `b989de4`.
|
|
|
|
## Purpose and boundary
|
|
|
|
KeyCape is Go identity **Tooling** for NetKingdom's lightweight IAM deployment.
|
|
It runs its own OIDC-style issuer, uses Authelia for upstream browser
|
|
authentication, reads identities from LLDAP, and delegates factor checks to
|
|
privacyIDEA. It also provides caller authentication commands, directory snapshot
|
|
validation, and migration artifact generators.
|
|
|
|
It issues identity, role, scope and assurance claims. Resource authorization
|
|
belongs to access-engine and consuming services; secret custody and OpenBao
|
|
policy/token enforcement belong to their platform owners. The implemented subset
|
|
supports NetKingdom integrations, but complete profile conformance and drop-in
|
|
Keycloak interchangeability are not established.
|
|
|
|
## Implemented capabilities
|
|
|
|
| Surface | What the current source provides |
|
|
| --- | --- |
|
|
| Issuer HTTP API | Discovery, `/authorize` and its callback/registration-return routes, `/token`, `/jwks`, `/userinfo`, local `/logout`, and `/healthz`. |
|
|
| Human authentication | Authorization code with S256 PKCE, exact registered redirects, scope allow-lists, Authelia login, privacyIDEA MFA challenges, client-specific assurance requirements, browser session reuse, step-up/fresh-login handling, and configured registration/enrollment handoffs. |
|
|
| Service authentication | Static confidential `client_credentials` clients authenticated with form-encoded `client_secret_basic`; configured subject, tenant, roles, scopes and per-client token lifetime. Secrets are resolved from environment references at startup. |
|
|
| Tokens and identity | Locally signed RS256 JWTs; configurable access-token resource audience while ID tokens retain the client audience; human/service principal types, tenant, groups, roles, scope and assurance claims. UserInfo resolves canonical directory subjects and filters profile/email/groups by scope. |
|
|
| Caller commands | `keycape login` for public-client browser PKCE login and `keycape service-token` for service exchange. HTTPS discovery/JWKS verification and private JSON token-file delivery outside Git; no token output on stdout. |
|
|
| Validation and migration | Canonical snapshot checks; deterministic LLDAP user/group/membership export that records whether it enumerated the whole directory; basic Keycloak realm JSON; LDIF generation for OpenLDAP, 389 Directory Server and AD targets. These generate artifacts rather than execute a complete migration. |
|
|
| Diagnostics and packaging | Structured authentication/enforcement/migration events, liveness and dependency-probing readiness endpoints, Go build/test/vet targets, a container packaging the issuer and all four migration/validation binaries, and development/CI scaffolding. |
|
|
|
|
## Material limits
|
|
|
|
- Client registrations, service secrets and the signing key are loaded at startup.
|
|
Login, authorization and handoff state are process-local. There is no shared
|
|
session store, general refresh-token flow, token introspection/revocation API,
|
|
or automatic signing-key/client-secret rotation service. Logout clears the
|
|
local KeyCape session, not every upstream or downstream session/token.
|
|
- The optional tenant-engine `tenant_roles` adapter is wired through a
|
|
`tenantEngine` config block and is off unless `baseURL` is set (KEY-WP-0024).
|
|
When enabled it fails open: an unreachable source omits the claim rather than
|
|
failing issuance, so `tenant_roles` is a cache and must not be trusted for
|
|
privileged decisions.
|
|
- The LLDAP export enumerates the group subtree directly, so groups with no
|
|
members are present, and every snapshot carries a `groupEnumeration` field
|
|
saying whether that enumeration ran or the membership-derived fallback did
|
|
(KEY-WP-0018). A failed enumeration aborts the export rather than writing a
|
|
smaller snapshot, and read failures are reported rather than skipped. Users,
|
|
groups and memberships are sorted, so an unchanged directory exports
|
|
identically. Completeness beyond users, groups and memberships — passwords and
|
|
MFA credentials in particular — is still not covered.
|
|
- The Keycloak CLI takes a `-clients` config and migrates client registrations:
|
|
flows follow the declared grants, and audience, tenant, service subject and
|
|
roles are carried as protocol mappers with per-client lifetime, handoff URLs
|
|
and the secret *reference* as attributes (KEY-WP-0020). What it cannot carry it
|
|
names, in a report separate from the consistency check: secret values, MFA
|
|
policy enforcement, passwords and factor enrolment, and subject continuity —
|
|
Keycloak mints its own `sub`, so the LLDAP canonical ID survives only as an
|
|
attribute. The output is a reviewed artifact, not a proven migration; that
|
|
proof needs a live provider swap and is not established.
|
|
- Snapshot validation is a limited Go rule set, not full machine-readable schema
|
|
enforcement. Raw LDAP attribute keys are checked for descriptor validity,
|
|
canonical-mapping shadowing and case-only duplicates (KEY-WP-0021); attribute
|
|
values are not validated against a directory schema, and no allow-list of
|
|
permitted attribute names exists, since that field carries what the canonical
|
|
model does not name. The canonical YAML model and discovery metadata now match the
|
|
runtime client-registration surface and are held there by a conformance check
|
|
(KEY-WP-0017); the Go model is the runtime authority and the YAML the reviewed
|
|
contract. That is narrower than schema enforcement in general.
|
|
- The authorization-code grant now binds the redirect URI,
|
|
enforces grant-type eligibility, authenticates confidential clients and
|
|
consumes codes atomically, and UserInfo enforces algorithm, issuer and
|
|
access-token purpose (KEY-WP-0016). Upstream Authelia ID tokens are verified
|
|
before any claim is trusted — signature against the provider's published keys,
|
|
advertised issuer, own client ID in the audience, validity window — failing
|
|
closed and independently of transport (KEY-WP-0019). KeyCape deliberately does
|
|
not check or gate on the upstream transport. Enforcing these bindings
|
|
is not complete profile conformance. Relying parties must repeat `redirect_uri`
|
|
on the token exchange and present the access token, not the ID token, to
|
|
`/userinfo`; see [authorization-code bindings](docs/authorization-code-bindings.md).
|
|
See the assessment below.
|
|
- Tests cover local handlers, adapters, transformations and CLI protocol behavior.
|
|
They do not establish complete replacement against a running Keycloak/full-LDAP
|
|
stack. The Scenario B/C harnesses now run, and `src/tests/conformance` targets a
|
|
live issuer named by `KEYCAPE_TEST_ISSUER`, skipping when it is unset
|
|
(KEY-WP-0022). Run against Keycloak 26.0 it verified discovery, the
|
|
authorization surface, the published keys and a real token exchange — and
|
|
showed that stock Keycloak advertises the excluded `implicit` and `password`
|
|
grants, so it is not a drop-in for this profile. Directory migration is proved
|
|
end to end into a real OpenLDAP, with every migrated membership resolving, and a
|
|
realm built from a live export imports into Keycloak and serves discovery
|
|
(KEY-WP-0023). Relying-party behaviour and MFA against a migrated realm remain
|
|
unexercised, and credential/MFA migration is not supplied at all, so no harness
|
|
can establish it. Subject continuity is explicitly not preserved: the canonical
|
|
ID survives as an attribute while Keycloak mints its own `sub`.
|
|
- The server listens on HTTP; HTTPS termination is deployment-owned.
|
|
`/healthz` is liveness and probes nothing; `/readyz` probes LLDAP, Authelia and
|
|
privacyIDEA and gates traffic. `SIGTERM` drains in-flight requests for 15s.
|
|
The supported topology is a single replica, since login and authorization state
|
|
is process-local — see [operations](docs/operations.md) for that and for the
|
|
key-rotation, client-removal and logout limits. Development
|
|
Compose needs key and Authelia material absent from the checkout by design;
|
|
`scripts/bootstrap-dev.sh` generates it locally and git-ignores it
|
|
(KEY-WP-0026). Production
|
|
deployment and custody are external; a source implementation or example client
|
|
fragment does not prove live registration or consumer cutover.
|
|
- Notification receipt closure is done: KEY-WP-0009 is finished, its four handoffs
|
|
delivered with receipts readable via `GET /messages/?from_agent=key-cape`.
|
|
Approval-client provisioning (KEY-WP-0013-T02) and coordinated Qonto rotation
|
|
plus the login-proxy consumer split (KEY-WP-0014-T04) remain open. Both requests
|
|
are now delivered and both wait on owners outside this repository —
|
|
railiance-platform for custody admission, approval-engine for the exact human
|
|
callback, ops-warden for the route contract and a rotation execution/rollback
|
|
authority. No client is materialized in the deployed image and no route changed.
|
|
|
|
## Deliberate exclusions
|
|
|
|
General-purpose IAM, dynamic client registration, implicit/password grants,
|
|
wildcard redirects, arbitrary identity brokering, resource authorization policy,
|
|
OpenBao credential custody, and Keycloak operations are outside this repository's
|
|
ownership. Registration/enrollment handoffs do not implement user provisioning
|
|
or factor enrollment themselves.
|
|
|
|
## Entry points and verification
|
|
|
|
Run the issuer with `keycape --config PATH`; `keycape server` and
|
|
`keycape migrate` are not implemented subcommands. Caller commands are
|
|
`keycape login` and `keycape service-token`.
|
|
|
|
Separate binaries under `src/cmd/` are `validator`, `lldap-export`,
|
|
`keycape-to-keycloak`, and `lldap-to-ldap`. `make -C src build` places binaries in
|
|
root `bin/`; the container packages only `keycape`. Tests live under `src/tests/`
|
|
and alongside packages in `src/internal/`.
|
|
|
|
`make test`, `make lint` (Go vet), `make build`, and `make contract-test` are the
|
|
root checks. The capability-contract check requires the sibling NetKingdom
|
|
validator. These checks passed for the reviewed revision in the preceding
|
|
implementation session; the scope assessment is a source/documentation review.
|
|
|
|
Use this repo for its bounded issuer, caller JWT acquisition, identity adapters
|
|
and migration preparation. Consult [INTENT.md](INTENT.md) for the architectural
|
|
target, [native authentication](docs/native-authentication.md) for caller
|
|
constraints, and the [scope/intent assessment](history/2026-09-05-011726-scope-intent-assessment.md)
|
|
for evidence, priorities and remaining gaps.
|
|
|
|
## Provided capabilities
|
|
|
|
```capability
|
|
type: security
|
|
title: Bounded OIDC and service-token issuance
|
|
description: Implements static-client browser PKCE and client-credentials authentication with RS256 identity claims through Authelia, LLDAP and privacyIDEA; complete Keycloak interchangeability remains unproven.
|
|
keywords: [oidc, pkce, authentication, iam, sso, mfa, identity, service-token]
|
|
```
|
|
|
|
```capability
|
|
type: security
|
|
title: Verified caller JWT acquisition
|
|
description: Provides native browser login and service-token exchange commands with HTTPS discovery, JWKS verification and private token-file delivery.
|
|
keywords: [oidc, pkce, jwt, cli, authentication, jwks]
|
|
```
|
|
|
|
```capability
|
|
type: security
|
|
title: Directory validation and migration artifact generation
|
|
description: Checks canonical snapshots and produces LLDAP exports, basic Keycloak realm JSON and target-specific LDIF; does not perform or prove a complete live migration.
|
|
keywords: [migration, identity, lldap, keycloak, ldif, validation]
|
|
```
|