From 852a8203d04f2450466733bb7748617ff9e364c8 Mon Sep 17 00:00:00 2001 From: core-devops Date: Thu, 18 Jun 2026 21:50:44 +0000 Subject: [PATCH 1/5] fix(concierge): declare molecule-platform-mcp via gitea:// source (not bare name) [platform_agent.go] Co-Authored-By: Claude Opus 4.8 (1M context) --- .../internal/handlers/platform_agent.go | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/workspace-server/internal/handlers/platform_agent.go b/workspace-server/internal/handlers/platform_agent.go index 74126655..740887e4 100644 --- a/workspace-server/internal/handlers/platform_agent.go +++ b/workspace-server/internal/handlers/platform_agent.go @@ -256,8 +256,8 @@ func (h *WorkspaceHandler) applyConciergeProvisionConfig( // is kind-gated) → it is the primary entitlement gate for the privileged // org-admin MCP; recordDeclaredPlugin fail-closes the same name for any // non-platform workspace as defense-in-depth. Idempotent (upsert). - if rec, skip := seedTemplatePlugins(ctx, workspaceID, []string{conciergePlatformMCPPlugin}); skip > 0 { - log.Printf("Provisioner: concierge %s could not declare %q plugin (recorded=%d skipped=%d) — management MCP may be absent until next provision", workspaceID, conciergePlatformMCPPlugin, rec, skip) + if rec, skip := seedTemplatePlugins(ctx, workspaceID, []string{conciergePlatformMCPSource}); skip > 0 { + log.Printf("Provisioner: concierge %s could not declare %q plugin (recorded=%d skipped=%d) — management MCP may be absent until next provision", workspaceID, conciergePlatformMCPSource, rec, skip) } // 1. Platform-MCP env (org-admin token + platform URL + org id). @@ -407,17 +407,29 @@ const conciergeProvider = "platform" // model resolves cleanly on its own (e.g. `sonnet` -> anthropic-oauth). const platformManagedModelPrefix = "moonshot/" -// conciergePlatformMCPPlugin is the management-MCP plugin the concierge declares -// (repo molecule-ai-plugin-molecule-platform-mcp). It wires the `molecule-mcp` +// The management-MCP plugin the concierge declares. It wires the `molecule-mcp` // server (MOLECULE_MCP_MODE=management — create_workspace, list_workspaces, …) // into the Claude Code runtime via the plugin channel's MCPServerAdaptor, // replacing the baked-image + asset-channel mcp_servers.yaml path that does NOT -// reach the on-box config (RFC: rfc-platform-mcp-as-plugin). Declaring it here — -// from the kind=platform-only applyConciergeProvisionConfig — IS the primary +// reach the on-box config (RFC: rfc-platform-mcp-as-plugin). Declaring it from +// the kind=platform-only applyConciergeProvisionConfig IS the primary // entitlement gate (no user workspace runs this path); recordDeclaredPlugin adds // a defense-in-depth refusal for this PRIVILEGED name on any non-platform // workspace. The post-online reconcile + boot-install then install it. -const conciergePlatformMCPPlugin = "molecule-platform-mcp" +// +// conciergePlatformMCPSource MUST be a gitea:// source, not a bare name: the +// box's boot-install (runtime-image entrypoint) ONLY fetches gitea:// sources +// and SKIPS anything else ("skip unsupported source"). A bare name parses to the +// `local` scheme, which only resolves plugins baked into the image — and this is +// a brand-new Gitea-only plugin repo, so a bare name would never be fetched. +const conciergePlatformMCPSource = "gitea://molecule-ai/molecule-ai-plugin-molecule-platform-mcp" + +// conciergePlatformMCPName is the install NAME plugins.PluginNameFromSource +// derives from the gitea:// source above (the repo segment, no subpath). It is +// what gets written to workspace_declared_plugins.plugin_name and what the +// recordDeclaredPlugin entitlement gate matches on — so it MUST equal the +// derivation, not the human label "molecule-platform-mcp". +const conciergePlatformMCPName = "molecule-ai-plugin-molecule-platform-mcp" // ensureConciergeProvider pins the concierge's LLM provider to `platform` (core // companion to ensureConciergeModel). It guarantees the env-level provider pin @@ -807,3 +819,4 @@ func installPlatformAgent(ctx context.Context, database *sql.DB, platformID, nam return tx.Commit() } + -- 2.52.0 From df82bc5a24ee9563b926bd0ae9c28ffb96205f62 Mon Sep 17 00:00:00 2001 From: core-devops Date: Thu, 18 Jun 2026 21:50:45 +0000 Subject: [PATCH 2/5] fix(concierge): declare molecule-platform-mcp via gitea:// source (not bare name) [plugins_tracking.go] Co-Authored-By: Claude Opus 4.8 (1M context) --- workspace-server/internal/handlers/plugins_tracking.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/workspace-server/internal/handlers/plugins_tracking.go b/workspace-server/internal/handlers/plugins_tracking.go index b1f1f595..6df4b7dd 100644 --- a/workspace-server/internal/handlers/plugins_tracking.go +++ b/workspace-server/internal/handlers/plugins_tracking.go @@ -112,7 +112,7 @@ func recordDeclaredPlugin(ctx context.Context, workspaceID, pluginName, sourceRa // seed, org_import, a user-authored workspace.yaml), so refusing it here for a // non-platform workspace closes the privilege-escalation vector regardless of // declaration source. Fail-closed on a kind read error. - if pluginName == conciergePlatformMCPPlugin { + if pluginName == conciergePlatformMCPName { var kind string if err := db.DB.QueryRowContext(ctx, `SELECT COALESCE(kind, 'workspace') FROM workspaces WHERE id = $1`, workspaceID).Scan(&kind); err != nil { @@ -286,3 +286,4 @@ func deleteWorkspacePluginRow(ctx context.Context, workspaceID, pluginName strin `, workspaceID, pluginName) return err } + -- 2.52.0 From 51e9df2f2ea2a61970a01d1224d2ff5aa549f323 Mon Sep 17 00:00:00 2001 From: core-devops Date: Thu, 18 Jun 2026 21:50:46 +0000 Subject: [PATCH 3/5] fix(concierge): declare molecule-platform-mcp via gitea:// source (not bare name) [platform_agent_test.go] Co-Authored-By: Claude Opus 4.8 (1M context) --- workspace-server/internal/handlers/platform_agent_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/workspace-server/internal/handlers/platform_agent_test.go b/workspace-server/internal/handlers/platform_agent_test.go index 53e0b0dc..d85b08a1 100644 --- a/workspace-server/internal/handlers/platform_agent_test.go +++ b/workspace-server/internal/handlers/platform_agent_test.go @@ -937,9 +937,9 @@ func TestRecordDeclaredPlugin_PrivilegedPluginEntitlement(t *testing.T) { mock.ExpectQuery(kindQuery).WithArgs("ws-concierge"). WillReturnRows(sqlmock.NewRows([]string{"kind"}).AddRow("platform")) mock.ExpectExec(declaredInsert). - WithArgs("ws-concierge", conciergePlatformMCPPlugin, sqlmock.AnyArg()). + WithArgs("ws-concierge", conciergePlatformMCPName, sqlmock.AnyArg()). WillReturnResult(sqlmock.NewResult(0, 1)) - if err := recordDeclaredPlugin(context.Background(), "ws-concierge", conciergePlatformMCPPlugin, conciergePlatformMCPPlugin); err != nil { + if err := recordDeclaredPlugin(context.Background(), "ws-concierge", conciergePlatformMCPName, conciergePlatformMCPSource); err != nil { t.Fatalf("platform concierge declaration of the management MCP must succeed: %v", err) } if err := mock.ExpectationsWereMet(); err != nil { @@ -952,7 +952,7 @@ func TestRecordDeclaredPlugin_PrivilegedPluginEntitlement(t *testing.T) { mock.ExpectQuery(kindQuery).WithArgs("ws-user"). WillReturnRows(sqlmock.NewRows([]string{"kind"}).AddRow("workspace")) // NO ExpectExec: the gate MUST refuse before any INSERT fires. - err := recordDeclaredPlugin(context.Background(), "ws-user", conciergePlatformMCPPlugin, conciergePlatformMCPPlugin) + err := recordDeclaredPlugin(context.Background(), "ws-user", conciergePlatformMCPName, conciergePlatformMCPSource) if err == nil { t.Fatal("a non-platform workspace MUST NOT be able to declare the privileged management MCP plugin") } @@ -975,3 +975,4 @@ func TestRecordDeclaredPlugin_PrivilegedPluginEntitlement(t *testing.T) { } }) } + -- 2.52.0 From 16ba44988af09e50fd34a584cbae941c80c08e09 Mon Sep 17 00:00:00 2001 From: core-devops Date: Thu, 18 Jun 2026 19:14:49 -0700 Subject: [PATCH 4/5] =?UTF-8?q?style(concierge):=20gofmt=20=E2=80=94=20dro?= =?UTF-8?q?p=20trailing=20blank=20line=20at=20EOF=20(3=20files)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No behavior change. gofmt -l flagged platform_agent.go, plugins_tracking.go, and platform_agent_test.go for a blank line after the final brace. Co-Authored-By: Claude Opus 4.8 (1M context) --- workspace-server/internal/handlers/platform_agent.go | 1 - workspace-server/internal/handlers/platform_agent_test.go | 1 - workspace-server/internal/handlers/plugins_tracking.go | 1 - 3 files changed, 3 deletions(-) diff --git a/workspace-server/internal/handlers/platform_agent.go b/workspace-server/internal/handlers/platform_agent.go index 740887e4..a10e1579 100644 --- a/workspace-server/internal/handlers/platform_agent.go +++ b/workspace-server/internal/handlers/platform_agent.go @@ -819,4 +819,3 @@ func installPlatformAgent(ctx context.Context, database *sql.DB, platformID, nam return tx.Commit() } - diff --git a/workspace-server/internal/handlers/platform_agent_test.go b/workspace-server/internal/handlers/platform_agent_test.go index d85b08a1..d3700905 100644 --- a/workspace-server/internal/handlers/platform_agent_test.go +++ b/workspace-server/internal/handlers/platform_agent_test.go @@ -975,4 +975,3 @@ func TestRecordDeclaredPlugin_PrivilegedPluginEntitlement(t *testing.T) { } }) } - diff --git a/workspace-server/internal/handlers/plugins_tracking.go b/workspace-server/internal/handlers/plugins_tracking.go index 6df4b7dd..60be1a80 100644 --- a/workspace-server/internal/handlers/plugins_tracking.go +++ b/workspace-server/internal/handlers/plugins_tracking.go @@ -286,4 +286,3 @@ func deleteWorkspacePluginRow(ctx context.Context, workspaceID, pluginName strin `, workspaceID, pluginName) return err } - -- 2.52.0 From 014af619d255fe5105ecb83975167e13b1237745 Mon Sep 17 00:00:00 2001 From: core-devops Date: Thu, 18 Jun 2026 19:19:00 -0700 Subject: [PATCH 5/5] fix(concierge): pin management-MCP plugin source to #main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gitea resolver rejects an unpinned spec in production (PLUGIN_ALLOW_UNPINNED is unset by default), so an unpinned conciergePlatformMCPSource would record the declaration but FAIL to fetch at boot-install → no management MCP, no create_workspace. Pin #main (matches the seo-all convention). The #ref does not affect PluginNameFromSource, so conciergePlatformMCPName is unchanged (verified: derives molecule-ai-plugin-molecule-platform-mcp). Found in review (security pass) of #3049. Co-Authored-By: Claude Opus 4.8 (1M context) --- workspace-server/internal/handlers/platform_agent.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/workspace-server/internal/handlers/platform_agent.go b/workspace-server/internal/handlers/platform_agent.go index a10e1579..651bb49f 100644 --- a/workspace-server/internal/handlers/platform_agent.go +++ b/workspace-server/internal/handlers/platform_agent.go @@ -422,7 +422,14 @@ const platformManagedModelPrefix = "moonshot/" // and SKIPS anything else ("skip unsupported source"). A bare name parses to the // `local` scheme, which only resolves plugins baked into the image — and this is // a brand-new Gitea-only plugin repo, so a bare name would never be fetched. -const conciergePlatformMCPSource = "gitea://molecule-ai/molecule-ai-plugin-molecule-platform-mcp" +// +// It MUST also carry a pinned #ref: the gitea resolver rejects an unpinned spec +// in production (PLUGIN_ALLOW_UNPINNED is unset by default — see plugins/gitea.go), +// so an unpinned source would record the declaration but then FAIL to fetch at +// boot-install time → no management MCP, no create_workspace. #main matches the +// established seo-all convention (gitea.go example). The #ref does NOT affect +// PluginNameFromSource, so conciergePlatformMCPName below is unchanged. +const conciergePlatformMCPSource = "gitea://molecule-ai/molecule-ai-plugin-molecule-platform-mcp#main" // conciergePlatformMCPName is the install NAME plugins.PluginNameFromSource // derives from the gitea:// source above (the repo segment, no subpath). It is -- 2.52.0