docs: spec agent config endpoint flip to flat JWT-scoped URL
Server team confirmed /api/v1/agents/config is the only AGENT-role config read route; the env-scoped URL from the 2026-04-16 design returns 403 in practice. This spec documents the agent-side flip, retains strict response env validation, and drops the now-unused application parameter from fetchApplicationConfig. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
# Agent Config Endpoint — Flat, JWT-Resolved
|
||||
|
||||
**Date:** 2026-04-17
|
||||
**Status:** Approved
|
||||
**Scope:** `cameleer-core`, `cameleer-common`
|
||||
**Supersedes:** The agent-side URL from `2026-04-16-env-scoped-config-url-agent-design.md` only. The env-scoped route family (`/api/v1/environments/{envSlug}/...`) remains for user-facing endpoints on the server; it simply is not how the agent fetches its own config.
|
||||
|
||||
## Problem
|
||||
|
||||
The 2026-04-16 landing moved the agent's config fetch to `GET /api/v1/environments/{envId}/apps/{app}/config`. In practice that route is 403 for the agent because the server's only `hasRole("AGENT")` config-read endpoint is flat and JWT-scoped (`SecurityConfig.java:127`). The env-scoped route is reserved for user-facing calls.
|
||||
|
||||
## Design
|
||||
|
||||
### Endpoint
|
||||
|
||||
```
|
||||
GET {baseUrl}/api/v1/agents/config
|
||||
```
|
||||
|
||||
Zero URL parameters. The server resolves `(application, environment)` from the agent's JWT subject → registry entry (heartbeat-authoritative), with the JWT env claim as fallback.
|
||||
|
||||
### `ServerConnection.fetchApplicationConfig`
|
||||
|
||||
Signature changes from `(String application)` to `()` — the parameter was only used to build the URL and is now redundant. Callers drop the argument.
|
||||
|
||||
New behavior:
|
||||
|
||||
```
|
||||
path = "/api/v1/agents/config"
|
||||
```
|
||||
|
||||
401/403 refresh-retry path (existing) targets the same URL.
|
||||
|
||||
Post-response checks are unchanged from the 2026-04-16 design:
|
||||
|
||||
1. HTTP status / 401+403 refresh path (existing).
|
||||
2. Deserialize envelope → `ApplicationConfig`.
|
||||
3. Merge `mergedSensitiveKeys` into `ApplicationConfig.sensitiveKeys` (existing).
|
||||
4. Version check: if `config.getVersion() == 0`, return `null` (no config stored).
|
||||
5. Env validation (version > 0 only): throw if `config.getEnvironment() == null` or does not equal `registeredEnvironmentId`.
|
||||
|
||||
The response envelope keeps `environment` populated when `version > 0`, so strict validation remains a cheap cross-check that the server's JWT→env resolution agrees with what the agent registered as.
|
||||
|
||||
### Fields and state
|
||||
|
||||
- `registeredEnvironmentId` is still populated by `register()` and still used for response validation and the heartbeat body. No change.
|
||||
- `ConfigCacheManager` keeps its per-application key. No change.
|
||||
- Heartbeat body keeps `environmentId`. No change.
|
||||
|
||||
### PROTOCOL.md
|
||||
|
||||
- Section 3 endpoint table: replace the `GET /api/v1/environments/{environmentId}/apps/{applicationId}/config` row with `GET /api/v1/agents/config` — "Fetch per-agent config (server resolves application and environment from the agent's JWT)".
|
||||
- "Config Endpoint Response Envelope" heading: update to the new URL.
|
||||
- The paragraph on strict env validation and the `version == 0` bypass stays as-is — the response envelope and validation behavior are unchanged.
|
||||
- SSE reconnection bullet that references the config URL: update to the flat URL.
|
||||
|
||||
## Error handling
|
||||
|
||||
Unchanged from 2026-04-16. Table applies verbatim:
|
||||
|
||||
| Case | Behavior |
|
||||
|---|---|
|
||||
| 401 / 403 | Refresh token, retry once. |
|
||||
| Non-200 after refresh | `RuntimeException("Config fetch failed: HTTP nnn")`. |
|
||||
| 200 + `version == 0` | Return `null`. Env not validated. |
|
||||
| 200 + `version > 0` + `environment == null` | Throw. |
|
||||
| 200 + `version > 0` + `environment != registeredEnvironmentId` | Throw. |
|
||||
| 200 + `version > 0` + env matches | Return config. |
|
||||
|
||||
## Testing
|
||||
|
||||
`cameleer-core/src/test/java/com/cameleer/core/connection/ServerConnectionTest.java`:
|
||||
|
||||
- **URL shape** — `ArgumentCaptor<HttpRequest>` asserts the request URI path is `/api/v1/agents/config` (no env/app segments).
|
||||
- **Strict mismatch rejection** — retained; response body still carries `environment`, and the agent still rejects a mismatch.
|
||||
- **Strict null rejection** — retained.
|
||||
- **Happy path env match** — retained.
|
||||
- All `fetchApplicationConfig(...)` call sites in tests drop the argument.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- No changes to `/api/v1/data/*` (unchanged, JWT-authoritative).
|
||||
- No changes to `/api/v1/agents/{instanceId}/*` (register, heartbeat, deregister, refresh, ack, SSE).
|
||||
- No removal of `registeredEnvironmentId` field or heartbeat env field.
|
||||
- No protocol-version bump.
|
||||
|
||||
## Rollout
|
||||
|
||||
Hard cut on main. Server's agent-config route is already live (prior env-scoped URL is 403), so this fixes the agent immediately on deploy.
|
||||
Reference in New Issue
Block a user