Summary
PUT /config/router is not idempotent when it receives the canonical document returned by GET /config/router.
The Dashboard configuration path does not show this behavior: it reads and writes CanonicalConfig directly through /api/router/config/all and /api/router/config/update. The management API instead parses the submitted canonical document and exports it again through CanonicalConfigFromRouterConfig, which prefixes canonical backend reference names a second time.
Reproduction
Start with a valid v0.3 provider model whose canonical backend reference is:
providers:
models:
- name: test-model
backend_refs:
- name: test-model_primary
endpoint: localhost:8000
protocol: http
- Read the active document with
GET /config/router.
- Submit that response unchanged with
PUT /config/router.
- Read the document again.
Actual result
The backend reference name becomes:
name: test-model_test-model_primary
Repeating the same round trip adds another prefix.
Expected result
Submitting the canonical document returned by GET /config/router should not change it.
Root cause
canonicalEndpointName always returns modelName + "_" + backendRef.Name, even when backendRef.Name already starts with the generated model prefix. The management update path invokes CanonicalConfigFromRouterConfig after parsing the submitted document.
The latent prefix behavior was introduced by #1553, and the management update normalization path was introduced by #1660. The issue is present in v0.3.0 and current main.
Impact
- External control planes cannot safely perform read-modify-write updates.
- Configuration drift checks report false mismatches.
- Rollback code that republishes a previously read document can mutate it again.
- Repeated retries can keep extending backend reference names.
Proposed fix
Make canonical endpoint-name generation idempotent when the backend reference already contains the model prefix, and add regression coverage for:
- canonical export of an already-canonical backend reference;
- an unchanged
GET /config/router → PUT /config/router round trip;
- repeated round trips remaining stable.
Summary
PUT /config/routeris not idempotent when it receives the canonical document returned byGET /config/router.The Dashboard configuration path does not show this behavior: it reads and writes
CanonicalConfigdirectly through/api/router/config/alland/api/router/config/update. The management API instead parses the submitted canonical document and exports it again throughCanonicalConfigFromRouterConfig, which prefixes canonical backend reference names a second time.Reproduction
Start with a valid v0.3 provider model whose canonical backend reference is:
GET /config/router.PUT /config/router.Actual result
The backend reference name becomes:
Repeating the same round trip adds another prefix.
Expected result
Submitting the canonical document returned by
GET /config/routershould not change it.Root cause
canonicalEndpointNamealways returnsmodelName + "_" + backendRef.Name, even whenbackendRef.Namealready starts with the generated model prefix. The management update path invokesCanonicalConfigFromRouterConfigafter parsing the submitted document.The latent prefix behavior was introduced by #1553, and the management update normalization path was introduced by #1660. The issue is present in v0.3.0 and current main.
Impact
Proposed fix
Make canonical endpoint-name generation idempotent when the backend reference already contains the model prefix, and add regression coverage for:
GET /config/router→PUT /config/routerround trip;