Skip to content

[Feature]: server-managed settings — control-plane fetch + local cache (managed tier, #454 follow-up) #463

Description

@initializ-mk

Context

The forge settings surface (#454) landed with the file-based managed mechanism: a fixed OS-path managed-settings.json (+ managed-settings.d/) loaded at the top of precedence, non-developer-overridable (forge-core/settings/layers.goloadManagedLayer, ManagedSettingsPath). Claude Code additionally supports a server-managed mechanism: "the servers/gateway hold the policy; the client keeps a local cache it applies at startup and replaces on each successful fetch." Forge should gain the same, because the initializ platform already has a control plane that injects policy server-side — server-managed settings are the natural client-side counterpart, letting an org manage the developer surface centrally without dropping files on every machine.

This is the last of the managed delivery mechanisms noted on #454 (the others — macOS configuration profile, Windows HKLM/HKCU registry — are separate follow-ups). Server-managed is the highest-value one here given the existing control plane.

Goal

Add a server-managed settings source in the managed tier: fetch the org's Settings JSON from a control-plane endpoint at startup, cache it locally, apply it at managed precedence, and replace the cache on each successful fetch. Non-developer-overridable, like the file-based managed layer.

Design

Reuse the existing platform-callout pattern. Forge already calls the control plane for admission (FORGE_ADMISSION_URL), the PDP, the remote session store, and the MCP platform token — all sending Org-Id + Workspace-Id (from FORGE_ORG_ID/FORGE_WORKSPACE_ID) + Authorization: Bearer ${FORGE_PLATFORM_TOKEN} + X-Workload-Token (#444). The settings fetch should use the same headers/contract (see forge-cli/runtime/admission_engine.go / forge-core/mcp/platform_token.go for the shape). Endpoint via a new env, e.g. FORGE_SETTINGS_URL (engaged only when set, mirroring admission's opt-in).

Response shape. The endpoint returns the same settings.Settings JSON (channels/models/tools/skills/env) — no new schema; the server-managed layer is just another source feeding the existing merge/lock machinery.

Loader integration (forge-core/settings):

  • Add the server-managed source to LoadAllLayers in the managed tier. Decide precedence within the managed tier vs the file-based managed-settings.json (Claude Code documents an explicit order — pick one, e.g. server-managed over file, or file over server; state it). A managed available_models lock from either must remain authoritative.
  • Cache: persist the last successful fetch to a local file (e.g. ~/.forge/managed-settings.cache.json, 0600). At startup: fetch → on success, apply + overwrite cache; on failure, fall back to the cached copy (last-known-good).

Failure semantics (decide + document). Because managed settings can carry a lock (models.available_models), a failed fetch with no cache must not silently widen what the org allows. Options to settle: (a) no server-managed layer when both fetch and cache are absent (consistent with file-absent) — simplest; (b) fail-closed / refuse to start when a fetch was expected (env set) but unavailable and uncached. Recommend (a) with a clear startup log, since the authoritative security enforcement is server-side policy anyway (settings are the developer surface).

Non-overridability. The endpoint + cache path are not developer-redirectable in production (same principle as the fixed file path — FORGE_SETTINGS_URL is an operator/deploy-time env like FORGE_ADMISSION_URL, not a per-developer override; the cache is 0600 and treated as managed-tier).

Acceptance

  • With FORGE_SETTINGS_URL set, forge fetches Settings from the control plane at startup (with Org-Id/Workspace-Id/Bearer/X-Workload-Token), applies it in the managed tier, and caches it.
  • On a subsequent failed fetch, the last-known-good cache is applied (not dropped).
  • Precedence within the managed tier (server vs file) is defined + documented; a managed available_models lock stays authoritative.
  • No-fetch-no-cache behavior is the documented default (recommend: no server-managed layer + startup log).
  • forge settings shows the server-managed layer (source = control plane) alongside the file layers.
  • Tests: fetch→apply→cache; failed-fetch→cache fallback; precedence/lock; header contract. Docs: settings.md managed-delivery section.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions