starmux 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +20 -30
- package/dist/src/access-control.js +1 -2
- package/dist/src/access-dashboard.js +2 -3
- package/dist/src/account-access-policy.js +54 -0
- package/dist/src/authorized-workspace-roots.js +0 -1
- package/dist/src/batch-read-policy.js +30 -0
- package/dist/src/cli/instances.js +6 -175
- package/dist/src/cli/oauth.js +0 -1
- package/dist/src/cli/starmux-status.js +10 -14
- package/dist/src/cli/starmux-supervisor.js +0 -1
- package/dist/src/cli/starmux.js +13 -25
- package/dist/src/config-schemas.js +0 -9
- package/dist/src/configured-mcp-upstreams.js +0 -1
- package/dist/src/contracts.js +0 -1
- package/dist/src/current-permission-probe.js +0 -1
- package/dist/src/dc-workspace-http.js +57 -33
- package/dist/src/deny-dashboard.js +2 -3
- package/dist/src/desktop-commander-facade.js +4 -4
- package/dist/src/explicit-deny-rules.js +2 -3
- package/dist/src/global-core-control.js +2 -3
- package/dist/src/home-write-sandbox.js +0 -1
- package/dist/src/http-host-policy.js +20 -0
- package/dist/src/instance-runtime.js +3 -3
- package/dist/src/instance-store.js +28 -22
- package/dist/src/instance-worker.js +2 -3
- package/dist/src/invocation.js +1 -2
- package/dist/src/local-desktop-read-probe.js +5 -88
- package/dist/src/managed-workspace-policy.js +38 -4
- package/dist/src/mcp-results.js +0 -1
- package/dist/src/mcp-upstream-router.js +0 -1
- package/dist/src/oauth-keycloak.js +0 -1
- package/dist/src/oauth-resource.js +0 -1
- package/dist/src/ops-dashboard.js +2 -179
- package/dist/src/permissions-dashboard.js +3 -4
- package/dist/src/platform.js +3 -4
- package/dist/src/privacy-diagnostics.js +0 -1
- package/dist/src/private-mcp-activity.js +0 -1
- package/dist/src/private-store-lock.js +0 -1
- package/dist/src/project-dev-presets.js +0 -1
- package/dist/src/project-git-workflow.js +0 -1
- package/dist/src/project-workspace-policy.js +10 -6
- package/dist/src/runtime-identity.js +0 -14
- package/dist/src/runtime-paths.js +0 -3
- package/dist/src/tool-dispatch.js +45 -46
- package/dist/src/validation.js +0 -1
- package/dist/src/workspace-grants.js +1 -2
- package/package.json +13 -17
- package/scripts/prepare.mjs +1 -1
- package/CONTRIBUTING.md +0 -18
- package/SECURITY.md +0 -7
- package/app/README.md +0 -29
- package/dist/src/access-control.js.map +0 -1
- package/dist/src/access-dashboard.js.map +0 -1
- package/dist/src/authorized-workspace-roots.js.map +0 -1
- package/dist/src/cli/access.js +0 -53
- package/dist/src/cli/access.js.map +0 -1
- package/dist/src/cli/community-remote-revoke.js +0 -32
- package/dist/src/cli/community-remote-revoke.js.map +0 -1
- package/dist/src/cli/instances.js.map +0 -1
- package/dist/src/cli/live-home-settings.js +0 -35
- package/dist/src/cli/live-home-settings.js.map +0 -1
- package/dist/src/cli/mac-mcp-gateway.js +0 -257
- package/dist/src/cli/mac-mcp-gateway.js.map +0 -1
- package/dist/src/cli/oauth.js.map +0 -1
- package/dist/src/cli/permissions-owner.js +0 -27
- package/dist/src/cli/permissions-owner.js.map +0 -1
- package/dist/src/cli/service-setup.js +0 -26
- package/dist/src/cli/service-setup.js.map +0 -1
- package/dist/src/cli/setup-wizard.js +0 -149
- package/dist/src/cli/setup-wizard.js.map +0 -1
- package/dist/src/cli/starmux-status.js.map +0 -1
- package/dist/src/cli/starmux-supervisor.js.map +0 -1
- package/dist/src/cli/starmux.js.map +0 -1
- package/dist/src/cli/verify-setup.js +0 -53
- package/dist/src/cli/verify-setup.js.map +0 -1
- package/dist/src/community-remote-revocations.js +0 -135
- package/dist/src/community-remote-revocations.js.map +0 -1
- package/dist/src/config-schemas.js.map +0 -1
- package/dist/src/configured-mcp-upstreams.js.map +0 -1
- package/dist/src/contracts.js.map +0 -1
- package/dist/src/current-permission-probe.js.map +0 -1
- package/dist/src/dc-workspace-http.js.map +0 -1
- package/dist/src/deny-dashboard.js.map +0 -1
- package/dist/src/desktop-commander-facade.js.map +0 -1
- package/dist/src/explicit-deny-rules.js.map +0 -1
- package/dist/src/gateway-status-format.js +0 -66
- package/dist/src/gateway-status-format.js.map +0 -1
- package/dist/src/global-core-control.js.map +0 -1
- package/dist/src/home-write-sandbox.js.map +0 -1
- package/dist/src/instance-migration.js +0 -138
- package/dist/src/instance-migration.js.map +0 -1
- package/dist/src/instance-runtime.js.map +0 -1
- package/dist/src/instance-store.js.map +0 -1
- package/dist/src/instance-worker.js.map +0 -1
- package/dist/src/invocation.js.map +0 -1
- package/dist/src/live-profile-home-settings.js +0 -80
- package/dist/src/live-profile-home-settings.js.map +0 -1
- package/dist/src/local-desktop-read-probe.js.map +0 -1
- package/dist/src/managed-workspace-policy.js.map +0 -1
- package/dist/src/mcp-results.js.map +0 -1
- package/dist/src/mcp-upstream-router.js.map +0 -1
- package/dist/src/oauth-keycloak.js.map +0 -1
- package/dist/src/oauth-resource.js.map +0 -1
- package/dist/src/operator-cli.js +0 -559
- package/dist/src/operator-cli.js.map +0 -1
- package/dist/src/ops-dashboard.js.map +0 -1
- package/dist/src/permissions-dashboard.js.map +0 -1
- package/dist/src/platform.js.map +0 -1
- package/dist/src/privacy-diagnostics.js.map +0 -1
- package/dist/src/private-mcp-activity.js.map +0 -1
- package/dist/src/private-store-lock.js.map +0 -1
- package/dist/src/project-dev-presets.js.map +0 -1
- package/dist/src/project-git-workflow.js.map +0 -1
- package/dist/src/project-workspace-policy.js.map +0 -1
- package/dist/src/read-scope.js +0 -44
- package/dist/src/read-scope.js.map +0 -1
- package/dist/src/release1-onboarding.js +0 -120
- package/dist/src/release1-onboarding.js.map +0 -1
- package/dist/src/release1-service.js +0 -131
- package/dist/src/release1-service.js.map +0 -1
- package/dist/src/remote-auth.js +0 -47
- package/dist/src/remote-auth.js.map +0 -1
- package/dist/src/runtime-identity.js.map +0 -1
- package/dist/src/runtime-paths.js.map +0 -1
- package/dist/src/starmux-migration.js +0 -172
- package/dist/src/starmux-migration.js.map +0 -1
- package/dist/src/tool-dispatch.js.map +0 -1
- package/dist/src/validation.js.map +0 -1
- package/dist/src/workspace-grants.js.map +0 -1
- package/docs/ACCESS_CONTROL.md +0 -51
- package/docs/CLIENTS.md +0 -22
- package/docs/CONNECT_CHATGPT.md +0 -148
- package/docs/INSTALL.md +0 -62
- package/docs/INSTANCES.md +0 -83
- package/docs/OAUTH.md +0 -116
- package/docs/PLATFORMS.md +0 -29
- package/docs/README.md +0 -16
- package/docs/REMOTE_SETUP.md +0 -280
- package/docs/SECURITY.md +0 -15
- package/docs/assets/starmux-app-en.png +0 -0
- package/docs/assets/starmux-architecture.svg +0 -315
- package/src/dc-workspace-http.mjs +0 -3
- package/test/fixtures/p1-fixture.txt +0 -2
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tool-dispatch.js","sourceRoot":"","sources":["../../src/tool-dispatch.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AuditSink,CallToolResult,RequestContext,ToolInvocation } from './contracts.js';\nimport type { BridgeOptions,BridgeUpstream } from './desktop-commander-facade.js';\nimport { authorizeInvocation,createInvocationExecutor,observeSafely } from './invocation.js';\nimport { errorCode } from './validation.js';\n\n// The dispatch order is part of the compatibility contract: Gateway tools,\n// extension routes, owner-enabled native mode, workspace policy, then fixtures.\nexport function createToolDispatcher(upstream: BridgeUpstream, audit: AuditSink, options: BridgeOptions) {\n const execute = createInvocationExecutor(upstream);\n return async (params: ToolInvocation, context: RequestContext): Promise<CallToolResult> => {\n const id = context.requestId;\n const name = params.name;\n const args = params.arguments || {};\n const observe = (fn: () => void) => observeSafely(fn, audit, context);\n if(name===\"health_check\"){\n audit({event:\"DC_BRIDGE_HEALTH_CHECK\",requestId:id});\n return {content:[{type:\"text\",text:JSON.stringify({\n status:\"ok\",service:\"mac-mcp-gateway-dc-bridge\",\n upstream:\"desktop-commander\",tools:upstream.upstreamCatalog.length,\n routes:options.upstreamRouter?.routes||[{id:\"desktop\",toolCount:upstream.upstreamCatalog.length}],\n workspaceMode:options.workspacePolicy?.mode||\"fixed-fixture\",\n developerCommands:options.workspacePolicy?.developerCommands||\"fixed-fixture\",\n nativeAccess:options.nativePassthrough===true,\n traceId:id\n })}]};\n }\n // Present effective Gateway permissions, not the raw upstream config.\n if(name===\"get_config\"&&options.workspacePolicy&&!options.nativePassthrough){\n const policy=options.workspacePolicy;\n audit({event:\"GATEWAY_CONFIG_READ\",requestId:id});\n return {content:[{type:\"text\",text:JSON.stringify({\n source:\"mac-mcp-gateway\",configType:\"effective-workspace-summary\",\n projectRoot:policy.root,workspaceMode:policy.mode,\n developerCommands:policy.developerCommands,\n commandSafety:\"Desktop Commander upstream blocklist; advisory, not OS sandbox\",\n remoteConfigMutation:\"owner-only\",\n nativeToolCount:upstream.upstreamCatalog.length,\n grantsDashboard:policy.grantsSnapshot?\"/permissions\":null,\n note:\"Gateway summary; not the complete upstream Desktop Commander config.\"\n },null,2)}]};\n }\n if(name===\"set_config_value\"&&options.workspacePolicy&&!options.nativePassthrough){\n audit({event:\"GATEWAY_CONFIG_MUTATION_DENIED\",requestId:id});\n return {isError:true,content:[{type:\"text\",text:\n \"OWNER_ONLY_CONFIG: Remote AI cannot modify safety settings; use the Mac owner dashboard.\"}]};\n }\n if(name===\"gateway_disk_usage\"&&options.workspacePolicy?.prepareDiskUsage){\n try{\n if(!args||typeof args.path!==\"string\"||\n Object.keys(args).some(k=>![\"path\",\"depth\"].includes(k)))\n throw Error(\"DISK_USAGE_ARGUMENTS_DENIED\");\n const invocation=await options.workspacePolicy.prepareDiskUsage(\n args.path,args.depth??1);\n const result=await execute(authorizeInvocation(invocation));\n observe(() => options.workspacePolicy?.observe?.(\"start_process\",result,\n {command:\"gateway_disk_usage\"}));\n audit({event:\"GATEWAY_DISK_USAGE_COMPLETED\",requestId:id,\n isError:result.isError===true});\n return result;\n }catch(error){\n audit({event:\"GATEWAY_DISK_USAGE_DENIED\",requestId:id,\n reason:errorCode(error)||\"DISK_USAGE_ERROR\"});\n return {isError:true,content:[{type:\"text\",text:\"WORKSPACE_DENIED: \"+\n \"Check directory grants on the Mac Dashboard /permissions\"}]};\n }\n }\n if(options.gitWorkflow?.names.includes(name)){\n audit({event:\"GIT_WORKFLOW_ATTEMPT\",requestId:id,tool:name});\n try{\n const result=await options.gitWorkflow.handle(name,args);\n audit({event:\"GIT_WORKFLOW_COMPLETE\",requestId:id,tool:name});\n return result;\n }catch(error){\n audit({event:\"GIT_WORKFLOW_DENIED\",requestId:id,tool:name,\n reason:errorCode(error)||\"GIT_WORKFLOW_FAILED\"});\n return {isError:true,content:[{type:\"text\",text:\"GIT_WORKFLOW_DENIED\"}]};\n }\n }\n if(options.upstreamRouter?.routeFor(name)&&\n options.upstreamRouter.routeFor(name)!==\"desktop\"){\n let routed;\n try{routed=await options.upstreamRouter.authorizeExtension(name,args);}\n catch(error){\n audit({event:\"MCP_ROUTE_DENIED\",requestId:id,tool:name,\n reason:errorCode(error)||\"ROUTE_POLICY\"});\n return {isError:true,content:[{type:\"text\",text:\"ROUTE_DENIED\"}]};\n }\n audit({event:\"MCP_ROUTE_FORWARDED\",requestId:id,tool:name});\n try{\n const result=await execute(authorizeInvocation(routed));\n audit({event:\"MCP_ROUTE_COMPLETED\",requestId:id,tool:name,\n isError:result.isError===true});\n return result;\n }catch(error){\n // Report ambiguous remote effects distinctly; never silently replay.\n const code=errorCode(error)===\"UPSTREAM_CALL_UNCERTAIN\"?\n \"UPSTREAM_CALL_UNCERTAIN\":\"UPSTREAM_UNAVAILABLE\";\n audit({event:\"MCP_ROUTE_UPSTREAM_FAILED\",requestId:id,tool:name,reason:code});\n return {isError:true,content:[{type:\"text\",text:code}]};\n }\n }\n // Deliberately trusted, owner-enabled native mode: the upstream server\n // and the macOS process account decide execution. Do not maintain a\n // second incomplete tool/path/argument whitelist here. Never use this\n // path for unrecognized names, routed extensions or Gateway tools.\n if(options.nativePassthrough&&\n upstream.upstreamCatalog.some(tool=>tool.name===name)){\n audit({event:\"DC_NATIVE_FORWARDED\",requestId:id,tool:name});\n try{\n const result=await execute(authorizeInvocation({name,arguments:args}));\n audit({event:\"DC_NATIVE_COMPLETED\",requestId:id,tool:name,\n isError:result.isError===true});\n return result;\n }catch(error){\n const reason=errorCode(error)===\"UPSTREAM_CALL_UNCERTAIN\"?\n \"UPSTREAM_CALL_UNCERTAIN\":\"UPSTREAM_UNAVAILABLE\";\n audit({event:\"DC_NATIVE_UPSTREAM_FAILED\",requestId:id,tool:name,reason});\n return {isError:true,content:[{type:\"text\",text:reason}]};\n }\n }\n if(options.workspacePolicy){\n let invocation;\n try{invocation=await options.workspacePolicy.prepare(name,args);}\n catch(error){\n audit({event:\"DC_BRIDGE_DENIED\",requestId:id,tool:name,\n reason:errorCode(error)||\"POLICY_ERROR\"});\n return {isError:true,content:[{type:\"text\",text:\n options.workspacePolicy.grantsSnapshot?\n \"WORKSPACE_DENIED: Review directory grants on your Mac Dashboard /permissions\":\n \"WORKSPACE_DENIED\"}]};\n }\n audit({event:\"DC_BRIDGE_FORWARDED\",requestId:id,tool:name});\n try{\n const result=await execute(authorizeInvocation(invocation));\n observe(() => options.workspacePolicy?.observe?.(name,result,args));\n audit({event:\"DC_BRIDGE_COMPLETED\",requestId:id,tool:name,\n commandBytes:name===\"start_process\"?Buffer.byteLength(String(args.command||\"\")):undefined,\n isError:result.isError===true});\n return result;\n }catch{\n audit({event:\"DC_BRIDGE_UPSTREAM_FAILED\",requestId:id,tool:name});\n return {isError:true,content:[{type:\"text\",text:\"UPSTREAM_UNAVAILABLE\"}]};\n }\n }\n const keys=Object.keys(args);\n const allowed=(name===\"read_file\"&&\n [\"p1-fixture.txt\",\"dc-bridge-canary.txt\",\"README.md\"].includes(String(args.path))&&\n keys.length===1&&keys[0]===\"path\") ||\n (name===\"write_file\"&&args.path===\"dc-bridge-canary.txt\"&&\n typeof args.content===\"string\"&&Buffer.byteLength(args.content)<=256&&\n keys.every(k=>[\"path\",\"content\"].includes(k))) ||\n (name===\"start_process\"&&args.command===\"/usr/bin/uname -s\"&&\n args.timeout_ms===3000&&\n keys.every(k=>[\"command\",\"timeout_ms\"].includes(k)));\n if(!allowed){\n audit({event:\"DC_BRIDGE_DENIED\",requestId:id,tool:name});\n return {isError:true,content:[{type:\"text\",text:\"NOT_APPROVED_IN_P1_FIXTURE\"}]};\n }\n audit({event:\"DC_BRIDGE_FORWARDED\",requestId:id,tool:name});\n try {\n let response;\n if(name===\"read_file\"&&args.path!==\"README.md\")\n response=await upstream.callApprovedRead!(String(args.path));\n else if(name===\"read_file\")\n response=await upstream.callApprovedProjectRead!();\n else if(name===\"write_file\")\n response=await upstream.callApprovedCanaryWrite!(String(args.content));\n else\n response=await upstream.callApprovedSystemName!();\n audit({event:\"DC_BRIDGE_COMPLETED\",requestId:id,tool:name,\n isError:response.isError===true});\n return response;\n }catch{\n audit({event:\"DC_BRIDGE_UPSTREAM_FAILED\",requestId:id,tool:name});\n return {isError:true,content:[{type:\"text\",text:\"UPSTREAM_FAILED\"}]};\n }\n };\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/validation.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,MAAM,OAAO,YAAa,SAAQ,KAAK;IACT;IAA5B,YAA4B,IAAY;QAAI,KAAK,CAAC,IAAI,CAAC,CAAC;QAA5B,SAAI,GAAJ,IAAI,CAAQ;IAAiB,CAAC;CAC3D;AACD,MAAM,UAAU,IAAI,CAAC,IAAY,IAAW,MAAM,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC3E,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,MAAM,IAAI,YAAY,CAAC,oBAAoB,CAAC,CAAC;IAClE,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC;AAC1B,CAAC;AACD,MAAM,UAAU,SAAS,CAAC,KAAc,IAAqB,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC9F,MAAM,UAAU,aAAa,CAAC,KAAc,IAAqB,OAAO,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACtG,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AACD,MAAM,UAAU,MAAM,CAAC,KAAc,EAAE,IAAI,GAAG,gBAAgB;IAC5D,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO,KAAK,CAAC;AACf,CAAC;AACD,MAAM,UAAU,SAAS,CAAC,IAAY,IAAa,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC7E,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,OAAO,CAAC,KAAK,YAAY,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,MAAM,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AACnI,CAAC;AACD,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAC5D,CAAC;AACD,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,CAAC,KAAK,YAAY,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,QAAQ,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;AAClI,CAAC","sourcesContent":["/** Boundary helpers: untrusted values stay unknown until narrowed. */\nexport class GatewayError extends Error {\n constructor(public readonly code: string) { super(code); }\n}\nexport function fail(code: string): never { throw new GatewayError(code); }\nexport function currentUid(): number {\n if (!process.getuid) throw new GatewayError('MACOS_UID_REQUIRED');\n return process.getuid();\n}\nexport function isInteger(value: unknown): value is number { return Number.isInteger(value); }\nexport function isSafeInteger(value: unknown): value is number { return Number.isSafeInteger(value); }\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\nexport function record(value: unknown, code = 'INVALID_OBJECT'): Record<string, unknown> {\n if (!isRecord(value)) fail(code);\n return value;\n}\nexport function parseJSON(text: string): unknown { return JSON.parse(text); }\nexport function errorCode(error: unknown): string | undefined {\n return (error instanceof Error || isRecord(error)) && 'code' in error && typeof error.code === 'string' ? error.code : undefined;\n}\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : 'UNKNOWN';\n}\nexport function errorStderr(error: unknown): string {\n return (error instanceof Error || isRecord(error)) && 'stderr' in error && typeof error.stderr === 'string' ? error.stderr : '';\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-grants.js","sourceRoot":"","sources":["../../src/workspace-grants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAC,WAAW,EAAC,sBAAsB,EAAC,MAAM,EAAC,MAAM,eAAe,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,YAAY,EAAC,SAAS,EAAC,SAAS,EAAC,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC1E,yEAAyE;AACzE,gFAAgF;AAChF,OAAO,EAAE,WAAW,EAAC,eAAe,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,KAAK,EAAC,KAAK,EAAC,QAAQ,EAAC,QAAQ,EAAC,MAAM,EAAC,EAAE,EAAC,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,SAAS,IAAI,CAAC,CAAS,IAAU,MAAM,CAAC,GAAC,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC,CAAA,MAAM,CAAC,CAAC,CAAA,CAAC;AAAA,CAAC;AACvE,MAAM,CAAC,MAAM,iBAAiB,GAAC,CAAC,IAAI,GAAC,EAAE,CAAC,OAAO,EAAE,EAAC,EAAE,CAClD,IAAI,CAAC,IAAI,CAAC,IAAI,EAAC,SAAS,EAAC,iBAAiB,EAAC,0BAA0B,CAAC,CAAC;AACzE,MAAM,QAAQ,GAAC,IAAI,GAAG,CAAC,CAAC,SAAS,EAAC,UAAU,EAAC,aAAa,EAAC,QAAQ,CAAC,CAAC,CAAC;AACtE,MAAM,CAAC,MAAM,QAAQ,GAAC,MAAM,CAAC;AAC7B,KAAK,UAAU,QAAQ,CAAC,IAAY,EAAC,EAAC,SAAS,GAAC,KAAK,EAAC,OAAO,GAAC,KAAK,EAAC,GAAC,EAAE;IACrE,MAAM,EAAE,GAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAA,EAAE,CAAA,SAAS,CAAC,CAAC,CAAC,KAAG,QAAQ,IAAE,OAAO,CAAA,CAAC,CAAA,IAAI,CAAA,CAAC,CAAA,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7F,IAAG,CAAC,EAAE;QAAC,OAAO,IAAI,CAAC;IACnB,IAAG,EAAE,CAAC,cAAc,EAAE,IAAE,CAAC,SAAS,CAAA,CAAC,CAAA,CAAC,EAAE,CAAC,WAAW,EAAE,CAAA,CAAC,CAAA,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC;QAChE,CAAC,WAAW,CAAC,IAAI,EAAC,EAAE,EAAC,IAAI,CAAC,IAAE,CAAC,CAAC,SAAS,IAAE,EAAE,CAAC,KAAK,KAAG,CAAC,CAAC;QAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACzF,OAAO,EAAE,CAAC;AACZ,CAAC;AACD,KAAK,UAAU,aAAa,CAAC,MAAc;IACzC,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAC/B,MAAM,QAAQ,CAAC,MAAM,EAAC,EAAC,SAAS,EAAC,IAAI,EAAC,CAAC,CAAC;AAC1C,CAAC;AACD,KAAK,UAAU,YAAY,CAAC,IAAY,EAAC,IAAY;IACnD,MAAM,IAAI,GAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAC,GAAG,GAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAC,GAAG,GAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IACpG,IAAG,CAAC;QACF,MAAM,SAAS,CAAC,IAAI,EAAC,IAAI,EAAC,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAC,CAAC,CAAC;QAClD,MAAM,MAAM,CAAC,IAAI,EAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;YAAO,CAAC;QAAA,MAAM,EAAE,CAAC,IAAI,EAAC,EAAC,KAAK,EAAC,IAAI,EAAC,CAAC,CAAC;IAAA,CAAC;AACxC,CAAC;AACD,KAAK,UAAU,WAAW,CAAC,GAAY,EAAC,IAAY;IAClD,IAAG,OAAO,GAAG,KAAG,QAAQ,IAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAE,GAAG,CAAC,MAAM,GAAC,IAAI;QAC9D,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IACpF,MAAM,KAAK,GAAC,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;IAChC,MAAM,EAAE,GAAC,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;IAC5B,IAAG,CAAC,EAAE,CAAC,WAAW,EAAE,IAAE,EAAE,CAAC,cAAc,EAAE,IAAE,CAAC,WAAW,CAAC,KAAK,EAAC,EAAE,CAAC;QAC/D,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAC5B,IAAG,KAAK,KAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,IAAE,KAAK,KAAG,IAAI,IAAE,KAAK,KAAG,QAAQ,IAAE,KAAK,KAAG,UAAU;QACnF,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAC/B,MAAM,SAAS,GAAC,IAAI,GAAG,CAAC,CAAC,MAAM,EAAC,MAAM,EAAC,QAAQ,EAAC,SAAS,EAAC,WAAW;QACnE,SAAS,EAAC,aAAa,CAAC,CAAC,CAAC;IAC5B,IAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAA,EAAE,CAAA,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QACpE,IAAI,CAAC,6BAA6B,CAAC,CAAC;IACtC,OAAO,KAAK,CAAC;AACf,CAAC;AACD,SAAS,QAAQ,CAAC,KAAc;IAC9B,MAAM,IAAI,GAAC,MAAM,CAAC,KAAK,EAAC,uBAAuB,CAAC,CAAC;IACjD,IAAG,CAAC,IAAI,IAAE,OAAO,IAAI,KAAG,QAAQ,IAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QACnD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,KAAG,QAAQ,CAAC,IAAI;QACxC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA,EAAE,CAAA,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAE,IAAI,CAAC,OAAO,KAAG,CAAC;QAC7D,OAAO,IAAI,CAAC,QAAQ,KAAG,SAAS,IAAE,OAAO,IAAI,CAAC,WAAW,KAAG,SAAS;QACrE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAE,IAAI,CAAC,MAAM,CAAC,MAAM,GAAC,EAAE;QAClD,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAChC,MAAM,IAAI,GAAC,IAAI,GAAG,EAAE,CAAC;IACrB,KAAI,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAC,CAAC;QAC9B,MAAM,IAAI,GAAC,MAAM,CAAC,KAAK,EAAC,uBAAuB,CAAC,CAAC;QACjD,IAAG,CAAC,IAAI,IAAE,OAAO,IAAI,CAAC,IAAI,KAAG,QAAQ,IAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;YAChE,IAAI,CAAC,IAAI,CAAC,MAAM,GAAC,IAAI,IAAE,CAAC,CAAC,WAAW,EAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9E,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,KAAG,WAAW,IAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;YAClE,IAAI,CAAC,uBAAuB,CAAC,CAAC;QAChC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC;IACD,OAAO,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC;AACD,MAAM,QAAQ,GAAC,GAAgB,EAAE,CAAA,CAAC,EAAC,OAAO,EAAC,CAAC,EAAC,QAAQ,EAAC,KAAK,EAAC,WAAW,EAAC,KAAK,EAAC,MAAM,EAAC,EAAE,EAAC,CAAC,CAAC;AAC1F,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,EAAC,IAAI,GAAC,iBAAiB,EAAE,EAAC,IAAI,GAAC,EAAE,CAAC,OAAO,EAAE,EAAC,KAAK,GAAC,GAAE,EAAE,GAAC,CAAC,KAAqD,EAAE;IACzJ,IAAG,OAAO,IAAI,KAAG,QAAQ,IAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,GAAC,GAAG;QAC/E,IAAI,CAAC,+BAA+B,CAAC,CAAC;IACxC,MAAM,aAAa,GAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;IACzC,MAAM,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACxC,MAAM,SAAS,GAAC,IAAI,GAAC,cAAc,CAAC;IACpC,MAAM,eAAe,CAAC,IAAI,GAAC,OAAO,EAAC,KAAK,IAAE,EAAE;QAC5C,MAAM,QAAQ,GAAC,MAAM,QAAQ,CAAC,IAAI,EAAC,EAAC,OAAO,EAAC,IAAI,EAAC,CAAC,CAAC;QACnD,IAAG,CAAC,QAAQ;YAAC,MAAM,YAAY,CAAC,IAAI,EAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAC,IAAI,EAAC,CAAC,CAAC,GAAC,IAAI,CAAC,CAAC;QAC7E,IAAG,CAAC,MAAM,QAAQ,CAAC,SAAS,EAAC,EAAC,OAAO,EAAC,IAAI,EAAC,CAAC;YAC1C,MAAM,YAAY,CAAC,SAAS,EAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAC,IAAI,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IACH,IAAI,KAAK,GAAmB,OAAO,CAAC,OAAO,EAAE,CAAC;IAC9C,KAAK,UAAU,QAAQ,CAAC,EAAC,aAAa,GAAC,IAAI,EAAC,GAAC,EAAE;QAC7C,MAAM,EAAE,GAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAG,EAAG,CAAC,IAAI,GAAC,KAAK;YAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QAClD,MAAM,MAAM,GAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC9D,IAAG,aAAa;YAAC,KAAI,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,EAAC,CAAC;gBAChD,MAAM,KAAK,GAAC,MAAM,WAAW,CAAC,IAAI,CAAC,IAAI,EAAC,aAAa,CAAC,CAAC;gBACvD,IAAG,KAAK,KAAG,IAAI,CAAC,IAAI;oBAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;YACnD,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,KAAK,UAAU,UAAU,CAAC,KAAc;QACtC,MAAM,EAAE,GAAC,MAAM,QAAQ,CAAC,SAAS,CAAC,CAAC;QACnC,IAAG,EAAG,CAAC,IAAI,GAAC,GAAG,IAAE,OAAO,KAAK,KAAG,QAAQ,IAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC;YACrE,OAAO,KAAK,CAAC;QACf,MAAM,MAAM,GAAC,CAAC,MAAM,QAAQ,CAAC,SAAS,EAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACvD,IAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC;YAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QAC/D,OAAO,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IACjE,CAAC;IACD,KAAK,UAAU,MAAM,CAAC,EAAU,EAAC,EAAC,MAAM,EAAC,IAAI,KAAsC,EAAE;QACnF,MAAM,IAAI,GAAC,KAAK,IAAE,EAAE;YAClB,MAAM,IAAI,GAAC,MAAM,QAAQ,CAAC,EAAC,aAAa,EAAC,KAAK,EAAC,CAAC,CAAC;YACjD,IAAG,EAAE,KAAG,aAAa;gBAAC,IAAI,CAAC,QAAQ,GAAC,IAAI,CAAC;iBACpC,IAAG,EAAE,KAAG,cAAc;gBAAC,IAAI,CAAC,QAAQ,GAAC,KAAK,CAAC;iBAC3C,IAAG,EAAE,KAAG,gBAAgB;gBAAC,IAAI,CAAC,WAAW,GAAC,IAAI,CAAC;iBAC/C,IAAG,EAAE,KAAG,iBAAiB;gBAAC,IAAI,CAAC,WAAW,GAAC,KAAK,CAAC;iBACjD,IAAG,CAAC,KAAK,EAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAC,CAAC;gBACrC,uEAAuE;gBACvE,MAAM,SAAS,GAAC,EAAE,KAAG,QAAQ,CAAA,CAAC,CAAA,MAAM,CAAA,CAAC;oBACnC,MAAM,WAAW,CAAC,MAAM,EAAC,aAAa,CAAC,CAAC;gBAC1C,IAAG,OAAO,SAAS,KAAG,QAAQ,IAAE,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;oBACxD,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,KAAG,SAAS;oBAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;gBACpE,MAAM,KAAK,GAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAA,EAAE,CAAA,CAAC,CAAC,IAAI,KAAG,SAAS,CAAC,CAAC;gBACzD,IAAG,EAAE,KAAG,QAAQ,EAAC,CAAC;oBAChB,IAAG,KAAK,GAAC,CAAC;wBAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;oBACnC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAC,CAAC,CAAC,CAAC;gBAC9B,CAAC;qBAAI,CAAC;oBACJ,IAAG,IAAI,KAAG,WAAW,IAAE,IAAI,KAAG,YAAY;wBAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;oBACtE,IAAG,KAAK,GAAC,CAAC;wBAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAC,IAAI,EAAC,SAAS,EAAC,IAAI,EAAC,CAAC,CAAC;;wBAC9C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAE,CAAC,IAAI,GAAC,IAAI,CAAC;gBACrC,CAAC;YACH,CAAC;;gBAAK,IAAI,CAAC,yBAAyB,CAAC,CAAC;YACtC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACf,MAAM,YAAY,CAAC,IAAI,EAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAC,IAAI,EAAC,CAAC,CAAC,GAAC,IAAI,CAAC,CAAC;YAC1D,KAAK,CAAC,EAAC,KAAK,EAAC,yBAAyB,EAAC,MAAM,EAAC,EAAE,EAAC,KAAK,EAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA,CAAC,CAAA,MAAM,CAAA,CAAC;oBAClF,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAA,CAAC,CAAA,SAAS,CAAA,CAAC,CAAA,QAAQ,EAAC,IAAI,EAAC,IAAI,IAAE,QAAQ,EAAC,CAAC,CAAC;YACpE,OAAO,MAAM,QAAQ,CAAC,EAAC,aAAa,EAAC,KAAK,EAAC,CAAC,CAAC;QAC/C,CAAC,CAAC;QACF,MAAM,OAAO,GAAC,KAAK,CAAC,IAAI,CAAC,GAAE,EAAE,CAAA,eAAe,CAAC,IAAI,GAAC,OAAO,EAAC,IAAI,CAAC,CAAC,CAAC;QACjE,KAAK,GAAC,OAAO,CAAC,KAAK,CAAC,GAAE,EAAE,GAAC,CAAC,CAAC,CAAC;QAC5B,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,MAAM,QAAQ,CAAC,EAAC,aAAa,EAAC,KAAK,EAAC,CAAC,CAAC;IACtC,OAAO,MAAM,CAAC,MAAM,CAAC,EAAC,IAAI,EAAC,SAAS,EAAC,IAAI,EAAC,aAAa,EAAC,QAAQ,EAAC,UAAU,EAAC,MAAM,EAAC,CAAC,CAAC;AACvF,CAAC","sourcesContent":["import {withPrivateLock} from './private-store-lock.js';\nimport {ownedByUser,createPrivateDirectory,within} from \"./platform.js\";\nimport { grantsSchema } from \"./config-schemas.js\";\nimport type { AuditSink,GrantsConfig } from \"./contracts.js\";\nimport { GatewayError,errorCode,parseJSON,record } from \"./validation.js\";\n// Owner-managed multi-root grants. Separate from MCP tool authorization;\n// a remotely controlled tool cannot modify this file or learn the admin secret.\nimport { randomBytes,timingSafeEqual } from \"node:crypto\";\nimport { lstat,mkdir,readFile,realpath,rename,rm,writeFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nfunction deny(s: string): never {const e=new GatewayError(s);throw e;};\nexport const defaultGrantsPath=(home=os.homedir())=>\n path.join(home,\".config\",\"mac-mcp-gateway\",\"workspace-grants-v1.json\");\nconst expected=new Set([\"version\",\"homeRead\",\"outsideRead\",\"custom\"]);\nexport const isInside=within;\nasync function safeStat(file: string,{directory=false,missing=false}={}){\n const st=await lstat(file).catch(e=>errorCode(e)===\"ENOENT\"&&missing?null:Promise.reject(e));\n if(!st)return null;\n if(st.isSymbolicLink()||(directory?!st.isDirectory():!st.isFile())||\n !ownedByUser(file,st,true)||(!directory&&st.nlink!==1))deny(\"GRANTS_UNSAFE_OWNERSHIP\");\n return st;\n}\nasync function privateFolder(folder: string){\n createPrivateDirectory(folder);\n await safeStat(folder,{directory:true});\n}\nasync function writePrivate(file: string,body: string){\n const temp=path.join(path.dirname(file),\".\"+path.basename(file)+\".\"+randomBytes(8).toString(\"hex\"));\n try{\n await writeFile(temp,body,{flag:\"wx\",mode:0o600});\n await rename(temp,file);\n }finally{await rm(temp,{force:true});}\n}\nasync function checkCustom(raw: unknown,home: string){\n if(typeof raw!==\"string\"||!path.isAbsolute(raw)||raw.length>1024||\n raw.includes(\"\\0\")||raw.split(path.sep).includes(\"..\"))deny(\"GRANT_PATH_INVALID\");\n const canon=await realpath(raw);\n const st=await lstat(canon);\n if(!st.isDirectory()||st.isSymbolicLink()||!ownedByUser(canon,st))\n deny(\"GRANT_ROOT_UNSAFE\");\n if(canon===path.parse(canon).root||canon===home||canon===\"/Users\"||canon===\"/Volumes\")\n deny(\"GRANT_ROOT_TOO_BROAD\");\n const sensitive=new Set([\".ssh\",\".aws\",\".gnupg\",\".config\",\"keychains\",\n \"secrets\",\"credentials\"]);\n if(canon.split(path.sep).some(part=>sensitive.has(part.toLowerCase())))\n deny(\"GRANT_SENSITIVE_ROOT_DENIED\");\n return canon;\n}\nfunction validate(value: unknown): GrantsConfig {\n const data=record(value,\"GRANTS_CONFIG_INVALID\");\n if(!data||typeof data!==\"object\"||Array.isArray(data)||\n Object.keys(data).length!==expected.size||\n Object.keys(data).some(k=>!expected.has(k))||data.version!==1||\n typeof data.homeRead!==\"boolean\"||typeof data.outsideRead!==\"boolean\"||\n !Array.isArray(data.custom)||data.custom.length>12)\n deny(\"GRANTS_CONFIG_INVALID\");\n const seen=new Set();\n for(const value of data.custom){\n const item=record(value,\"GRANTS_CUSTOM_INVALID\");\n if(!item||typeof item.path!==\"string\"||!path.isAbsolute(item.path)||\n item.path.length>1024||![\"read-only\",\"read-write\"].includes(String(item.mode))||\n Object.keys(item).sort().join()!==\"mode,path\"||seen.has(item.path))\n deny(\"GRANTS_CUSTOM_INVALID\");\n seen.add(item.path);\n }\n return grantsSchema.parse(data);\n}\nconst defaults=(): GrantsConfig=>({version:1,homeRead:false,outsideRead:false,custom:[]});\nexport async function createWorkspaceGrants({file=defaultGrantsPath(),home=os.homedir(),audit=()=>{}}: {file?: string; home?: string; audit?: AuditSink}={}){\n if(typeof file!==\"string\"||!path.isAbsolute(file)||path.basename(file).length>160)\n deny(\"GRANTS_FILE_ABSOLUTE_REQUIRED\");\n const canonicalHome=await realpath(home);\n await privateFolder(path.dirname(file));\n const tokenFile=file+\".owner-token\";\n await withPrivateLock(file+\".lock\",async()=>{\n const existing=await safeStat(file,{missing:true});\n if(!existing)await writePrivate(file,JSON.stringify(defaults(),null,2)+\"\\n\");\n if(!await safeStat(tokenFile,{missing:true}))\n await writePrivate(tokenFile,randomBytes(32).toString(\"hex\")+\"\\n\");\n });\n let queue: Promise<unknown>=Promise.resolve();\n async function snapshot({validatePaths=true}={}){\n const st=await safeStat(file);\n if(st!.size>16384)deny(\"GRANTS_CONFIG_TOO_LARGE\");\n const config=validate(parseJSON(await readFile(file,\"utf8\")));\n if(validatePaths)for(const item of config.custom){\n const canon=await checkCustom(item.path,canonicalHome);\n if(canon!==item.path)deny(\"GRANTS_ROOT_CHANGED\");\n }\n return config;\n }\n async function authorized(token: unknown){\n const st=await safeStat(tokenFile);\n if(st!.size>256||typeof token!==\"string\"||!/^[a-f0-9]{64}$/.test(token))\n return false;\n const actual=(await readFile(tokenFile,\"utf8\")).trim();\n if(!/^[a-f0-9]{64}$/.test(actual))deny(\"GRANTS_TOKEN_INVALID\");\n return timingSafeEqual(Buffer.from(token),Buffer.from(actual));\n }\n async function update(op: string,{target,mode}: {target?: unknown; mode?: unknown}={}){\n const step=async()=>{\n const data=await snapshot({validatePaths:false});\n if(op===\"home-enable\")data.homeRead=true;\n else if(op===\"home-disable\")data.homeRead=false;\n else if(op===\"outside-enable\")data.outsideRead=true;\n else if(op===\"outside-disable\")data.outsideRead=false;\n else if([\"add\",\"revoke\"].includes(op)){\n // Revocation must work even when an external drive has been unplugged.\n const canonical=op===\"revoke\"?target:\n await checkCustom(target,canonicalHome);\n if(typeof canonical!==\"string\"||!path.isAbsolute(canonical)||\n path.normalize(canonical)!==canonical)deny(\"GRANT_PATH_INVALID\");\n const index=data.custom.findIndex(x=>x.path===canonical);\n if(op===\"revoke\"){\n if(index<0)deny(\"GRANT_NOT_FOUND\");\n data.custom.splice(index,1);\n }else{\n if(mode!==\"read-only\"&&mode!==\"read-write\")deny(\"GRANT_MODE_INVALID\");\n if(index<0)data.custom.push({path:canonical,mode});\n else data.custom[index]!.mode=mode;\n }\n }else deny(\"GRANT_OPERATION_INVALID\");\n validate(data);\n await writePrivate(file,JSON.stringify(data,null,2)+\"\\n\");\n audit({event:\"WORKSPACE_GRANT_UPDATED\",action:op,scope:op.startsWith(\"home\")?\"home\":\n op.startsWith(\"outside\")?\"outside\":\"custom\",mode:mode||\"toggle\"});\n return await snapshot({validatePaths:false});\n };\n const pending=queue.then(()=>withPrivateLock(file+\".lock\",step));\n queue=pending.catch(()=>{});\n return pending;\n }\n await snapshot({validatePaths:false});\n return Object.freeze({file,tokenFile,home:canonicalHome,snapshot,authorized,update});\n}\n"]}
|
package/docs/ACCESS_CONTROL.md
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# Authentication
|
|
2
|
-
|
|
3
|
-
Authentication decides who can connect. Your instance configuration decides what its tools can do.
|
|
4
|
-
|
|
5
|
-
## Choose an authentication mode
|
|
6
|
-
|
|
7
|
-
| Mode | Use when | Configure |
|
|
8
|
-
| --- | --- | --- |
|
|
9
|
-
| `none` *(default)* | Connecting on the same computer, or your tunnel/reverse proxy already restricts callers | `starmux auth mode none` |
|
|
10
|
-
| `api-key` | Your client accepts a Bearer API key header | `starmux auth key "My AI Client"` |
|
|
11
|
-
| `oauth` | Your client uses browser login and consent (e.g., ChatGPT Web) | [Configure a provider](OAUTH.md), then `starmux auth mode oauth` |
|
|
12
|
-
|
|
13
|
-
A new local instance defaults to `none` (also accepted as `ingress`). For remote access in this mode, your tunnel or reverse proxy must restrict access; HTTPS encrypts traffic but does not authenticate callers. Each instance has one active authentication mode at a time, and all authorized clients share the instance's approved workspace permissions.
|
|
14
|
-
|
|
15
|
-
## API keys
|
|
16
|
-
|
|
17
|
-
```sh
|
|
18
|
-
starmux auth key "WorkBuddy" --expires 30d
|
|
19
|
-
starmux auth
|
|
20
|
-
starmux auth revoke CREDENTIAL_ID
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Creating a key automatically enables `api-key` mode and prints both Form UI fields and a ready-to-paste `mcpServers` JSON snippet. Copy the key when printed; Starmux stores only its digest. New keys begin with `smx_` (migrated `mgw_` keys continue to work). Expiration accepts positive durations (`30m`, `12h`, `30d`, `4w`) or a future ISO timestamp. Omit it for no expiry.
|
|
24
|
-
|
|
25
|
-
Configure your client with `Authorization: Bearer YOUR_KEY`. See the [complete HTTPS example](REMOTE_SETUP.md#2-https--bearer-api-key).
|
|
26
|
-
|
|
27
|
-
## OAuth
|
|
28
|
-
|
|
29
|
-
```sh
|
|
30
|
-
starmux auth oauth setup --issuer https://auth.example.com/realms/gateway \
|
|
31
|
-
--resource https://mcp.example.com/mcp --client-id gateway-chatgpt \
|
|
32
|
-
--token-auth client_secret_basic --subject OWNER_USER_ID
|
|
33
|
-
starmux auth oauth doctor
|
|
34
|
-
starmux auth mode oauth
|
|
35
|
-
starmux auth
|
|
36
|
-
starmux auth revoke SESSION_ID
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Setup discovers and saves provider settings; mode activation is explicit. The same revoke command recognizes API-key IDs and OAuth session IDs. Revoked OAuth sessions remain denied after token refresh and restart. [Provider requirements and ChatGPT fields →](OAUTH.md)
|
|
40
|
-
|
|
41
|
-
## Local management
|
|
42
|
-
|
|
43
|
-
```sh
|
|
44
|
-
starmux dashboard
|
|
45
|
-
starmux dashboard --no-open # Print a private login link
|
|
46
|
-
starmux dashboard --token # Print the owner management token
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
The console opens locally with its management token. Keep owner tokens and private login links on the host; AI clients receive an API key or OAuth token instead. Management endpoints stay local when you expose `/mcp` remotely.
|
|
50
|
-
|
|
51
|
-
All commands support `-i NAME` for a specific instance. Revocations and mode changes affect subsequent requests immediately, without `config apply`; executing tools are not canceled by revocation.
|
package/docs/CLIENTS.md
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# AI client compatibility
|
|
2
|
-
|
|
3
|
-
Choose a client you already use, then check its recommended connection below. Transport, authentication and host platform are configured independently.
|
|
4
|
-
|
|
5
|
-
| Client | Validation status | Connection guide |
|
|
6
|
-
| --- | --- | --- |
|
|
7
|
-
| **ChatGPT Web** | ✅ Tested: Secure Tunnel + OAuth (coding, session revocation and login) | [ChatGPT OAuth](REMOTE_SETUP.md#4-configure-starmux-and-chatgpt) |
|
|
8
|
-
| **Claude App** | 🎯 Target: Remote MCP Custom Connector | [Official custom connector setup](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp) |
|
|
9
|
-
| **Codex** | ✅ Tested: Protected Tunnel | [Tunnel setup](CONNECT_CHATGPT.md#2-secure-mcp-tunnel--no-inbound-port) · [HTTP API-key example](REMOTE_SETUP.md#configure-workbuddy-or-another-header-capable-client) |
|
|
10
|
-
| **WorkBuddy** | ✅ Tested: Direct HTTPS + IPv6 + Bearer API key | [API Key setup](REMOTE_SETUP.md#2-https--bearer-api-key) |
|
|
11
|
-
| **Doubao (豆包)** | ✅ Community verified | [Deployment options](REMOTE_SETUP.md) |
|
|
12
|
-
|
|
13
|
-
See [OAuth setup](OAUTH.md) and [remote setup](REMOTE_SETUP.md) for step-by-step configuration.
|
|
14
|
-
|
|
15
|
-
## Use WorkBuddy or Desktop AI Clients with a Linux / NVIDIA Jetson Host
|
|
16
|
-
|
|
17
|
-
**Your AI client's operating system does not have to match your target machine's operating system.** Many AI clients (such as WorkBuddy or ChatGPT Desktop) only offer macOS and Windows desktop apps. You can keep your AI client on your Mac or Windows laptop, run `starmux up --code` on a **Linux (`x64` / `ARM64`)** server or **NVIDIA Jetson** device, and connect over HTTPS + Bearer API Key or Tunnel to edit code and run terminal commands on the target machine remotely.
|
|
18
|
-
|
|
19
|
-
See [platform support](PLATFORMS.md) for Linux and cross-platform details.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
[Choose authentication](ACCESS_CONTROL.md#choose-an-authentication-mode) · [Remote deployment](REMOTE_SETUP.md) · [All documentation](README.md)
|
package/docs/CONNECT_CHATGPT.md
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
# Connect ChatGPT to Starmux
|
|
2
|
-
|
|
3
|
-
**Your Gateway is running on your Mac. How does ChatGPT reach it?**
|
|
4
|
-
|
|
5
|
-
For the complete current **OAuth / HTTPS + Bearer API Key** deployment procedure, use [Remote installation](REMOTE_SETUP.md). This page explains network choices. ChatGPT OAuth over Tunnel has passed real coding and revocation tests; direct-HTTPS OAuth remains separately unverified.
|
|
6
|
-
|
|
7
|
-
Choose the connection that matches your network. **Start with Direct HTTPS** if your Mac can accept inbound internet traffic. Choose **Secure MCP Tunnel** if it cannot, or you prefer not to open a public HTTPS entry point.
|
|
8
|
-
|
|
9
|
-
| | **1. Direct HTTPS — recommended for reachable networks** | **2. Secure MCP Tunnel — for restricted networks** |
|
|
10
|
-
| --- | --- | --- |
|
|
11
|
-
| ChatGPT connects using | Your public `https://your-domain.example/mcp` URL | Your personal OpenAI Platform `tunnel_id` |
|
|
12
|
-
| Network requirement | Publicly reachable IPv4 and/or IPv6 + inbound TCP 443 | Outbound HTTPS from your Mac |
|
|
13
|
-
| You operate | DNS, TLS, HTTPS reverse proxy, Gateway; authorization server for OAuth | `tunnel-client`, Gateway; reachable authorization server for OAuth |
|
|
14
|
-
| Hosted forwarding | No Desktop Commander relay or OpenAI Tunnel | OpenAI Tunnel is used; no Desktop Commander relay |
|
|
15
|
-
| Release status | ChatGPT OAuth acceptance **pending** | Owner OAuth coding/revocation tested; independent new-user acceptance **pending** |
|
|
16
|
-
|
|
17
|
-
This is a guide for **your own private ChatGPT connection**, not a claim that this project is already an independently verified public one-click service.
|
|
18
|
-
|
|
19
|
-
## Before either connection
|
|
20
|
-
|
|
21
|
-
1. Install and start Starmux using [Local installation](INSTALL.md).
|
|
22
|
-
2. Run `starmux status` and confirm **`● Starmux is running`**. Note your **actual local port**. The default port is **18893**; use the actual value shown by `status`.
|
|
23
|
-
3. The **local** MCP address is `http://127.0.0.1:<PORT>/mcp`. You don't paste that into ChatGPT Web: ChatGPT runs in the cloud and cannot dial your machine's `localhost` directly.
|
|
24
|
-
4. Start with the default **Read-only** mode (`starmux up`). Switch to **Coding mode** (`starmux up --code`) when you want ChatGPT to edit files or run Shell commands.
|
|
25
|
-
|
|
26
|
-
## 1. Direct HTTPS — your domain, your Gateway
|
|
27
|
-
|
|
28
|
-
**Best for:** a Mac with a publicly reachable IP address, typically on an international home network, office network, or a suitable hosted Mac. This is the preferred direct route when reliable inbound access is possible.
|
|
29
|
-
|
|
30
|
-
**The idea is simple:**
|
|
31
|
-
|
|
32
|
-
```text
|
|
33
|
-
ChatGPT → https://mcp.your-domain.example/mcp
|
|
34
|
-
→ public IP (DNS A/AAAA)
|
|
35
|
-
→ HTTPS :443 + authentication
|
|
36
|
-
→ reverse proxy on your Mac
|
|
37
|
-
→ http://127.0.0.1:<PORT>/mcp
|
|
38
|
-
→ Starmux → Desktop Commander
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
The public domain points to **your** machine or home router, not to the Gateway's loopback port. Here are the steps.
|
|
42
|
-
|
|
43
|
-
### Step 1 — Check whether your Mac can be reached from the internet
|
|
44
|
-
|
|
45
|
-
Determine how your Mac reaches the internet:
|
|
46
|
-
|
|
47
|
-
- **Public IPv4:** your DNS **A** record usually points to your router's public WAN IPv4. Configure your router to forward **TCP 443** to an HTTPS reverse proxy running on the Mac. If your ISP uses **CGNAT**, normal inbound IPv4 port-forwarding generally won't work.
|
|
48
|
-
- **Public IPv6:** your DNS **AAAA** record can point to the Mac's **globally reachable IPv6** address. Allow inbound **TCP 443** through the router's IPv6 firewall and macOS firewall as appropriate. IPv6 address stability, ISP filters and ChatGPT's actual reachability must still be checked.
|
|
49
|
-
- **Changing public IP:** use your DNS provider's **Dynamic DNS (DDNS)** feature or a small updater to keep A/AAAA records current.
|
|
50
|
-
|
|
51
|
-
DNS is only the **address book**. Pointing a domain at an IP does **not** automatically open ports, install HTTPS, authenticate clients, or make an IPv6-only endpoint reachable by ChatGPT. If inbound access is unavailable, choose [Secure MCP Tunnel](#2-secure-mcp-tunnel--no-inbound-port).
|
|
52
|
-
|
|
53
|
-
### Step 2 — Register a domain and set its DNS record
|
|
54
|
-
|
|
55
|
-
Buy or use a domain from any registrar, then open the DNS dashboard for the provider hosting its DNS zone. Create a subdomain such as `mcp.your-domain.example`.
|
|
56
|
-
|
|
57
|
-
| DNS record | Name | Value (example only) | When to use |
|
|
58
|
-
| --- | --- | --- | --- |
|
|
59
|
-
| **A** | `mcp` | `203.0.113.10` | Public IPv4 of your router/server |
|
|
60
|
-
| **AAAA** | `mcp` | `2001:db8::10` | Publicly reachable IPv6 of your Mac |
|
|
61
|
-
|
|
62
|
-
These are **documentation-only addresses**, not usable addresses. Use your own public IP. For the first test, publish only the address family you can actually reach; a broken AAAA record alongside a working A record can complicate connection attempts.
|
|
63
|
-
|
|
64
|
-
Check the result with `dig +short mcp.your-domain.example A` and `dig +short mcp.your-domain.example AAAA`. DNS changes can take time to propagate.
|
|
65
|
-
|
|
66
|
-
### Step 3 — Set up HTTPS *and* authentication
|
|
67
|
-
|
|
68
|
-
Run an HTTPS reverse proxy such as **Caddy** or **Nginx**, listen on public **TCP 443**, and obtain a valid TLS certificate for `mcp.your-domain.example`. Its protected MCP route forwards requests internally to `http://127.0.0.1:<PORT>/mcp`.
|
|
69
|
-
|
|
70
|
-
**Before** exposing this route, configure **real caller authentication and authorization**. Core OAuth validates the token and checks owner/session authorization; Core API-key mode validates supported clients’ Bearer keys. A separately controlled ingress is also supported in ingress mode. HTTPS encryption by itself **does not** authenticate the AI client; a private-looking domain name is not access control.
|
|
71
|
-
|
|
72
|
-
**Implementation status:** OAuth and API-key authentication are connected to Core HTTP. Follow the [deployment guide](REMOTE_SETUP.md) to configure them. This is not yet a one-click public OAuth/reverse-proxy installation. If you have not configured and tested real authentication, **do not publish /mcp**. Never expose the local Gateway's `<PORT>` directly to the internet.
|
|
73
|
-
|
|
74
|
-
### Step 4 — Add the URL in ChatGPT
|
|
75
|
-
|
|
76
|
-
Once the public HTTPS MCP endpoint is reachable **and authenticated**:
|
|
77
|
-
|
|
78
|
-
1. Open **ChatGPT on the web** and navigate to **Plugins → + → Add custom MCP server**.
|
|
79
|
-
2. Enter a recognizable name, e.g. *My Mac Gateway*.
|
|
80
|
-
3. Under **Connection**, enter the **full public URL**, including `/mcp`:
|
|
81
|
-
|
|
82
|
-
`https://mcp.your-domain.example/mcp`
|
|
83
|
-
|
|
84
|
-
4. Configure the authentication requested by your server. Read and accept the security warning, then choose **Create as a plugin**.
|
|
85
|
-
5. Review the discovered tools. Make a harmless authorized `read_file` request from ChatGPT and verify that a forbidden write is rejected. Also verify that revoked access stops working.
|
|
86
|
-
|
|
87
|
-
**Success means a real ChatGPT tool call worked**, not just that DNS resolved, a browser displayed a certificate, or `/healthz` answered locally.
|
|
88
|
-
|
|
89
|
-
## 2. Secure MCP Tunnel — no inbound port
|
|
90
|
-
|
|
91
|
-
**Best for:** Mac users behind CGNAT, restrictive firewalls, unstable inbound IP networking, or those who cannot/won't expose a public HTTPS entry point.
|
|
92
|
-
|
|
93
|
-
A Tunnel keeps the Gateway local and uses an **outbound connection from your Mac to OpenAI**. This avoids opening your router's inbound ports. The trade-off is that **OpenAI's Tunnel service forwards the requests**; it is not a completely third-party-free transport.
|
|
94
|
-
|
|
95
|
-
The steps below are intentionally short because **the official OpenAI guide is the authoritative source**: [Secure MCP Tunnel documentation](https://developers.openai.com/api/docs/guides/secure-mcp-tunnels).
|
|
96
|
-
|
|
97
|
-
### Step 1 — Create your own Tunnel in OpenAI Platform
|
|
98
|
-
|
|
99
|
-
Visit [OpenAI Platform Tunnel settings](https://platform.openai.com/settings/organization/tunnels), sign in to the intended organization and create a Tunnel. Confirm the Tunnel is associated with the **ChatGPT workspace** that will use it.
|
|
100
|
-
|
|
101
|
-
Creating or editing a Tunnel requires **Tunnels Read + Manage** permission; running `tunnel-client` or choosing a Tunnel in ChatGPT requires **Tunnels Read + Use**. Ask your organization/workspace admin if these controls aren't available.
|
|
102
|
-
|
|
103
|
-
Save your **own `tunnel_id`**. The Starmux release doesn't bundle the maintainer's Tunnel ID, organization identity or runtime credential.
|
|
104
|
-
|
|
105
|
-
### Step 2 — Run the official tunnel-client on your Mac
|
|
106
|
-
|
|
107
|
-
Install OpenAI's supported `tunnel-client` using the official guide. Follow its current instructions to configure a profile with:
|
|
108
|
-
|
|
109
|
-
- Your own Platform Tunnel ID and **restricted runtime API key**, kept out of Git, shell history and screenshots.
|
|
110
|
-
- For ingress-managed mode, the MCP target **`http://127.0.0.1:<PORT>/mcp`**, reachable locally on the Mac.
|
|
111
|
-
- For the tested **OAuth** layout, use the same-origin HTTPS target and trusted issuer described in [OAuth Tunnel setup](REMOTE_SETUP.md#official-openai-tunnel--tested-chatgpt-path), not the loopback target above.
|
|
112
|
-
|
|
113
|
-
Use the version-specific help and health check:
|
|
114
|
-
|
|
115
|
-
```bash
|
|
116
|
-
tunnel-client help quickstart
|
|
117
|
-
tunnel-client doctor --profile <YOUR_PROFILE> --explain
|
|
118
|
-
tunnel-client run --profile <YOUR_PROFILE>
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Keep both Starmux and `tunnel-client` running.
|
|
122
|
-
|
|
123
|
-
### Step 3 — Choose the Tunnel in ChatGPT
|
|
124
|
-
|
|
125
|
-
1. Open **ChatGPT web → Plugins → + → Add custom MCP server**.
|
|
126
|
-
2. Set a name and choose **Connection: Tunnel**.
|
|
127
|
-
3. Select your available Tunnel, or enter your **own `tunnel_id`** if the UI permits.
|
|
128
|
-
4. Complete authentication and risk review; create the plugin.
|
|
129
|
-
5. Verify a real permitted `read_file` request and a denied write from ChatGPT.
|
|
130
|
-
|
|
131
|
-
If ChatGPT doesn't list your Tunnel, check the Platform organization ↔ ChatGPT workspace association, **Tunnels Read + Use**, runtime readiness and custom MCP permissions.
|
|
132
|
-
|
|
133
|
-
## Which route should you choose?
|
|
134
|
-
|
|
135
|
-
**Reachable international network with a stable domain?** Start with **Direct HTTPS**, once you have secure authentication in place.
|
|
136
|
-
|
|
137
|
-
**Behind CGNAT, restrictive NAT/firewall, or unable to verify inbound IPv6?** Use **Secure MCP Tunnel**. It sacrifices independent transport but avoids an inbound listener.
|
|
138
|
-
|
|
139
|
-
Neither route replaces Desktop Commander or changes your local file permissions. The Gateway owns the connection and routing boundary; the upstream MCP server does the actual work.
|
|
140
|
-
|
|
141
|
-
## Troubleshooting & current limits
|
|
142
|
-
|
|
143
|
-
- **Can reach the Dashboard, but ChatGPT fails:** Dashboard is local. Verify public `https://.../mcp` reachability, valid TLS and authentication, or check Tunnel readiness and workspace linking.
|
|
144
|
-
- **DNS resolves, but HTTPS fails:** DNS alone doesn't open TCP 443. Recheck router port-forward/IPv6 firewall, ISP blocks, proxy and certificate.
|
|
145
|
-
- **Tools appear, but write is denied:** This may be correct. Check remote caller authorization and local restricted/developer mode. Don't disable security just to make discovery green.
|
|
146
|
-
- **No “Add custom MCP server” option:** ChatGPT's available custom MCP features depend on plan and workspace permissions. See [OpenAI's current connection instructions](https://help.openai.com/en/articles/12584461-connecting-custom-mcp-servers-in-chatgpt).
|
|
147
|
-
|
|
148
|
-
[Main README](../README.md) · [Local installation](INSTALL.md) · [Security](SECURITY.md)
|
package/docs/INSTALL.md
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
# Install Starmux
|
|
2
|
-
|
|
3
|
-
Install on the computer whose files and tools your AI client will use. You need Node.js **22.12+** and npm. Desktop Commander is installed automatically as a pinned dependency.
|
|
4
|
-
|
|
5
|
-
```sh
|
|
6
|
-
npm install -g starmux
|
|
7
|
-
cd /path/to/project
|
|
8
|
-
starmux up
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
The first start creates a private configuration and starts a background process on `127.0.0.1:18893`. The terminal returns when the service is ready.
|
|
12
|
-
|
|
13
|
-
Read the **Connect an AI client (MCP settings)** section printed by `up` (also available with `status`):
|
|
14
|
-
|
|
15
|
-
- **None (no token required):** a local client uses Streamable HTTP with authentication set to None. For a web or remote client, connect through a protected HTTPS proxy or tunnel that forwards to `127.0.0.1:18893`.
|
|
16
|
-
- **API key:** configure Bearer token authentication (`Authorization: Bearer <YOUR_API_KEY>`) with the key you saved when creating it. The output shows how many keys are active and the command to create one if needed.
|
|
17
|
-
- **OAuth:** use the configured **URL (Remote)** and complete browser sign-in with an allowed account.
|
|
18
|
-
|
|
19
|
-
`starmux dashboard` opens the local Web Access Console with its owner login so you can view and copy settings in your browser. `--json` retains structured status for scripts.
|
|
20
|
-
|
|
21
|
-
```sh
|
|
22
|
-
starmux verify
|
|
23
|
-
starmux status
|
|
24
|
-
starmux dashboard
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
For coding, run `starmux up --code`. It enables file edits and Shell/Git using your OS account permissions after one confirmation. For unattended setup, use `starmux up --code -y`. `--write` is an alias for `--code`.
|
|
28
|
-
|
|
29
|
-
## Daily operation
|
|
30
|
-
|
|
31
|
-
```sh
|
|
32
|
-
starmux up ~/my-project # Choose a workspace explicitly
|
|
33
|
-
starmux init ~/my-project # Save without starting
|
|
34
|
-
starmux logs -f
|
|
35
|
-
starmux doctor
|
|
36
|
-
starmux down
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
A later `starmux up` reuses the saved workspace. Use `starmux up .` to switch to your current directory. `up --foreground` runs until Ctrl+C; background runs continue after the terminal closes. Background operation does not configure login autostart.
|
|
40
|
-
|
|
41
|
-
If port 18893 is occupied, choose `starmux up --port 18894`. Use the actual endpoint shown by `status` when configuring your proxy or client.
|
|
42
|
-
|
|
43
|
-
With API-key or OAuth authentication enabled, give `verify` a valid Bearer credential using `STARMUX_TOKEN` or `--token-stdin`.
|
|
44
|
-
|
|
45
|
-
## Connect remotely
|
|
46
|
-
|
|
47
|
-
Continue with [remote setup](REMOTE_SETUP.md): HTTPS + API key, ChatGPT OAuth, or an existing protected tunnel. A local verification pass confirms the local service; the last step of each remote route is a real tool call from your client.
|
|
48
|
-
|
|
49
|
-
## Install from a checkout
|
|
50
|
-
|
|
51
|
-
Until using a published release, or when developing locally:
|
|
52
|
-
|
|
53
|
-
```sh
|
|
54
|
-
npm ci
|
|
55
|
-
npm run build
|
|
56
|
-
npm install --global .
|
|
57
|
-
starmux --version
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
A published tarball already contains compiled JavaScript. It needs no TypeScript installation. If the command is not found, check your npm global bin directory and shell PATH; zsh may need `rehash` after a command rename.
|
|
61
|
-
|
|
62
|
-
See [contributing](../CONTRIBUTING.md) for builds and tests, [App development](../app/README.md) for the macOS App, and [platform support](PLATFORMS.md) for validation status.
|
package/docs/INSTANCES.md
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
# Instances and configuration
|
|
2
|
-
|
|
3
|
-
Use one implicit default instance for everyday work. Add `-i NAME` when you want separate projects with separate ports, credentials, policies and logs.
|
|
4
|
-
|
|
5
|
-
```sh
|
|
6
|
-
starmux up ~/work/repo-a -i work --code
|
|
7
|
-
starmux up ~/personal/blog -i blog --read
|
|
8
|
-
starmux ls
|
|
9
|
-
starmux status -i work
|
|
10
|
-
starmux logs -f -i blog
|
|
11
|
-
starmux down -i work
|
|
12
|
-
starmux rm blog
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Names such as `work` and `blog` are arbitrary labels. Names and stable IDs both work with `-i` / `--instance`, before or after the command. Instances share the OS account; coding mode uses that account's permissions.
|
|
16
|
-
|
|
17
|
-
## Edit configuration
|
|
18
|
-
|
|
19
|
-
```sh
|
|
20
|
-
starmux config path
|
|
21
|
-
starmux config show
|
|
22
|
-
starmux config edit
|
|
23
|
-
starmux config diff
|
|
24
|
-
starmux config apply
|
|
25
|
-
starmux config show --effective
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Set `VISUAL` or `EDITOR` to your editor executable for `config edit`. JSONC supports comments and trailing commas. An edit is validated and saved with a revision check; `apply` restarts a running instance. Applying while stopped validates the next start. `up` also applies pending settings before returning success.
|
|
29
|
-
|
|
30
|
-
```text
|
|
31
|
-
~/.config/starmux/
|
|
32
|
-
registry.json
|
|
33
|
-
gateway.schema.json
|
|
34
|
-
instances/<stable-id>/
|
|
35
|
-
config.jsonc
|
|
36
|
-
state/
|
|
37
|
-
logs/
|
|
38
|
-
snapshots/
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Configuration holds workspace, port, permissions and optional upstream settings. Credentials and secrets live in private state. `config validate` and `config schema` provide diagnostics and the full schema. `starmux config schema` also works before creating an instance.
|
|
42
|
-
|
|
43
|
-
## Import, bind and export
|
|
44
|
-
|
|
45
|
-
```sh
|
|
46
|
-
starmux config import ./gateway.jsonc -i work
|
|
47
|
-
starmux config import ./project.jsonc -i blog --link
|
|
48
|
-
starmux config relink -i blog --file /new/path/project.jsonc
|
|
49
|
-
starmux config manage -i blog
|
|
50
|
-
starmux config export -i blog --output ./export.jsonc
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Import makes a private copy. `--link` keeps the external file authoritative. `manage` converts a linked source into a managed copy. External source files must be regular, owner-owned files with trustworthy parent directories. Export includes settings and secret references, not secret values.
|
|
54
|
-
|
|
55
|
-
## Manage instances
|
|
56
|
-
|
|
57
|
-
```sh
|
|
58
|
-
starmux config copy experiment -i work
|
|
59
|
-
starmux config rename development -i work
|
|
60
|
-
starmux config default development
|
|
61
|
-
starmux rm experiment # Alias for: starmux config archive -i experiment
|
|
62
|
-
starmux ls --archived
|
|
63
|
-
starmux config restore -i experiment
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
Copies receive a new identity and port, without credentials or private secrets. An authenticated copy needs its authentication configured before startup. Archive stops the instance and retains recoverable data. The first created instance is the CLI default; `config default` changes it explicitly.
|
|
67
|
-
|
|
68
|
-
With no instances, `starmux ls` points to `starmux up` to get started. If all instances are archived, use `starmux ls --archived` and restore one with `starmux config restore -i NAME`. Instance operations reject missing or extra arguments; use `-i NAME` to select the instance to archive or restore.
|
|
69
|
-
|
|
70
|
-
## Snapshots, secrets and automation
|
|
71
|
-
|
|
72
|
-
Concurrent edits return `CONFIG_CONFLICT`; reload before saving again. A failed apply attempts to restore the last successful worker configuration. Credential revocations are never rolled back. `config recover` repairs a missing source from the last successful snapshot without replacing an existing file.
|
|
73
|
-
|
|
74
|
-
```sh
|
|
75
|
-
starmux config secret set service_token -i development < /private/token-file
|
|
76
|
-
starmux config secret remove service_token -i development
|
|
77
|
-
starmux config save --file ./config.jsonc --revision REVISION
|
|
78
|
-
starmux config patch --patch '{"port":18894}' --revision REVISION
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
Use `--json` for scripts and obtain the revision from `config show`. Configured stdio upstreams retain explicit executable/tool allowlists and private environment references. Apply after changing upstream secrets.
|
|
82
|
-
|
|
83
|
-
The macOS App manages the same instances and configuration. Quitting it stops only App-owned instances; CLI background instances remain running.
|
package/docs/OAUTH.md
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
# OAuth for ChatGPT and other MCP clients
|
|
2
|
-
|
|
3
|
-
Starmux acts as the OAuth **resource server**. A separate authorization server (such as Keycloak) owns user login, consent, PKCE authorization codes, token issuance and refresh. Starmux remains loopback-only (`127.0.0.1`); HTTPS or Tunnel transport is configured independently of authorization.
|
|
4
|
-
|
|
5
|
-
**Start here:** [step-by-step remote installation](REMOTE_SETUP.md) covers Keycloak, HTTPS proxy, CLI configuration, ChatGPT fields and the tested Tunnel layout. This document covers provider setup and the OAuth reference contract.
|
|
6
|
-
|
|
7
|
-
## Implementation overview
|
|
8
|
-
|
|
9
|
-
- **Supported features:** protected-resource discovery (`/.well-known/oauth-protected-resource`), Bearer challenges, JWT validation, owner subject/client allowlists, tool security metadata, live local session revocation, and CLI / Web Dashboard management.
|
|
10
|
-
- **Token contract:** JWT access tokens with `sub`, `azp` (OAuth client ID), `sid` (stable authorization-server session ID), `iat`, `exp`, audience and scope. Opaque tokens/introspection are not supported.
|
|
11
|
-
- **Discovery:** Resource metadata is available without a token in OAuth mode at `/.well-known/oauth-protected-resource` and the resource-path-specific location. MCP initialization, tool listing, tool calls and resources all require a valid OAuth token.
|
|
12
|
-
|
|
13
|
-
## Configure an existing authorization server
|
|
14
|
-
|
|
15
|
-
Create a dedicated owner account and an OAuth client with authorization-code flow, PKCE S256, the exact ChatGPT callback URL and explicit consent. Disable password grants, implicit grants and public account registration. The issuer must issue a token for this Gateway resource with `gateway:access`. The scope means access to **Owner default**, not a filesystem sandbox.
|
|
16
|
-
|
|
17
|
-
```sh
|
|
18
|
-
starmux auth oauth setup \
|
|
19
|
-
--issuer https://auth.example.com/realms/gateway \
|
|
20
|
-
--resource https://gateway.example.com/mcp \
|
|
21
|
-
--client-id gateway-chatgpt \
|
|
22
|
-
--token-auth client_secret_basic \
|
|
23
|
-
--subject OWNER_SUBJECT_ID
|
|
24
|
-
starmux auth oauth doctor
|
|
25
|
-
starmux auth mode oauth
|
|
26
|
-
starmux auth oauth status
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
For an explicitly approved alternate resource identifier, append `--resource-aliases 'https://exact-resource.example/mcp'` (comma-separated, maximum eight). Include all intended aliases on reconfiguration; omitting the option removes them. `doctor` checks discovery separately from these owner-configured aliases.
|
|
30
|
-
|
|
31
|
-
`setup` fetches standard OAuth/OIDC discovery, checks exact issuer and PKCE S256, validates HTTPS endpoints on the same origin, and saves configuration without changing the active mode. Cross-origin authorization/JWKS endpoints are deliberately not supported in this first adapter. Redirecting discovery endpoints are rejected. `--allow-loopback-http` is explicitly for isolated development, never remote deployment.
|
|
32
|
-
|
|
33
|
-
The default registration method is `provided` and token endpoint method is `none` (public client + PKCE). `--registration cimd|dcr` is accepted only when the provider advertises support; use the actual OAuth client ID emitted as `azp`. For CIMD that may be the exact client document URL shown by ChatGPT. DCR client IDs must be explicitly approved after registration; automatic enrollment of arbitrary clients is intentionally not implemented. `--token-auth` also accepts `client_secret_basic`, `client_secret_post`, and `private_key_jwt`. Gateway never needs the OAuth client's secret; configure it with the provider and ChatGPT only.
|
|
34
|
-
|
|
35
|
-
Configure ChatGPT with OAuth, the corresponding registration mode and client ID. `none` means no **client secret**, not no user authentication. Use `gateway:access` as the base scope. Leave OIDC/domain-claim features off unless your provider is configured for them. Tool names, input schemas and results retain upstream semantics; OAuth security metadata is added to advertised descriptors without replacing other `_meta` entries.
|
|
36
|
-
|
|
37
|
-
Forward the MCP route and the protected-resource metadata routes through your HTTPS ingress, preserving Authorization and WWW-Authenticate headers. The local upstream Host header must match the loopback endpoint; the Core retains its loopback Host validation. Keep `/access`, `/dashboard`, `/permissions` and other local management endpoints private. Do not derive issuer/resource from an untrusted Host or forwarded header.
|
|
38
|
-
|
|
39
|
-
For Secure MCP Tunnel, verify metadata discovery through the tunnel separately. The authorization server's browser-facing login and token endpoints are not automatically made reachable by an MCP tunnel.
|
|
40
|
-
|
|
41
|
-
## Reference self-hosted provider: Keycloak 26.8.1
|
|
42
|
-
|
|
43
|
-
Generate a realm import using your real HTTPS MCP resource and the exact callback shown by ChatGPT:
|
|
44
|
-
|
|
45
|
-
```sh
|
|
46
|
-
starmux auth oauth init-keycloak \
|
|
47
|
-
--resource https://gateway.example.com/mcp \
|
|
48
|
-
--callback https://chatgpt.com/connector/oauth/YOUR_CALLBACK_ID > gateway-realm.json
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Import into a dedicated Keycloak instance/realm. The generator contains no users or passwords, enables explicit client consent, requires S256, disables password/implicit grants, uses short-lived access tokens and refresh-token rotation, and registers only the exact callback. Create the owner account using Keycloak's admin interface; take its user ID as `--subject`. The reference client ID is `gateway-chatgpt`, using `client_secret_basic`. Obtain its generated secret from the Keycloak admin interface and enter it only into ChatGPT. Keycloak 26.8.1 does not advertise `none` in token endpoint authentication metadata, so this reference uses a confidential client rather than claiming public-client discovery support.
|
|
52
|
-
|
|
53
|
-
Start Keycloak with `--features=resource-indicators` so it processes and validates the resource parameter. The import explicitly maps the subject (`sub`) into access tokens and declares a `gateway-resource` client with `resource_url` and a `gateway:access` audience mapper targeting that client. This feature is experimental in 26.8.1. Production deployments require a persistent database, HTTPS/hostname settings, protected administration and backups; do not expose `start-dev` publicly. CIMD is not enabled by this template.
|
|
54
|
-
|
|
55
|
-
## What to enter in ChatGPT
|
|
56
|
-
|
|
57
|
-
Three addresses have different jobs. Your existing website can keep its current address. The **MCP resource** is the externally reachable Gateway endpoint (for example, `https://mcp.example.com/mcp`); it must match the audience configured above. The **issuer** is the authorization server (for example, `https://auth.example.com/realms/gateway`). Owning a website or running a tunnel does not automatically provide an issuer. You can host the issuer on a separate hostname or a carefully routed path; it does not need to run on the same machine as the Core.
|
|
58
|
-
|
|
59
|
-
For the reference Keycloak configuration:
|
|
60
|
-
|
|
61
|
-
| ChatGPT field | Value |
|
|
62
|
-
| --- | --- |
|
|
63
|
-
| Authentication | OAuth |
|
|
64
|
-
| Client registration | Custom/provided OAuth client |
|
|
65
|
-
| Callback URL | Copy the exact URL shown by ChatGPT into the realm generator |
|
|
66
|
-
| OAuth client ID | `gateway-chatgpt` |
|
|
67
|
-
| OAuth client secret | Generated by Keycloak; enter directly in ChatGPT, never in Gateway CLI or chat |
|
|
68
|
-
| Token endpoint authentication | `client_secret_basic` |
|
|
69
|
-
| Base authorization scopes | `gateway:access` |
|
|
70
|
-
| Default requested scopes | `gateway:access` |
|
|
71
|
-
| Authorization URL | `{issuer}/protocol/openid-connect/auth` |
|
|
72
|
-
| Token URL | `{issuer}/protocol/openid-connect/token` |
|
|
73
|
-
| Authorization server base / issuer | The exact configured issuer |
|
|
74
|
-
| Resource | The exact configured public MCP resource URL |
|
|
75
|
-
| Registration URL | Not required for the provided-client method; do not select DCR for this template |
|
|
76
|
-
| PKCE | S256 |
|
|
77
|
-
| OIDC/domain claim | Leave disabled for this first setup |
|
|
78
|
-
|
|
79
|
-
The endpoints should normally be discovered rather than entered manually. If discovery fails, first check that the resource metadata routes and the issuer discovery document are reachable from outside your machine. Manually filling fields does not fix missing network routes. Public plain HTTP is not accepted by this configuration.
|
|
80
|
-
|
|
81
|
-
For OpenAI's official tunnel, keep tunnel provisioning separate: the tunnel credential is not an OAuth client secret or an issuer. Complete the provider setup first, then verify discovery and authorization through the actual tunnel. Do not assume a regular website URL is the MCP resource URL used by the tunnel client.
|
|
82
|
-
|
|
83
|
-
## View and revoke
|
|
84
|
-
|
|
85
|
-
Use Dashboard → Access control (also reachable from the macOS App), or:
|
|
86
|
-
|
|
87
|
-
```sh
|
|
88
|
-
starmux auth
|
|
89
|
-
starmux auth revoke CONNECTION_ID
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
The connection ID hashes issuer, subject, client and session. The Core never stores access/refresh tokens. Revocation is persistent: an old session stays denied even if the provider refreshes its token or the Core restarts. The provider may still issue tokens, but they cannot restore Gateway access for the revoked session. A fresh, authorized login can create a new session; remove the subject from the configured allowlist to block that account entirely. This is not provider-wide logout. Already running tools are not canceled.
|
|
93
|
-
|
|
94
|
-
A maximum of 512 remembered OAuth sessions is enforced without evicting revocation records. At capacity, new sessions fail closed. Missing/invalid local state fails closed. Signing keys are cached for up to five minutes; unknown key IDs can refresh JWKS after a short cooldown. Cached valid keys may continue validating unexpired tokens during provider downtime; every call still checks the local allowlist and revocation state.
|
|
95
|
-
|
|
96
|
-
JWT verification checks signatures, issuer, audience, expiry/not-before, issued-at, client, owner subject, session and scope. A wrong scope/owner is denied before MCP dispatch. The Core does not retry tools when authentication fails. Locally trusted Shell tools remain capable of accessing owner files; OAuth admission does not isolate them from the owner account.
|
|
97
|
-
|
|
98
|
-
See [client compatibility](CLIENTS.md) and [remote setup](REMOTE_SETUP.md) for deployment details.
|
|
99
|
-
|
|
100
|
-
## Reproduce the local provider test
|
|
101
|
-
|
|
102
|
-
Use a **disposable** unpacked Keycloak 26.8.1 distribution and Java 21. The test starts Keycloak only on loopback with an in-memory database, imports a temporary realm into that distribution, generates temporary credentials, and stops it afterward. Do not point this test at an existing provider installation.
|
|
103
|
-
|
|
104
|
-
```sh
|
|
105
|
-
GATEWAY_KEYCLOAK_HOME=/absolute/path/to/disposable/keycloak-26.8.1 npm run test:oauth:keycloak
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
The test exercises exact callback checks, consent rejection, PKCE mismatch, authorization-code replay rejection, real token audience, MCP discovery and reading, token expiry, refresh rotation, and Gateway revocation surviving a provider refresh. It does not connect to ChatGPT or deploy a public authorization server. The normal `npm test` suite does not require Keycloak.
|
|
109
|
-
|
|
110
|
-
## Sources
|
|
111
|
-
|
|
112
|
-
- [OpenAI authentication](https://developers.openai.com/plugins/build/auth)
|
|
113
|
-
- [OpenAI custom MCP setup](https://developers.openai.com/api/docs/guides/custom-mcp-server)
|
|
114
|
-
- [OpenAI Tunnel OAuth](https://developers.openai.com/api/docs/guides/secure-mcp-tunnels#oauth)
|
|
115
|
-
- [Keycloak MCP integration](https://www.keycloak.org/securing-apps/mcp-authz-server)
|
|
116
|
-
- [Keycloak Docker setup](https://www.keycloak.org/getting-started/getting-started-docker)
|
package/docs/PLATFORMS.md
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# Platform support
|
|
2
|
-
|
|
3
|
-
Starmux Core and CLI share a TypeScript implementation for macOS, Linux and Windows. The SwiftUI App is macOS-only. All hosts require Node.js 22.12+.
|
|
4
|
-
|
|
5
|
-
```sh
|
|
6
|
-
npm install -g starmux
|
|
7
|
-
starmux up
|
|
8
|
-
starmux doctor
|
|
9
|
-
starmux verify
|
|
10
|
-
starmux down
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
`up` starts a detached background process without installing a system service. Use `--foreground` when a terminal or an external service manager should own the process. Run Starmux as your normal OS account.
|
|
14
|
-
|
|
15
|
-
## Permissions and storage
|
|
16
|
-
|
|
17
|
-
- Read-only mode uses the existing workspace and tool policy, including denial of arbitrary Shell and file writes.
|
|
18
|
-
- `--code` / `--write` enables file edits and Shell/Git using OS-account permissions on all platforms after confirmation.
|
|
19
|
-
- Existing macOS Home-only configurations retain their sandbox. Importing them on a host without that sandbox fails; choose a new permission mode explicitly.
|
|
20
|
-
- State is stored under `~/.config/starmux`; on Windows, `~` is the user profile. Private files use owner-only POSIX permissions or Windows ACLs.
|
|
21
|
-
- Windows runtime control uses authenticated named pipes; Unix hosts use private sockets. Gateway-generated commands use host-appropriate quoting and shells.
|
|
22
|
-
|
|
23
|
-
## Supported hosts
|
|
24
|
-
|
|
25
|
-
Starmux runs on **macOS** (`Apple Silicon` and `Intel`), **Linux** (`x64`, `ARM64`, and edge devices such as **NVIDIA Jetson**), and **Windows**.
|
|
26
|
-
|
|
27
|
-
The native macOS menu bar App is provided as a development build in [`app/`](../app/README.md).
|
|
28
|
-
|
|
29
|
-
`npm test` uses isolated home directories and ports to verify process lifecycle, workspace permissions, authentication, configuration, and the CLI.
|