Refactor Platform API + AI Workspace configuration and update documentation#2759
Refactor Platform API + AI Workspace configuration and update documentation#2759Thushani-Jayasekera wants to merge 14 commits into
Conversation
- Updated environment variable prefixes in README and config files to use `APIP_CP_` for consistency. - Refactored configuration structure to group server settings under a new `server` section. - Enhanced authentication configuration to select modes via `auth.mode`, supporting JWT, file-based, and IDP modes. - Updated tests to reflect changes in configuration structure and authentication handling. - Removed legacy variable names and clarified documentation for environment variable usage. - Adjusted default values and added environment variable fallbacks in the config template.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughPlatform API configuration is moved under ChangesConfiguration and startup migration
Artifact quota removal
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
platform-api/README.md (1)
262-278: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winRemove the stale
skip_validationdocs from Local JWT Mode.
skip_validationis no longer plumbed through config, and the server hardcodesSkipValidation: false. The README still advertisesauth.jwt.skip_validation,APIP_CP_AUTH_JWT_SKIP_VALIDATION, and theskip_validation = trueexample, which operators can’t actually use. Please delete those references.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@platform-api/README.md` around lines 262 - 278, Remove the stale skip_validation documentation from the Local JWT Mode section: delete the auth.jwt.skip_validation explanation, the APIP_CP_AUTH_JWT_SKIP_VALIDATION table entry, and the skip_validation=true example. Preserve the remaining JWT secret and issuer documentation and local startup instructions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@platform-api/config/config.toml`:
- Around line 15-18: Update the configuration header comment to reference the
namespaced platform_api tables for auth mode and file-based organization/users
settings, matching the sections consumed by the loader; change only the stale
section names while preserving the existing guidance and config-template
reference.
---
Outside diff comments:
In `@platform-api/README.md`:
- Around line 262-278: Remove the stale skip_validation documentation from the
Local JWT Mode section: delete the auth.jwt.skip_validation explanation, the
APIP_CP_AUTH_JWT_SKIP_VALIDATION table entry, and the skip_validation=true
example. Preserve the remaining JWT secret and issuer documentation and local
startup instructions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 56c3ad68-011c-48b9-b10e-3be164cd53e1
📒 Files selected for processing (26)
platform-api/README.mdplatform-api/cmd/main.goplatform-api/config/config-template.tomlplatform-api/config/config.goplatform-api/config/config.tomlplatform-api/config/config_test.goplatform-api/config/default_config.goplatform-api/internal/apperror/catalog.goplatform-api/internal/apperror/codes.goplatform-api/internal/constants/constants.goplatform-api/internal/handler/auth_login.goplatform-api/internal/handler/llm_provider_integration_test.goplatform-api/internal/server/server.goplatform-api/internal/server/server_tls_test.goplatform-api/internal/service/llm.goplatform-api/internal/service/llm_test.goplatform-api/internal/service/mcp.goplatform-api/plugins/eventgateway/plugin.goplatform-api/plugins/eventgateway/service/webbroker_api.goplatform-api/plugins/eventgateway/service/websub_api.goportals/ai-workspace/configs/config-platform-api.tomlportals/ai-workspace/configs/config-template.tomlportals/developer-portal/configs/config-platform-api-template.tomlportals/developer-portal/it/configs/config-platform-api-it.tomlportals/developer-portal/scripts/setup.shtests/integration-e2e/platform-api-config.toml
💤 Files with no reviewable changes (8)
- portals/ai-workspace/configs/config-platform-api.toml
- portals/ai-workspace/configs/config-template.toml
- portals/developer-portal/configs/config-platform-api-template.toml
- platform-api/internal/service/mcp.go
- platform-api/plugins/eventgateway/service/webbroker_api.go
- platform-api/plugins/eventgateway/service/websub_api.go
- platform-api/internal/service/llm.go
- platform-api/internal/service/llm_test.go
- Changed default authentication mode from "jwt" to "external_token" in configuration files and documentation. - Updated README and config-template.toml to reflect the new authentication structure and usage of environment variables. - Enhanced comments in config files to clarify the purpose of each authentication mode. - Adjusted test cases to align with the new authentication mode and configuration changes. - Ensured consistency across various configuration files and documentation regarding authentication settings.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@platform-api/config/config.go`:
- Around line 105-130: Replace HMAC-based JWT configuration and validation with
asymmetric public/private key material, explicitly rejecting symmetric and
unsigned algorithms throughout the external_token and file authentication flows.
In platform-api/config/config.go lines 105-130, redesign Auth/JWT to reference
asymmetric keys; update platform-api/config/config.go lines 537-544 to validate
the required key material and matching pair; revise
platform-api/config/config_test.go lines 160-165 for the new requirements;
update platform-api/README.md lines 276-283 and
portals/developer-portal/README.md lines 232-235 to document asymmetric key
configuration and remove shared-secret guidance, using the required approved
signature primitive where applicable.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 314e1989-42b6-436a-ae58-ecc63a41b2e5
⛔ Files ignored due to path filters (1)
go.work.sumis excluded by!**/*.sum
📒 Files selected for processing (18)
platform-api/README.mdplatform-api/config/config-template.tomlplatform-api/config/config.goplatform-api/config/config.tomlplatform-api/config/config_test.goplatform-api/config/default_config.goplatform-api/internal/server/server.goplatform-api/internal/service/llm.goportals/ai-workspace/Makefileportals/ai-workspace/README.mdportals/ai-workspace/docker-compose.yamlportals/ai-workspace/production/README.mdportals/developer-portal/.gitignoreportals/developer-portal/Makefileportals/developer-portal/README.mdportals/developer-portal/docker-compose.platform-api.yamlportals/developer-portal/docker-compose.yamlportals/developer-portal/scripts/setup.sh
💤 Files with no reviewable changes (3)
- portals/developer-portal/.gitignore
- platform-api/internal/service/llm.go
- portals/developer-portal/scripts/setup.sh
🚧 Files skipped from review as they are similar to previous changes (4)
- platform-api/config/config.toml
- platform-api/config/default_config.go
- platform-api/config/config-template.toml
- platform-api/internal/server/server.go
|
@Piumal1999 [tls].ca_file --> Keep devportal-only (used for optional mTLS in server.js). NOT added to platform-api — no mTLS on its listener; add later if mTLS lands Replace all three enabled flags with single [auth].mode = "file" | "idp". Comment: modes are mutually exclusive by design — the current code needs validateAuthModeExclusivity purely to reject both-enabled configs. A discriminated union (mode selector + one section per mode) makes that invalid state inexpressible, same pattern as database.driver. Industry precedent: Harbor auth_mode (database|ldap_auth|oidc_auth), Vault storage stanzas. Per-provider enabled flags (Grafana, k8s API server) are for CONCURRENT providers — not our semantics platform-api: enabled flag removed — active when auth.mode = "idp"; section stays at [auth.idp] (NOT moved to top-level [idp]). Comment: devportal's [idp] is OAuth relying-party client config (client_id, callbacks, silent SSO) — a different thing from platform-api's token-verification config; forcing one shape was surface-level unification. Claim-mapping key NAMES still align (that's where consistency pays off) |
…st.go - Added new dependencies for aws/smithy-go, coreos/go-oidc/v3, prometheus/otlptranslator, and google.golang.org/genproto. - Changed the configuration format in harness_test.go to reflect the new structure for platform API settings, ensuring proper organization of encryption and authentication parameters.
- Added detailed explanations for the local-development configuration in README.md, including usage of `config/config.toml` and authentication modes. - Streamlined comments in `config/config.toml` to clarify the purpose of various settings, particularly around authentication and database configuration. - Ensured consistency in documentation to support quickstart setups for AI Workspace and Developer Portal.
- Consolidated all AI Workspace settings under a single top-level `[ai_workspace]` table to align with the Platform API's configuration structure, preventing key collisions in shared config files. - Updated related documentation in `configuration.md`, `asgardeo-setup.md`, and `oidc-auth.md` to reflect the new namespacing and clarify the usage of environment variables. - Adjusted test cases to ensure compatibility with the new configuration format, including changes in the expected paths for OIDC and platform API settings. - Enhanced comments in `config-template.toml` and `README.md` to provide clearer guidance on configuration options and their defaults.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/ai-workspace/authentication/asgardeo-setup.md (1)
119-137: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winObsolete Platform API configuration structure in documentation.
The Platform API configuration was significantly refactored to use a
[platform_api]root table, a[platform_api.server.https]listener section, and a mutually exclusivemodediscriminator for authentication (e.g.,mode = "idp"), which replaces the individual[auth.*] enabled = ...flags. However, several AI Workspace documentation files were not updated and still show the obsolete schema for the Platform API.Please update the following locations to reflect the new
[platform_api]root table andmodeconfiguration:
docs/ai-workspace/authentication/asgardeo-setup.md#L119-L137: Update the TOML snippet to use[platform_api.auth]andmode = "idp".docs/ai-workspace/authentication/oidc-auth.md#L76-L116: Update the TOML snippets to use[platform_api.auth]andmode = "idp".docs/ai-workspace/configuration.md#L142-L160: Update the TOML snippet to use[platform_api.auth]andmode = "idp".portals/ai-workspace/distribution/README.md#L96-L101: Update the table to list the namespaced keys (e.g.,[platform_api.database].driver,[platform_api.server.https], and[platform_api.auth.idp]).portals/ai-workspace/distribution/README.md#L135-L135: Revise the instructions that tell users to setenabled = trueand[auth.file_based].enabled = false.portals/ai-workspace/production/README.md#L115-L124: Update the TOML snippet from[auth.idp]to[platform_api.auth.idp].portals/ai-workspace/README.md#L307-L309: Revise the text mentioningAPIP_CP_AUTH_JWT_ENABLED=falseandAPIP_CP_AUTH_FILE_BASED_ENABLED=false.portals/ai-workspace/README.md#L319-L337: Update the TOML snippet to use[platform_api.auth]andmode = "idp".portals/ai-workspace/README.md#L371-L374: Revise the troubleshooting table row that referencesauth.jwt.enabledandAPIP_CP_AUTH_JWT_ENABLED=false.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/ai-workspace/authentication/asgardeo-setup.md` around lines 119 - 137, Replace the obsolete Platform API authentication documentation with the new [platform_api] schema: use [platform_api.auth] with mode = "idp" and namespace nested settings under [platform_api.auth.idp], [platform_api.database], and [platform_api.server.https] as applicable. Apply this consistently in docs/ai-workspace/authentication/asgardeo-setup.md (119-137), docs/ai-workspace/authentication/oidc-auth.md (76-116), docs/ai-workspace/configuration.md (142-160), portals/ai-workspace/distribution/README.md (96-101 and 135), portals/ai-workspace/production/README.md (115-124), and portals/ai-workspace/README.md (307-309, 319-337, and 371-374); revise legacy enabled-flag and environment-variable references to describe the mode-based configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@platform-api/internal/integration/harness_test.go`:
- Line 53: Update the generated configuration in the harness test’s fmt.Fprintf
call to remove the obsolete auth.jwt enabled flag and explicitly set the
mutually exclusive [platform_api.auth] mode selector to the intended JWT mode,
while preserving the existing secret_key and encryption_key values.
---
Outside diff comments:
In `@docs/ai-workspace/authentication/asgardeo-setup.md`:
- Around line 119-137: Replace the obsolete Platform API authentication
documentation with the new [platform_api] schema: use [platform_api.auth] with
mode = "idp" and namespace nested settings under [platform_api.auth.idp],
[platform_api.database], and [platform_api.server.https] as applicable. Apply
this consistently in docs/ai-workspace/authentication/asgardeo-setup.md
(119-137), docs/ai-workspace/authentication/oidc-auth.md (76-116),
docs/ai-workspace/configuration.md (142-160),
portals/ai-workspace/distribution/README.md (96-101 and 135),
portals/ai-workspace/production/README.md (115-124), and
portals/ai-workspace/README.md (307-309, 319-337, and 371-374); revise legacy
enabled-flag and environment-variable references to describe the mode-based
configuration.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 4a8627bd-da83-4a57-8d55-a6228a6a8b35
⛔ Files ignored due to path filters (1)
go.work.sumis excluded by!**/*.sum
📒 Files selected for processing (14)
docs/ai-workspace/authentication/asgardeo-setup.mddocs/ai-workspace/authentication/oidc-auth.mddocs/ai-workspace/configuration.mdplatform-api/README.mdplatform-api/config/config.tomlplatform-api/internal/integration/harness_test.goportals/ai-workspace/README.mdportals/ai-workspace/bff/internal/config/config_test.goportals/ai-workspace/bff/internal/config/settings.goportals/ai-workspace/bff/internal/config/shipped_config_test.goportals/ai-workspace/configs/config-template.tomlportals/ai-workspace/configs/config.tomlportals/ai-workspace/distribution/README.mdportals/ai-workspace/production/README.md
🚧 Files skipped from review as they are similar to previous changes (2)
- platform-api/config/config.toml
- platform-api/README.md
- Added a step to build the platform-api image in the CI workflow for integration tests. - Updated the environment variable for the platform API image in the developer portal's Docker Compose files to allow for using a freshly built image. - Modified the configuration in `harness_test.go` to change the authentication mode to "external_token" for better integration with the updated API settings.
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
platform-api/internal/server/server.go (1)
550-559: 🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy liftReject symmetric JWT signature algorithms.
As per coding guidelines, you must allow only asymmetric signing methods (such as RSA-PSS or EdDSA) for JWT signature verification and explicitly reject symmetric algorithms (like HMAC) and
none. The current logic explicitly enables HMAC validation and relies on a sharedSecretKey.Please update the authentication configuration and
LocalJWTAuthMiddlewareto enforce an asymmetric algorithm and verify tokens using a public key instead of a shared secret.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@platform-api/internal/server/server.go` around lines 550 - 559, Update the non-IDP authentication setup in the server flow and LocalJWTAuthMiddleware configuration to reject HMAC and none algorithms, allow only the approved asymmetric JWT signing methods, and validate tokens with the configured public key rather than Auth.JWT.SecretKey. Preserve issuer and skip-path settings while ensuring unsupported algorithms are explicitly rejected.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@platform-api/internal/server/server.go`:
- Around line 550-559: Update the non-IDP authentication setup in the server
flow and LocalJWTAuthMiddleware configuration to reject HMAC and none
algorithms, allow only the approved asymmetric JWT signing methods, and validate
tokens with the configured public key rather than Auth.JWT.SecretKey. Preserve
issuer and skip-path settings while ensuring unsupported algorithms are
explicitly rejected.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a39ecc04-8d17-4ef1-be83-8ea05ef367c2
📒 Files selected for processing (31)
docs/ai-workspace/authentication/asgardeo-setup.mddocs/ai-workspace/authentication/oidc-auth.mddocs/ai-workspace/configuration.mdplatform-api/README.mdplatform-api/config/config-template.tomlplatform-api/config/config.goplatform-api/config/default_config.goplatform-api/internal/middleware/auth.goplatform-api/internal/middleware/auth_role_extraction_test.goplatform-api/internal/server/server.goplatform-api/resources/roles.yamlportals/ai-workspace/Makefileportals/ai-workspace/README.mdportals/ai-workspace/VERSIONportals/ai-workspace/bff/internal/config/config.goportals/ai-workspace/bff/internal/config/config_test.goportals/ai-workspace/bff/internal/config/runtime_config.goportals/ai-workspace/bff/internal/config/settings.goportals/ai-workspace/bff/internal/config/shipped_config_test.goportals/ai-workspace/bff/internal/config/toml_test.goportals/ai-workspace/bff/internal/proxy/transport.goportals/ai-workspace/bff/internal/server/composite_handlers.goportals/ai-workspace/bff/internal/server/composite_handlers_test.goportals/ai-workspace/bff/internal/server/server.goportals/ai-workspace/bff/main.goportals/ai-workspace/configs/config-template.tomlportals/ai-workspace/configs/config.tomlportals/ai-workspace/distribution/README.mdportals/ai-workspace/production/README.mdportals/ai-workspace/src/config.env.tsportals/ai-workspace/vite.config.ts
🚧 Files skipped from review as they are similar to previous changes (9)
- docs/ai-workspace/authentication/oidc-auth.md
- portals/ai-workspace/production/README.md
- portals/ai-workspace/distribution/README.md
- platform-api/config/config-template.toml
- platform-api/config/default_config.go
- platform-api/README.md
- portals/ai-workspace/bff/internal/config/settings.go
- portals/ai-workspace/bff/internal/config/config_test.go
- platform-api/config/config.go
…e with JWKS URL and issuer
| # Custom header required on state-mutating requests (CSRF protection). | ||
| csrf_header = '{{ env "APIP_AIW_CSRF_HEADER" "X-Requested-By" }}' | ||
| # Port the server listens on. | ||
| port = '{{ env "APIP_AIW_SERVER_HTTPS_PORT" "5380" }}' |
There was a problem hiding this comment.
Shall we move port to [ai_workspace.server] because it applies to both http and https?
Relates to: #2730
Fix Platform API and AI Workspace configuration structure
Summary
This PR is a significant restructuring of the Platform API configuration system and its downstream consumers (AI Workspace BFF, Developer Portal, integration tests, and CI). It fixes key-collision and ergonomics problems in the previous flat config layout, standardizes environment variable prefixes, changes the default authentication mode, and removes a half-finished artifact-limits feature that was never wired up to configuration.
Breaking changes
Compared to
upstream/main, existingconfig.toml/config-template.tomlfiles and env vars for Platform API and AI Workspace need migration:auth.jwt.enabled/auth.idp.enabled/auth.file_based.enabledflags are gone, replaced by a singleauth.modeselector (external_token|file|idp). The effective default flips from JWT-with-skip_validation=true(i.e. auth effectively disabled) toexternal_token(signature validation always on).auth.jwt.skip_validationremoved — JWT signature validation can no longer be bypassed via config.auth.jwt.secret_key(a single shared 32-byte HMAC secret) is replaced by an RSA key pair:auth.jwt.public_key(PEM RSA public key — required in every mode, verifies signatures) andauth.jwt.private_key(PEM RSA private key — required only infilemode, signs login tokens; must match the public key). The Platform API now rejects HMAC (HS256/384/512) and unsigned (none) tokens outright, closing the public-key-as-HMAC-secret forgery class (seeauthentication_authorization.mdGO-AUTH-002). Because PEM keys are multi-line they cannot live in an env-file value: config reads them via{{ file }}(e.g.public_key = '{{ file "/etc/platform-api/keys/jwt_public.pem" }}'), andscripts/setup.shgenerates the pair as PEM files rather than writing an env var. Env varsAPIP_CP_AUTH_JWT_SECRET_KEY/APIP_DP_PLATFORMAPI_JWTSECRETare gone.auth.file_basedrenamed toauth.file, and itsenabledflag removed (selection is viaauth.mode=file).[[auth.file_based.users]]→[[auth.file.users]].auth.idp.claim_mappingsmoved and renamed toauth.claim_mappings(now shared across all three auth modes instead of IDP-only). Field names changed:organization_claim_name→organization,org_name_claim_name→org_name,org_handle_claim_name→org_handle,user_id_claim_name→user_id,username_claim_name→username,email_claim_name→email,scope_claim_name→scope,roles_claim_path→roles(now underclaim_mappings, notidp).auth.idp.role_mappings_filerenamed toauth.idp.role_mappings.enable_scope_validationmoved and renamed toauth.scope_validation.log_level/log_formatmoved and renamed tologging.level/logging.formatunderserver(matched case-insensitively, lowercase canonical; env varsAPIP_CP_LOGGING_LEVEL/APIP_CP_LOGGING_FORMAT).http/httpslisteners moved underserver.http/server.https. Listenerportfields changed type from string to int (e.g.port = "9243"→port = 9243).https.cert_dirreplaced byserver.https.cert_file+server.https.key_file(was a directory expected to containcert.pem/key.pem; now two explicit file paths directly under[server.https]).timeouts,cors,websocketmoved from the config root underserver.timeouts,server.cors,server.websocket.websocket.max_connections_per_orgremoved — no longer configurable/enforced.webhook.gateway_typeremoved — no longer configurable.default_devportalconfig block removed entirely — no replacement.artifact_limitsconfig block removed entirely, along with all enforcement of per-organization LLM provider/proxy, MCP proxy, and WebSub/WebBroker API count limits. Any deployment relying on these limits (even though they defaulted to unlimited) has no config-based way to reintroduce them.APIP_CP_— legacy/inconsistent env var names for Platform API config are no longer recognized.[ai_workspace]table — any existing AI Workspaceconfig.tomlwith settings at the root (or under previous per-feature tables) needs to move under[ai_workspace].apperrorcodesLLM_PROVIDER_LIMIT_REACHED,LLM_PROXY_LIMIT_REACHED,MCP_PROXY_LIMIT_REACHED,WEBSUB_API_LIMIT_REACHED,WEBBROKER_API_LIMIT_REACHED— any client code matching on these error codes will no longer see them.scripts/setup.shno longer generatesconfigs/config-platform-api.toml— deployments relying on that generated file must switch toconfigs/config-platform-api-template.tomlwith the newplatform_api.*/platform_api.auth.file.*structure.Platform API configuration refactor
serversection: server-level settings (listeners, timeouts, deployments, etc.) are now grouped under aservertable instead of living at the config root, avoiding collisions with sibling apps that share a config file.auth.mode-driven authentication: replaced the old per-mechanismenabledflags (auth.jwt.enabled,auth.idp.enabled,auth.file_based.enabled) with a single explicitauth.modeselector (jwt|file|idp/external_token), so exactly one mode is active at a time instead of relying on flag combinations.jwttoexternal_token.auth.file_basedrenamed toauth.file.JWT.SecretKey→JWT.PublicKey+JWT.PrivateKey(both PEM RSA).validateJWTConfigtakes arequireSigningKeyflag — verify-onlyexternal_tokenmode needs just the public key;filemode additionally requires the private key and checks the pair matches (priv.PublicKey.Equal(pub)).internal/middleware/auth.gonow verifies withjwt.WithValidMethods(["RS256","RS384","RS512"])and rejects any non-RSA method (blocksnoneand the public-key-as-HMAC forgery);internal/handler/auth_login.gosigns login tokens withSigningMethodRS256.internal/server/server.goparses the PEM public key once at startup and passes an*rsa.PublicKeyinto the middleware. Added aTODO(pqc)on theJWTstruct — RS256 is quantum-vulnerable and should move to ML-DSA (FIPS 204) once a Go JWT library exposes it.IDPClaimMappingsfields renamed (OrganizationClaimName→Organization,OrgNameClaimName→OrgName,OrgHandleClaimName→OrgHandle,UserIDClaimName→UserID,UsernameClaimName→Username,EmailClaimName→Email,ScopeClaimName→Scope) and support dot-separated nested claim paths, not just top-level claims —internal/middleware/auth.go'sextractClaimByPath/getStringClaimnow resolve arbitrary nested claim paths (resolveClaimPath) instead of a single-level map lookup.EnableScopeValidationmoved intoAuth.ScopeValidation.server.http/server.https(ServerListeners), TLS cert/key paths now explicitcert_file/key_file(fieldsHTTPSListener.CertFile/KeyFile, directly on the HTTPS listener — no nestedtlssub-table) instead of aCertDir, and listener ports are now typed as integers instead of strings. TheLoggingstruct fields areLevel/Format(keyslevel/format).APIP_CP_across README, config template, and config loader — legacy variable names removed.DefaultDevPortalconfig block removed — dead/unused config surface.ArtifactLimitsconfig,config.LimitReached, and all per-organization creation-limit enforcement (LLM providers, LLM proxies, MCP proxies, WebSub APIs, WebBroker APIs) have been deleted frominternal/service/llm.go,internal/service/mcp.go, and the event-gateway plugin, along with their associatedapperrorcodes/catalog entries (*LimitReached) and tests. This functionality was never actually configurable (limits were unlimited by default and no config surface existed to set them), so it was removed rather than fixed.internal/constants/constants.go: removed a stale comment referencing the deleted artifact-limits mechanism; formatting fix inPolicyManagedBy*constants.resources/roles.yaml: updated to match the new auth/scopes structure.internal/middleware/auth_role_extraction_test.gocovering nested claim path extraction.internal/server/server.go,cmd/main.go: updated to build listeners/auth from the new config shape.AI Workspace (BFF + portal) configuration refactor
[ai_workspace]table inconfig-template.toml/config.toml, mirroring the Platform API's nested structure and preventing key collisions when the AI Workspace and Platform API share a config file.bff/internal/config/:config.go(nestedConfigstruct +Load/normalize/validate), a newdefault_config.go(defaultConfig()),settings.go(now just the koanf loaderloadConfigKoanf), andruntime_config.go(browser-safe allowlist read from the koanf instance).providers/file+parsers/toml/v2+providers/confmap), matching the Gateway and Platform API; the hand-rolled stdlib TOML-subset parser (toml.go+ its test) and thesettings-map/get*facade were removed.config.gois now shaped like the Platform API's: a nestedkoanf-taggedConfigmirroring the[ai_workspace.*]tables, withdefaultConfig()→Unmarshal→normalize()→validate(). Logging keys renamed to[ai_workspace.logging] level/format, the listener moved to[ai_workspace.server.https]with an integerportand inlinecert_file/key_file. See the cross-product review §3, §5, §7 below.portals/ai-workspace/src/config.env.ts,vite.config.ts: adjusted for the new config shape.docs/ai-workspace/configuration.md,docs/ai-workspace/authentication/{README,asgardeo-setup,file-based-auth,oidc-auth}.md,portals/ai-workspace/README.md,portals/ai-workspace/distribution/README.md, andportals/ai-workspace/production/README.mdto reflect the new namespacing and env var usage.portals/ai-workspace/Makefile,docker-compose.yaml: updated for the new config/env layout.Developer Portal integration
configs/config-platform-api.tomlprovisioning step fromscripts/setup.sh(previously hand-rolled a Platform API config with hardcoded admin scopes and legacy[auth.jwt]/[auth.idp]/[auth.file_based]keys) in favor of the newconfig-platform-api-template.toml, which now uses the nestedplatform_api.*/platform_api.auth.file.*structure.scripts/setup.shnow provisions the RS256 JWT keypair (openssl genpkey→resources/keys/jwt_private.pem+jwt_public.pem, idempotent,644like the TLS cert) instead of generating a shared HMAC secret intoapi-platform.env.docker-compose.yamlmountsresources/keysinto the platform-api container at/etc/platform-api/keys(on its{{ file }}allowlist), andplatform-api/config/config.tomlreads the pair viapublic_key/private_key = '{{ file "/etc/platform-api/keys/jwt_*.pem" }}'.docker-compose.platform-api.yaml,docker-compose.yaml,it/configs/config-platform-api-it.toml,it/docker-compose.test.{postgres,yaml}: updated to the new config structure and to support pointing at a freshly builtplatform-apiimage viaPLATFORM_API_IMAGE..gitignore: removed now-unnecessary entry for the deleted generated config file.CI
.github/workflows/devportal-integration-test.yml: added a step to build theplatform-apiimage before running Developer Portal REST API and Cypress integration tests, and wiredPLATFORM_API_IMAGEthrough so the tests exercise the freshly built image instead of a stale/pulled one.internal/integration/harness_test.go,tests/integration-e2e/platform-api-config.toml: test fixtures updated to the new nested config format (including switching toexternal_tokenauth mode).go.work.sum: new transitive dependencies pulled in (aws/smithy-go,coreos/go-oidc/v3,prometheus/otlptranslator,google.golang.org/genproto).Documentation
platform-api/README.md: substantially rewritten — documents the newauth.modeselector, local-development quickstart usingconfig/config.toml, and theAPIP_CP_env var convention.platform-api/config/config.toml,config-template.toml: comments clarified and restructured to match the new nested layout.Testing
config/config_test.go,bff/internal/config/config_test.go,bff/internal/config/shipped_config_test.go,internal/server/server_tls_test.go,internal/handler/llm_provider_integration_test.go,bff/internal/server/composite_handlers_test.goupdated for the new config shape and the removal of artifact limits.bff/internal/config/toml_test.godeleted along with the hand-rolledtoml.goparser it covered (AI Workspace now parses TOML via koanf — see the cross-product review §7).Notes for reviewers
jwt→external_token) and theauth.file_based→auth.filerename are breaking changes for anyone with an existing config file — worth confirming migration guidance is adequate in the updated READMEs.Cross-Product Configuration Consistency Review
The API Platform ships four separately-deployable products that are meant to be wired together into one system:
gateway-controller+gateway-runtime/policy-engine+router(share oneconfig.toml), plus the build-timegateway-builder.This section reviews how consistent their configuration systems are with each other, and where the biggest wins are for making them feel like one platform. It is descriptive of the current state of the
fix-configsbranch — the Platform API / AI Workspace refactor above already closes several of the gaps that used to exist (e.g. single-table namespacing, int ports, logging/TLS key alignment).Overall consistency scorecard
config.toml+config-template.tomlpatternsmol-toml+ JS loaderAPIP_GW_APIP_CP_APIP_AIW_APIP_DP_{{ env }}{{ env }}tokens only{{ env }}tokens only{{ env }}tokens only{{ env }}/{{ file }}){{ file }}allowlist (/etc/<p>,/secrets/<p>, 1 MiB,APIP_CONFIG_FILE_SOURCE_ALLOWLIST)[platform_api][ai_workspace]level/formatlevel/formatlevel/formatconsole_only(+LOG_LEVELenv)enabledflagsmodeselectormodeselectorclientId)cert_path/key_pathcert_file/key_filecert_file/key_filecert_file/key_filetype,path,user,sslmodedriver,path,user,ssl_modetype,file,username,ssl.*user)Bottom line: the products are strongly consistent on the outer contract (TOML, the template pattern, the
{{ env }}/{{ file }}interpolation and its allowlist/size rules, theAPIP_<product>_prefix family, and fail-closed required secrets) but inconsistent on the inner details — the env-override model, top-level namespacing, and the actual key names for logging, auth, TLS, and database.What is consistent (keep it this way)
config.tomlplus a fully-documentedconfig-template.tomlwhose defaults double as reference. This is a genuinely good shared idiom.{{ env "VAR" "default" }}and{{ file "/path" }}behave identically everywhere: a defaultless{{ env }}and any{{ file }}fail closed at startup; the file allowlist defaults to/etc/<product>and/secrets/<product>, caps reads at 1 MiB, guards against traversal/null-byte/symlink-TOCTOU, and is overridable via the single sharedAPIP_CONFIG_FILE_SOURCE_ALLOWLIST. The three Go services literally sharecommon/configinterpolate; the Dev Portal re-implements the same contract in JS. Resolved secret values are never logged in any product.APIP_GW_/APIP_CP_/APIP_AIW_/APIP_DP_all follow oneAPIP_<product>_shape.{{ env }}/{{ file }}with no insecure default.organization,org_name,org_handle,username,email,scope,roles), which is exactly right given the BFF reads back the HMAC JWT the Platform API signs in file/basic mode.Where they diverge (and where to improve)
1. Env-override model — the sharpest inconsistency
The Gateway (koanf
env.Provider) has an automatic overlay: anyAPIP_GW_CONTROLLER_LOGGING_LEVELmaps tocontroller.logging.levelwith no token needed in the file. The other three have no automatic overlay — an env var only takes effect where the TOML explicitly writes{{ env "NAME" }}. (The Dev Portal even removed its oldAPIP_DP_*auto-mapping on purpose.)Consequence:
APIP_GW_*"just works" against any key, whileAPIP_CP_*/APIP_AIW_*/APIP_DP_*only work for keys the template author remembered to tokenize. Two different mental models for operators of the same platform. A visible symptom:docker-compose.postgres.yamlsetsAPIP_DP_DATABASE_HOST/PORT/...but the shipped Dev Portalconfig.tomlonly referencesAPIP_DP_DATABASE_FILE, so those vars are silently inert. (The old symptom wherescripts/setup.shhad to write the same HMAC secret under two env names —APIP_CP_AUTH_JWT_SECRET_KEYandAPIP_DP_PLATFORMAPI_JWTSECRET— is now gone with the RS256 migration: signing material is an RSA PEM key pair provisioned as mounted files, so there is no shared secret to copy between services.)Improve: pick one model platform-wide. The token model is the more auditable/secure one (explicit, greppable, no accidental binding) and is already the majority — consider aligning the Gateway onto it (or at least documenting the Gateway's automatic overlay as a deliberate exception).
2. Top-level namespacing
Platform API (
[platform_api]) and AI Workspace ([ai_workspace]) now nest everything under one product table — specifically so they can safely share oneconfig.toml(the AI Workspace compose already mounts Platform API's ownconfig.toml). The Gateway spreads across many roots (controller,router,policy_engine,collector,analytics,tracing, …) and the Dev Portal uses bare roots (server,tls,logging,database,idp, …). If the intent is that connected products can co-habit a config file, the two un-namespaced products break that guarantee (e.g. both define a top-level[server]/logging).Improve: wrap Dev Portal under
[developer_portal]and (longer-term) the Gateway components under a[gateway]umbrella, matching the Platform API / AI Workspace precedent.3. Logging keys and level casing
Previously:
level/format(Gateway) vslog_level/log_format(Platform API, AI Workspace) vsconsole_onlywith the actual level coming from a bareLOG_LEVELenv var (Dev Portal). Level value casing also differed: Platform API expected UPPERCASE (INFO), the others lowercase (info).Done (this branch): Platform API and AI Workspace now use
[logging] level/format— matching the Gateway's existing key names — parsed case-insensitively with lowercase as canonical (info,text). The env-var tokens moved to the path convention (APIP_CP_LOGGING_LEVEL/APIP_CP_LOGGING_FORMAT,APIP_AIW_LOGGING_LEVEL/APIP_AIW_LOGGING_FORMAT). Platform API's default dropped fromINFO→info; its startup validator now reports thelogging.level/logging.formatkey names.Still open: the Dev Portal keeps
console_only+ the out-of-bandLOG_LEVELenv (left for a follow-up — giving it a reallogging.levelkey means wiring the Winston logger through the config loader).4. Auth mode selection and vocabulary
Four different selection models: Gateway uses
enabledflags, Platform API and AI Workspace use amodeselector, and the Dev Portal infers local-vs-OIDC from whetheridp.clientIdis empty. Worse, the twomode-based products use different words for the same pairing: Platform APIfile/idp/external_tokenvs AI Workspacebasic/oidc, even thoughfile↔basicandidp↔oidcare meant to line up. Claim-mapping keys also split: Platform API/AI Workspace share the full 7-key set, while the Dev Portal uses a different, smalleridp.claims(role,orgId,groups) and the Gateway management API usesroles_claim.Improve: adopt one
auth.modeselector everywhere with one shared vocabulary, and reconcile the Dev Portal / Gateway claim-mapping keys onto the Platform API set.5. TLS and server-listener keys
Previously: TLS cert/key were
cert_file/key_filein Platform API, AI Workspace, and Dev Portal, butcert_path/key_pathin the Gateway router. Listener shapes also differed: Platform APIserver.http/server.httpswithenabled+ intport(with cert/key under a nestedserver.https.tls); Gatewayrouterwithlistener_port/https_port/https_enabled; AI Workspace a singleserver.portstring.Done (this branch): AI Workspace now follows the platform listener shape —
[ai_workspace.server.https]withenabled+ an integerport(anintfield koanf unmarshals into, replacing the former string port). The nested.tlssubtable was dropped on both products:cert_file/key_filenow sit directly under[server.https](there is no ambiguity to disambiguate — an HTTPS listener implies TLS), so the shape is[server.https] enabled / port / cert_file / key_filefor both Platform API and AI Workspace.cert_file/key_file(notcert_path/key_path) is confirmed as the standard — it matches OpenSSL'sSSL_CERT_FILEand Go'stls.LoadX509KeyPair(certFile, keyFile). Env tokens follow suit (APIP_CP_SERVER_HTTPS_CERT_FILE,APIP_AIW_SERVER_HTTPS_PORT, …).Note on the flatten: the Gateway's own TLS blocks are not a single pattern —
[controller.policy_server.tls]uses a nested.tlssubtable withcert_file/key_file, while[router.downstream_tls]uses a separate table withcert_path/key_path. The flattened[server.https] cert_file/key_filehere deliberately favors simplicity (an HTTPS listener implies TLS) over mirroring the Gateway's nestedpolicy_server.tlsshape.Still open: the Gateway router (
cert_path/key_path,listener_port/https_port/https_enabled— an Envoy/xDS listener) is intentionally out of scope for this branch; aligning it touches the xDS translator, validation, and existing env vars.6. Database keys
The most scattered area. Selector:
type(Gateway, Dev Portal) vsdriver(Platform API). SQLite path:path(Gateway, Platform API) vsfile(Dev Portal). DB name:database(Gateway) vsname(Platform API, Dev Portal). SSL:sslmodestring vsssl_modestring vs a nestedssl.enabled+ssl.caFilebool object. The DB user key is nowuserin both Platform API and the Gateway (Platform API's earlierusernamerename was reverted —useris the DB-standard name, matching the PostgreSQL DSNuser=/PGUSER); the Dev Portal still usesusername.Improve: define one shared DB block schema (
driver,path,host,port,name,user,password,ssl_mode,ssl_root_cert) and adopt it across the three data-storing products.7. Config parser divergence
Previously: koanf v2 (Gateway, Platform API), a hand-rolled stdlib TOML-subset parser (AI Workspace BFF), and
smol-toml(Dev Portal).Done (this branch): the AI Workspace BFF now loads config through koanf v2 (
providers/file+parsers/toml/v2+providers/confmap), the same stack as the Gateway and Platform API, and itsconfig.gois now structured like the Platform API's: a nestedkoanf-taggedConfigwhose shape mirrors the[ai_workspace.*]TOML tables, populated by adefaultConfig()overlaid withUnmarshalWithConf(WeaklyTypedInput+ a duration decode hook), then anormalize()step for derived fields (Addr(), trimmed URLs,oidc-mode⇒enabled, cookie constants) and avalidate()step for fail-closed checks. The hand-rolled parser (toml.go) and the entiresettingsmap +get/getbool/getint/getdurfacade were removed; browser-only passthrough keys (domain,gateway.*,moesif_*, …) are read straight from the parsed koanf instance inbuildRuntimeConfig, preserving the file-present-only semantics of the SPA allowlist.{{ env }}/{{ file }}interpolation continues through the sharedconfiginterpolatelibrary. All three Go services now share one config library and the same load→default→unmarshal→validate shape; only the Dev Portal keeps a JS loader (smol-toml), which is unavoidable cross-language.Trade-off accepted: koanf's go-toml parser is more permissive than the removed subset parser — it silently accepts arrays, arrays-of-tables, dotted/quoted keys, and duplicate keys that the old parser refused with a line-numbered error. This drops a deliberate fail-closed-on-unknown-construct property in exchange for one shared library; the AI Workspace config uses none of those constructs, so no shipped config is affected.
8. Minor / latent
src/config.env.tsstill readsAPIP_AIW_OIDC_AUTHORITY/_CLIENT_ID/_REDIRECT_URI/_POST_LOGOUT_REDIRECT_URI, while the BFF and template use the consolidatedAPIP_AIW_AUTH_OIDC_*names. Since the BFF owns the OIDC handshake these SPA vars are likely vestigial, but they are a naming mismatch introduced/left on this branch — worth removing or renaming.APIP_CP_("control plane"), notAPIP_PA_/APIP_API_; harmless but a small surprise relative to the product name.Suggested priority order
Consolidate on koanf for all Go services (§7)— done on this branch (AI Workspace BFF migrated to koanf v2). Fix the SPA OIDC var names (§8) remains.