Problem
Operators of OpenNDX (Portal Backend) have no way to manage members, schemas, applications, and application policies except by hand-crafting authenticated HTTP calls against Portal Backend's API - there's no CLI, and no browser-based operator login.
On top of that gap, a first cut at such a CLI still forces an operator to retype a full set of identity-provider flags (--auth-url, --token-url, --client-id, --scopes, --callback-port) on every login, even though:
- Most OIDC-compliant identity providers publish their
authorization_endpoint/token_endpoint via a standard discovery document, so only a base issuer URL should be needed.
- Every environment this CLI targets (local ThunderID, an Asgardeo tenant, a partner's deployment) needs the same set of values repeated verbatim on every invocation, with no way to save and switch between them.
Proposed Solution
Add ndx, a Go CLI for OpenNDX management operations against Portal Backend:
cmd/cli - entry point wiring ndx login, ndx members create, ndx schemas create, ndx applications create/list/get, and ndx policy update.
internal/cli/auth - browser-based OAuth2 Authorization Code + PKCE login (RFC 8252), local token cache with refresh, and OIDC Discovery support: an --issuer/NDX_ISSUER flag that derives --auth-url/--token-url from {issuer}/.well-known/openid-configuration, so standards-compliant IDPs only need a base URL (explicit --auth-url/--token-url still work and take precedence for IDPs that don't serve discovery at the standard path).
internal/cli/pbclient - a Portal Backend API client for the management operations above, reusing internal/pb/v1/models types.
internal/cli/profile - named profiles (ndx profile list/use/set) bundling issuer, client ID, scopes, callback port, Portal Backend URL, and TLS verification per environment, with a built-in local profile matching this repo's ThunderID docker-compose stack so ndx login works with zero flags out of the box. Any explicit flag still overrides the active profile; --profile/NDX_PROFILE selects a profile for a single invocation without changing the default. Each non-default profile gets its own cached-token file so switching profiles can't pick up a token cached against a different identity provider.
thunderid/bootstrap/*.yaml - registers the NDX_CLI OAuth2 client, an ndx-admin operator user, and the OpenNDX_Admin role/group needed for ndx login to work end-to-end against the local ThunderID stack.
Alternatives
- Require every IDP endpoint flag explicitly, with no discovery. Rejected - it works, but is needless friction for every IDP that already publishes a discovery document, and doesn't reduce as new environments are added.
- Hardcode one IDP's endpoints/client ID into the binary. Rejected - the CLI has to work against any OIDC-compatible IDP Portal Backend is configured to trust (ThunderID locally, Asgardeo elsewhere, potentially others later), and hardcoding would silently break every environment except the one baked in.
- Configure everything via environment variables only, no profile file. Considered - env vars already work as flag defaults today, but they don't let an operator save and switch between several named environments without re-exporting every variable each time.
Problem
Operators of OpenNDX (Portal Backend) have no way to manage members, schemas, applications, and application policies except by hand-crafting authenticated HTTP calls against Portal Backend's API - there's no CLI, and no browser-based operator login.
On top of that gap, a first cut at such a CLI still forces an operator to retype a full set of identity-provider flags (
--auth-url,--token-url,--client-id,--scopes,--callback-port) on everylogin, even though:authorization_endpoint/token_endpointvia a standard discovery document, so only a base issuer URL should be needed.Proposed Solution
Add
ndx, a Go CLI for OpenNDX management operations against Portal Backend:cmd/cli- entry point wiringndx login,ndx members create,ndx schemas create,ndx applications create/list/get, andndx policy update.internal/cli/auth- browser-based OAuth2 Authorization Code + PKCE login (RFC 8252), local token cache with refresh, and OIDC Discovery support: an--issuer/NDX_ISSUERflag that derives--auth-url/--token-urlfrom{issuer}/.well-known/openid-configuration, so standards-compliant IDPs only need a base URL (explicit--auth-url/--token-urlstill work and take precedence for IDPs that don't serve discovery at the standard path).internal/cli/pbclient- a Portal Backend API client for the management operations above, reusinginternal/pb/v1/modelstypes.internal/cli/profile- named profiles (ndx profile list/use/set) bundling issuer, client ID, scopes, callback port, Portal Backend URL, and TLS verification per environment, with a built-inlocalprofile matching this repo's ThunderID docker-compose stack sondx loginworks with zero flags out of the box. Any explicit flag still overrides the active profile;--profile/NDX_PROFILEselects a profile for a single invocation without changing the default. Each non-default profile gets its own cached-token file so switching profiles can't pick up a token cached against a different identity provider.thunderid/bootstrap/*.yaml- registers theNDX_CLIOAuth2 client, anndx-adminoperator user, and theOpenNDX_Adminrole/group needed forndx loginto work end-to-end against the local ThunderID stack.Alternatives